设计模式之代理模式

写在前面

1:介绍

1.1:什么时候使用代理模式

当我们有对象因为安全性,不能直接对外暴露,或者是需要对对象的操作本身记录日志等信息时就可以考虑使用代理模式,

1.2:UML类图

享元设计模式,包含如下元素:

1:被代理类
    需要被代理的类
2:代理类
    代理类
3:客户端类
    使用代理类执行操作的类

UML图如下:

另外,代理又分为静态代理和动态代理,静态代理就是在编译器已经确定的代理方式,即是硬编码到程序中的,这种方式不灵活,动态代理是在运行期动态确定的代理方式,灵活度高。动态代理主要有jdk动态代理(需要有接口),asm,cglib(基于asm,简化操作)。来看下。

2:实例

源码

2.1:场景

数据库操作场景,需要在service方法调用前开启事务,方法调用后提交事务。

2.2:静态代理

  • 定义PersonService接口
java 复制代码
public interface PersonService {
	public void savePerson();
}
  • 定义PersonServiceImpl
java 复制代码
public class PersonServiceImpl implements PersonService {
	@Override
	public void savePerson() {
		System.out.println("保存人了");
	}
}
  • 定义事务类
java 复制代码
public class Transaction {
	public void beginTransaction(){
		System.out.println("开启事务 ");
	}
	public void commit(){
		System.out.println("提交事务");
	}
}
  • 定义代理类
java 复制代码
public class PersonServiceProxy implements PersonService {
	
	//目标类
	private PersonService personService;
	
	//增强类
	private Transaction transaction;
	
	//利用构造函数将目标类和增强类注入
	public PersonServiceProxy(PersonService personService,Transaction transaction) {
		this.personService = personService;
		this.transaction = transaction;
	}
	
	@Override
	public void savePerson() {
		// 这是每个类都需要的操作
		transaction.beginTransaction();

		personService.savePerson();

		// 这是每个类都需要的操作
		transaction.commit();
	}
}
  • 测试
java 复制代码
@Test
public void serviceStatic() {
    new PersonServiceProxy(new PersonServiceImpl(), new Transaction()).savePerson();
}

开启事务 
保存人了
提交事务

假定我们现在又有个AnimalService如下:

java 复制代码
public interface AnimalService {
	public void saveAnimal();
}

public class AnimalServiceImpl implements AnimalService {
    @Override
    public void saveAnimal() {
        System.out.println("保存动物了");
    }
}

则需要重新定义一个AnimalService的代理类来为其添加事务,如下:

java 复制代码
public class AnimalServiceProxy implements AnimalService {

	//目标类
	private AnimalService animalService;

	//增强类
	private Transaction transaction;

	//利用构造函数将目标类和增强类注入
	public AnimalServiceProxy(AnimalService animalService, Transaction transaction) {
		this.animalService = animalService;
		this.transaction = transaction;
	}
	
	@Override
	public void saveAnimal() {
		// 这是每个类都需要的操作(PersonService写过一遍了,这里又要再写一遍,好痛苦!!!)
		transaction.beginTransaction();
		animalService.saveAnimal();
		// 这是每个类都需要的操作(PersonService写过一遍了,这里又要再写一遍,好痛苦!!!)
		transaction.commit();
	}
}

测试:

java 复制代码
new AnimalServiceProxy(new AnimalServiceImpl(), new Transaction()).saveAnimal();

开启事务 
保存动物了
提交事务

如果我们有100个service,则同样的定义对应代理类的工作就要做100次,岂不疯了!!!但是,幸好我们有动态代理,接下来看下使用动态代理解决这个需要重复定义代理类的问题。

2.2:jdk动态代理

  • 定义InvocationHandler
java 复制代码
public class ServiceInvocationHandler implements InvocationHandler {
    // 被代理的对象
    private Object target;
    private Transaction transaction;
    public ServiceInvocationHandler(Object obj){
        this.target=obj;
        this.transaction = new Transaction();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 使用静态代理重复的事务开启操作就移动到这里了(只需要定义一次)
        this.transaction.beginTransaction();
        method.invoke(this.target, args);
        // 使用静态代理重复的事务提交操作就移动到这里了(只需要定义一次)
        this.transaction.commit();
        return null;
    }
}
  • 测试代理PersonService
java 复制代码
@Test
public void serviceJdkProxy() {
    ((PersonService) ServiceProxyFactory.makeProxy(new PersonServiceImpl())).savePerson();
}

开启事务 
保存人了
提交事务

如果是增加了AnimalService只需要增加如下代码即可获取代理类:

java 复制代码
((AnimalService) ServiceProxyFactory.makeProxy(new AnimalServiceImpl())).saveAnimal();

开启事务 
保存动物了
提交事务

但是,jdk动态代理有一个缺点就是必须要有接口,这在一定程度上限制了灵活度,如果是我们么有接口的话,则可以考虑使用cjlib来生成动态代理,一起看下!

2.3:cglib动态代理

  • 定义MethodInterceptor子类
java 复制代码
public class ServiceMethodInterceptor implements MethodInterceptor {
    private Transaction transaction = new Transaction();
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 开启事务(只需要写一次)
        transaction.beginTransaction();
        methodProxy.invokeSuper(o, objects);
        // 提交事务(只需要写一次)
        transaction.commit();
        return null;
    }
}
  • 获取代理工厂类
java 复制代码
public class CjlibProxyFactory {
    public static Object getGcLibDynProxy(Object target){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new ServiceMethodInterceptor());
        Object targetProxy= enhancer.create();
        return targetProxy;
    }
}
  • 测试
java 复制代码
@Test
public void serviceCjlibProxy() {
    ((PersonServiceImpl) CjlibProxyFactory.getGcLibDynProxy(new PersonServiceImpl())).savePerson();
}

开启事务 
保存人了
提交事务

如果是增加了AnimalService则增加如下代码即可获取代理类:

java 复制代码
((AnimalServiceImpl) CjlibProxyFactory.getGcLibDynProxy(new AnimalServiceImpl())).saveAnimal();

开启事务 
保存动物了
提交事务

写在后面

参考文章列表

java都有哪些动态代理机制?

Java中的代理模式------静态代理以及分析静态代理的缺点

相关推荐
WaaTong21 分钟前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
霁月风24 分钟前
设计模式——观察者模式
c++·观察者模式·设计模式
暗黑起源喵3 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
wrx繁星点点10 小时前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
金池尽干12 小时前
设计模式之——观察者模式
观察者模式·设计模式
也无晴也无风雨12 小时前
代码中的设计模式-策略模式
设计模式·bash·策略模式
肘击鸣的百k路21 小时前
Java 代理模式详解
java·开发语言·代理模式
捕鲸叉21 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
wrx繁星点点1 天前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
凉辰1 天前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式