JAVA动态代理
0. 前言
学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,它利用的是反射机制,依赖注入就不用多说了,而对于Spring的核心AOP来说,使用了动态代理,所以本篇随笔就是对java的动态代理进行一个回顾
1. 代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等
2. 静态代理
由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。缺点:代理类和被代理类编译期间都写死了,不够灵活
package com.zad.java; /** * * 静态代理举例 * 代理类和被代理类编译期间都写死了,不够灵活 */ interface smile{ void produceCloth(); } /**代理类**/ class ProxyClothFactory implements smile{ private smile factory;//用被代理类对象进行实例化 public ProxyClothFactory(smile factory){ this.factory = factory; } @Override public void produceCloth() { System.out.println("你真的"); factory.produceCloth(); System.out.println("是不是"); } } //被代理类 class realsmile implements smile{ @Override public void produceCloth() { System.out.println("很开心"); } } public class StaticProxyTest { public static void main(String[] args) { //创建被代理类对象 realsmile real = new realsmile(); //创建代理类对象 ProxyClothFactory proxyClothFactory = new ProxyClothFactory(real); proxyClothFactory.produceCloth(); } }
3. 动态代理
Java
反射提供了一种类动态代理机制,可以通过代理接口实现类来完成程序无侵入式扩展。
Java动态代理主要使用场景:
- 统计方法执行所耗时间。
- 在方法执行前后添加日志。
- 检测方法的参数或返回值。
- 方法访问权限控制。
- 方法
Mock
测试。
创建动态代理类会使用到java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。java.lang.reflect.Proxy
主要用于生成动态代理类Class
、创建代理类实例。proxy类实现了java.io.Serializable
接口
InvocationHandler接口
java.lang.reflect.InvocationHandler
接口用于调用Proxy
类生成的代理类方法,该类只有一个invoke
方法,那么也必须要实现该方法,入口参数Object proxy即为要被代理的对象,method为被调用的方法,args为代理实例的方法参数数组,若没参数则为null
proxy类
该类中我们用的最多的就是 newProxyInstance 这个方法,这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
package com.zad.java; import org.omg.CORBA.SystemException; import org.omg.CORBA.portable.InputStream; import org.omg.CORBA.portable.InvokeHandler; import org.omg.CORBA.portable.OutputStream; import org.omg.CORBA.portable.ResponseHandler; import sun.awt.AWTAccessor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 动态代理举例 * * * **/ interface Human{ String getBelief(); void eat(String food); } //被代理类 class SuperMan implements Human{ @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("我喜欢吃"+food); } } /** * * proxy:代表动态代理对象 * method:代表正在执行的方法 * args:代表调用目标方法时传入的实参 * * 实现动态代理需要解决的问题: * 问题1:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。 * 问题2:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a */ class ProxyFactory{ //调用此方法,返回一个代理类的对象,解决了问题1 public static Object getProxychInstance(Object obj){//obj:被代理类的对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } } class MyInvocationHandler implements InvocationHandler { private Object obj; public void bind (Object obj){ this.obj = obj; } //当我们通过代理类的对象,调用方法a,就会自动调用如下方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method:就是代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 Object returnValue = method.invoke(obj, args); return returnValue; } } public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); Human proxyInstance = (Human) ProxyFactory.getProxychInstance(superMan); proxyInstance.getBelief(); proxyInstance.eat("麻辣烫"); } }
cglib代理
这里我们先简单说一下这两种代理方式最大的区别,JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口,那么代理类和目标类的方法名就一样了,这种方式上一篇说过了;CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法
cglib特点:
- 可以代理没有实现接口的类
- 可以在运行期扩展java类与实现java接口,被许多aop框架所使用,例如spring AOP和dynaop提供方法的interception拦截
cglib和动态代理的区别
- 使用动态代理必须实现接口
- cglib无需实现接口,达到代理类无侵入
引入cglib的jar包(https://repo1.maven.org/maven2/cglib/cglib/3.2.5/cglib-3.2.5.jar)
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency>
男的写了