设计模式之代理模式:三种代理模式的实现方式
前言:代理模式和另外一种设计模式--装饰者模式十分相像,他们都是在不修改目标对象源代码的基础上,对源代码的一次重构。只是,代理模式,关注的是目标对象的使用,他有目标的控制权,而装饰者模式,只是对目标对象功能的增强,仅此而已,不拥有目标对象的控制权。
代理模式有三种实现方式:
- 继承目标对象的同一个接口,引入目标对象,重写方法;
- 使用JDK动态代理的API,动态生成一个动态代理对象的实例;
- 使用CGlib,在不需要传入接口的条件下,就可以重构代码;
一、土味代理模式
所谓土味代理模式,就是最原生的实现方式,不使用任何API。
步骤:
- 继承目标对象的接口;
- 引入目标对象的成员变量,并重写目标对象的方法;
代码:
1.目标对象源码:
/**
* 接口实现
* 目标对象
*/
public class UserDao implements IUserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
2.代理对象源码:
public class UserDaoProxy implements IUserDao{
//接收保存目标对象
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target=target;
}
public void save() {
System.out.println("开始事务...");
target.save();//执行目标对象的方法
System.out.println("提交事务...");
}
}
此方法实现简单,也确实完成了不修改目标对象的代码的情况下对代码重构的目的,但是缺点也十分明显,有几个目标对象要生成代理对象,就得维护几个代理对象,如果接口增加方法,那么代理对象相应的也有进行修改。
二、JDK实现动态代理
上述方法是静态代理的实现,接下来介绍两种动态代理的实现方式,所谓动态代理,就是动态的生成一个代理对象,但实际上并没有写一个代理对象的类。
JDK实现动态代理就是用JDK原有的API传入目标对象的类加载器和接口,再重写代理方法,返回一个代理对象。
代码:
代理对象的工厂类:
/**
* 创建动态代理对象
* 动态代理不需要实现接口,但是需要指定接口类型
*/
public class ProxyFactory{
//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}
}
JDK动态代理的确实现了动态代理,无需重写接口,多维护几个代理类,但是,需要传入接口,一样成为了限制他的缺点。
三、CGlib实现动态代理
CGlib实现需要先导入相应的jar包,相关的代码如下:
动态代理工厂类:
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
CGlib底层是动态的在内存中生成了目标对象的子类的字节码,并生成相应的对象,JDK动态代理实现则是使用的反射机制。
CGlib实现动态代理能满足没有接口的目标对象的代理对象实现。
所以,在Spring中,实现AOP的AspectJ框架底层,会对无接口的目标对象采用CGlib,有接口的目标对象则采用JDK动态代理。