深入浅出设计模式——代理模式

1.代理模式介绍
2.用代码演示代理模式
3.总结
1.代理模式介绍
定义:
1)代理模式,为代理对象提供一个替身,以控制这个对象的访问,就是通过代理对象来访问对象。这样做的好处是:可以在目标对象的基础上,再增加额外的增强操作。
2)被代理的对象可以是远程对象,创建开销大的字段和需要安全控制的字段
3)代理模式有三种不同的形式,静态代理,动态代理,和cglib代理。
描述:
代理最著名的使用场景就是人尽皆知的AOP,就拿我们很熟悉的事务注解@Transactional来说,我们知道加上了这个注解后,函数方法就会成为事务操作。
我们都知道,用sql写事务的时候, 如果我们要进行事务操作,需要写两个这样的sql语句:

start transaction; //中间执行业务逻辑commit;

这时我们写好了中间的业务逻辑,加上注解之后,相当于把这个对象交给了一个代理对象,让代理对象执行开启事务和结束时的逻辑,我们只要编写业务逻辑就好了。同时这个方法也得到了增强,有了前置和后置方法,有了事务的效果,增强额外的功能操作。
2.用代码演示依代理模式
1)静态代理
静态代理模式的基本介绍:
静态代理在使用的时候,需要定义接口或者父类,被代理对象(目标对象)与代理对象一起实现共同的接口或者是继承相同的父类。
具体要求
1)定义一个接口ITeacherDao
2)目标对象TeacherDAO实现接口ITeacherDAO
3)定义一个代理对象TeacherProxy,也实现ITeacherDao
4)调用的时候通过调用代理对象的方法来调用目标对象
5)代理对象与目标要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。
代码展示
ITeacherDao:
public interface ITeacherDao {void teach(); //授课的方法 }

TeacherDao:
public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("老师开始上课了!"); } }

TeacherProxy:
public class TeacherProxy implements ITeacherDao {//先把要代理的对象传进来 private ITeacherDao target = new TeacherDao(); public TeacherProxy(ITeacherDao target) { this.target = target; }//执行代理方法,在方法前后进行增强 @Override public void teach() { System.out.println("代理开始"); target.teach(); System.out.println("代理结束"); } }

public static void main(String[] args) { //被代理对象 ITeacherDao iTeacherDao = new TeacherDao(); //代理对象 TeacherProxy teacherProxy = new TeacherProxy(iTeacherDao); //执行代理对象的方法 teacherProxy.teach(); }

优点:符合开闭原则,能对目标对象进行扩展。
缺点:为每一个需要增强的服务都需要创建类,容易形成类爆炸。
【深入浅出设计模式——代理模式】2)动态代理
动态代理基本介绍:
1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能使用
2)代理对象的生成,是利用jdk的api,动态的在内存中构建代理对象
3)动态代理也叫作:jdk代理,接口代理
代码实现:
ITeacherDao:
public interface ITeacherDao {void teach(); //授课的方法 }

TeacherDao:
public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("老师开始上课了!"); } }

ProxyFactory:
public class ProxyFactory {//维护一个目标对象 private Object target; //创建构造器对一个目标对象进行初始化 public ProxyFactory(Object target) { this.target = target; }public Object getProxyInstance() { //当前对象使用的目标类加载器 ClassLoader classLoader = target.getClass().getClassLoader(); //目标对象实现的接口类型,使用泛型的方式确认类型 Class[] classes = target.getClass().getInterfaces(); //执行目标对象的方法时,会出发事情处理方法,会把当前执行的对象当做参数传入 InvocationHandler invocationHandler = null; return Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强逻辑 System.out.println("JDK代理开始"); //原方法执行逻辑,执行完之后返回值 Object invoke = method.invoke(target, args); System.out.println("JDK代理结束"); return invoke; } }); } }

要注意一下Proxy.newProxyInstance() 方法有三个参数,
Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
Class[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
只需要按照上述的注释传入就可以了,用法也是十分简单。
优点:动态代理大大减少了我们的开发任务,同时减少了业务接口依赖。
缺点:始终无法摆脱interface代理的桎梏,无法实现对class的动态代理。
3)cglib代理
1)需要引入cglib的依赖
2)通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
代码展示:
TeacherDao:
public class TeacherDao {public void teach() { System.out.println("老师开始上课了!"); }}

CglibProxyFactory:
public class CglibProxyFactory implements MethodInterceptor { //要增强的目标类 private Object target; public CglibProxyFactory(Object target) { this.target = target; }public Object getProxyInstance() { // 创建工具类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //创建子类对象,即代理对象 return enhancer.create(); }//重写intercept,会调用目标对象的方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //增强的逻辑 System.out.println("cglib开始"); //执行完毕后返回的参数 Object invoke = methodProxy.invokeSuper(obj, args); //增强的逻辑 System.out.println("cglib结束"); //返回参数 return invoke; }}

public static void main(String[] args) { //目标代理对象 TeacherDao target = new TeacherDao(); //代理工厂 CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(target); //从代理工厂返回代理对象 TeacherDao proxyInstance = (TeacherDao) cglibProxyFactory.getProxyInstance(); //执行代理对象的方法 proxyInstance.teach(); }

优点:能对class进行增强,CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高。
缺点:CGLIB创建代理对象时所花费的时间比JDK多得多,而且编码不是非常方便,同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
3.总结
代理模式就是spring底层AOP的实现方法,目的就是为了把一些增强切入到业务逻辑代码里,并将这个切面统一管理。

    推荐阅读