写在前面
1:介绍
1.1:什么时候使用代理模式
当我们有对象因为安全性,不能直接对外暴露,或者是需要对对象的操作本身记录日志等信息时就可以考虑使用代理模式,
1.2:UML类图
享元设计模式,包含如下元素:
1:被代理类需要被代理的类
2:代理类代理类
3:客户端类使用代理类执行操作的类
UML图如下:
另外,代理又分为静态代理和动态代理,静态代理就是在编译器已经确定的代理方式,即是硬编码到程序中的,这种方式不灵活,动态代理是在运行期动态确定的代理方式,灵活度高。动态代理主要有jdk动态代理(需要有接口),asm,cglib(基于asm,简化操作)。来看下。
2:实例
源码 。
2.1:场景
数据库操作场景,需要在service方法调用前开启事务,方法调用后提交事务。
2.2:静态代理
- 定义PersonService接口
public interface PersonService {public void savePerson();
}
- 定义PersonServiceImpl
public class PersonServiceImpl implements PersonService {@Overridepublic void savePerson() {System.out.println("保存人了");}
}
- 定义事务类
public class Transaction {public void beginTransaction(){System.out.println("开启事务 ");}public void commit(){System.out.println("提交事务");}
}
- 定义代理类
public class PersonServiceProxy implements PersonService {//目标类private PersonService personService;//增强类private Transaction transaction;//利用构造函数将目标类和增强类注入public PersonServiceProxy(PersonService personService,Transaction transaction) {this.personService = personService;this.transaction = transaction;}@Overridepublic void savePerson() {// 这是每个类都需要的操作transaction.beginTransaction();personService.savePerson();// 这是每个类都需要的操作transaction.commit();}
}
- 测试
@Test
public void serviceStatic() {new PersonServiceProxy(new PersonServiceImpl(), new Transaction()).savePerson();
}开启事务
保存人了
提交事务
假定我们现在又有个AnimalService如下:
public interface AnimalService {public void saveAnimal();
}public class AnimalServiceImpl implements AnimalService {@Overridepublic void saveAnimal() {System.out.println("保存动物了");}
}
则需要重新定义一个AnimalService的代理类来为其添加事务,如下:
public class AnimalServiceProxy implements AnimalService {//目标类private AnimalService animalService;//增强类private Transaction transaction;//利用构造函数将目标类和增强类注入public AnimalServiceProxy(AnimalService animalService, Transaction transaction) {this.animalService = animalService;this.transaction = transaction;}@Overridepublic void saveAnimal() {// 这是每个类都需要的操作(PersonService写过一遍了,这里又要再写一遍,好痛苦!!!)transaction.beginTransaction();animalService.saveAnimal();// 这是每个类都需要的操作(PersonService写过一遍了,这里又要再写一遍,好痛苦!!!)transaction.commit();}
}
测试:
new AnimalServiceProxy(new AnimalServiceImpl(), new Transaction()).saveAnimal();开启事务
保存动物了
提交事务
如果我们有100个service,则同样的定义对应代理类的工作就要做100次,岂不疯了!!!但是,幸好我们有动态代理,接下来看下使用动态代理解决这个需要重复定义代理类
的问题。
2.2:jdk动态代理
- 定义InvocationHandler
public class ServiceInvocationHandler implements InvocationHandler {// 被代理的对象private Object target;private Transaction transaction;public ServiceInvocationHandler(Object obj){this.target=obj;this.transaction = new Transaction();}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 使用静态代理重复的事务开启操作就移动到这里了(只需要定义一次)this.transaction.beginTransaction();method.invoke(this.target, args);// 使用静态代理重复的事务提交操作就移动到这里了(只需要定义一次)this.transaction.commit();return null;}
}
- 测试代理PersonService
@Test
public void serviceJdkProxy() {((PersonService) ServiceProxyFactory.makeProxy(new PersonServiceImpl())).savePerson();
}开启事务
保存人了
提交事务
如果是增加了AnimalService只需要增加如下代码即可获取代理类:
((AnimalService) ServiceProxyFactory.makeProxy(new AnimalServiceImpl())).saveAnimal();开启事务
保存动物了
提交事务
但是,jdk动态代理有一个缺点就是必须要有接口,这在一定程度上限制了灵活度,如果是我们么有接口的话,则可以考虑使用cjlib来生成动态代理,一起看下!
2.3:cglib动态代理
- 定义MethodInterceptor子类
public class ServiceMethodInterceptor implements MethodInterceptor {private Transaction transaction = new Transaction();@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {// 开启事务(只需要写一次)transaction.beginTransaction();methodProxy.invokeSuper(o, objects);// 提交事务(只需要写一次)transaction.commit();return null;}
}
- 获取代理工厂类
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;}
}
- 测试
@Test
public void serviceCjlibProxy() {((PersonServiceImpl) CjlibProxyFactory.getGcLibDynProxy(new PersonServiceImpl())).savePerson();
}开启事务
保存人了
提交事务
如果是增加了AnimalService则增加如下代码即可获取代理类:
((AnimalServiceImpl) CjlibProxyFactory.getGcLibDynProxy(new AnimalServiceImpl())).saveAnimal();开启事务
保存动物了
提交事务
写在后面
参考文章列表
java都有哪些动态代理机制? 。
Java中的代理模式——静态代理以及分析静态代理的缺点 。