正文
简介讲述代理的一些知识点【Java代理】【静态代理】【动态代理】【动态代理的2种方式】
一、代理模式#
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。
二、代理的分类#
- 静态代理
- 动态代理
三、静态代理#
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
我们一起看一下静态代理的例子:
1.定义一个接口
/** * @author :breakpoint/赵立刚 * @date : 2020/04/29 */ public interface SayHelloInterface { void sayHello(String name); }
2.定义实现类
/** * @author :breakpoint/赵立刚 * @date : 2020/04/29 */ public class SayHelloInterfaceImpl implements SayHelloInterface { @Override public void sayHello(String name) { System.out.println("hello:" + name); } }
3.定义我们的代理类
/** * @author :breakpoint/赵立刚 * @date : 2020/04/29 */ public class SayHelloProxy implements SayHelloInterface { private SayHelloInterface target; public SayHelloProxy(SayHelloInterface target) { this.target = target; } @Override public void sayHello(String name) { System.out.println("pre code"); target.sayHello(name); System.out.println("after code"); } }
4.定义测试类
/** * @author :breakpoint/赵立刚 * @date : 2020/04/29 */ public class Main { public static void main(String[] args) { SayHelloInterface sayHelloInterface=new SayHelloInterfaceImpl(); SayHelloProxy proxy=new SayHelloProxy(sayHelloInterface); proxy.sayHello("zhaoligang"); } }
5.运行结果
pre code hello:zhaoligang after code Process finished with exit code 0
静态代理总结:
- 1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
- 2.缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式。
四、动态代理#
动态代理有以下特点:
- 1.代理对象,不需要实现接口
- 2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
- 3.动态代理也叫做:JDK代理,接口代理
JDK中生成代理对象的API
- 代理类所在包:java.lang.reflect.Proxy
- JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
看一下我们动态代理的例子:
1.接口也是使用的我们前面的接口:
public interface SayHelloInterface { void sayHello(String name); }
2.定义我们的实现类
/** * @author :breakpoint/赵立刚 * @date : 2020/04/29 */ public class SayHelloInterfaceImpl implements SayHelloInterface { @Override public void sayHello(String name) { System.out.println("hello:" + name); } }
- 定义 SayHelloProxyHandler
/** * @author :breakpoint/赵立刚 * @date : 2020/04/29 */ public class SayHelloProxyHandler implements InvocationHandler{ private Object target; public SayHelloProxyHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("pre Code"); Object returnValue =method.invoke(target,args); System.out.println("after Code"); return returnValue; } }
4.测试
/** * @author :breakpoint/赵立刚 * @date : 2020/04/29 */ public class Main { public static void main(String[] args) { // SayHelloInterface sayHelloInterface=new SayHelloInterfaceImpl(); // // SayHelloProxy proxy=new SayHelloProxy(sayHelloInterface); // // proxy.sayHello("zhaoligang"); SayHelloInterface sayHelloInterface=new SayHelloInterfaceImpl(); SayHelloProxyHandler sayHelloProxyHandler=new SayHelloProxyHandler(sayHelloInterface); SayHelloInterface sayhello=(SayHelloInterface)Proxy.newProxyInstance(sayHelloInterface.getClass().getClassLoader(), sayHelloInterface.getClass().getInterfaces(),sayHelloProxyHandler); sayhello.sayHello("zhaoligang"); } }
5.运行结果
pre Code hello:zhaoligang after Code Process finished with exit code 0
最后的效果是一样的。
具体有如下四步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
五、Spring的2种代理方式#
spring容器在创建我们的对象以及注入,使用的是动态代理实现的,怎么看呢?我们看下图:
根据上面的图,我们很明显的看到,Spring容器给我们的对象是代理对象
spring 里面一共有2种代理方式:
- JDK代理
- GClib代理方式
5.1 JDK代理#
情况与上面的相似
看一下handler
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
5.2 GCLIB代理#
- cglib(Code Generation Library )是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
- cglib封装了asm,可以在运行期动态生成新的class。
- cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。
5.3 他们之间的区别#
- 原理区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP?
1.添加CGLIB库,SPRING_HOME/cglib/*.jar
2.在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和CGLIB字节码生成的区别?
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
- 因为是继承,所以该类或方法最好不要声明成final