动态代理
1、为什么需要代理
public class Math { public int add(int n1,int n2){ int result=n1+n2; System.out.println(n1+"+"+n2+"="+result); return result; }
比如定义好的类,此时需要在加法后添加新功能,比如打印"hello,world。",就需要修改代码,这样会违反开闭原则,单一职责、依赖倒转等。
2、静态代理
下面使用静态代理实现:
public interface IMath { //加 int add(int n1, int n2); }
public class Math implements IMath { //加 public int add(int n1,int n2){ int result=n1+n2; System.out.println(n1+"+"+n2+"="+result); return result; }
}
public class MathProxy implements IMath { //被代理的对象 IMath math=new Math(); //加 public int add(int n1, int n2) { //开始时间 long start=System.currentTimeMillis(); lazy(); int result=math.add(n1, n2); Long span= System.currentTimeMillis()-start; System.out.println("共用时:"+span); return result; } }
public class Test { IMath math=new MathProxy(); @org.junit.Test public void test01() { int n1=100,n2=5; math.add(n1, n2)
}
}
从上面的实例代码上可以看出来代理类必须和被代理类实现一个接口。静态代理解决了上述的问题,但是每一个被代理类都要实现一个代理类,会造成大量的工作任务。下面由动态代理来解决。
3、动态代理
3.1、JDK动态代理
只需要一个代理类,而不是针对每个类编写代理类。通过传参数来实现所有的被代理类。下面先分析一下jdk动态代理的过程。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Random; /** * 动态代理类 */ public class DynamicProxy implements InvocationHandler { //被代理的对象 Object targetObject; /** * 获得被代理后的对象 * @param object 被代理的对象 * @return 代理后的对象 */ public Object getProxyObject(Object object){ this.targetObject=object; return Proxy.newProxyInstance( targetObject.getClass().getClassLoader(), //类加载器 targetObject.getClass().getInterfaces(), //获得被代理对象的所有接口 this); //InvocationHandler对象 //loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来生成代理对象进行加载 //interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 //h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上,间接通过invoke来执行 } /** * 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口 * proxy 被代理后的对象 * method 将要被执行的方法信息(反射) * args 执行方法时需要的参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //被织入的内容,开始时间 long start=System.currentTimeMillis(); lazy(); //使用反射在目标对象上调用方法并传入参数 Object result=method.invoke(targetObject, args); //被织入的内容,结束时间 Long span= System.currentTimeMillis()-start; System.out.println("共用时:"+span); return result; } //模拟延时 public void lazy() { try { int n=(int)new Random().nextInt(500); Thread.sleep(n); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Test { //实例化一个MathProxy代理对象 //通过getProxyObject方法获得被代理后的对象 IMath math=(IMath)new DynamicProxy().getProxyObject(new Math()); @org.junit.Test public void test01() { int n1=100,n2=5; math.add(n1, n2);
}
}
IMath math=(IMath)new DynamicProxy().getProxyObject(new Math());这行代码生成代理类,
math.add(n1, n2);这行代码 调用动态代理类DynamicProxy中invoken()方法,在invoke方法中写需要增强的功能,然后method.invoke(targetObject, args);是调用被代理类中的业务方法。
实现jdk动态代理的关键是InvocationHandler接口和Proxy类,以及使用反射机制生成代理类。
在获取被代理的对象是采用Proxy.newProxyInstance方法,最后ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);接口是通过反射获取。通过反射我们可以得出
代理类实现Proxy类,为了获得有关方法和InvocationHandler构造方法传参,但是代理类又必须关联被代理类,因为java类单继承的特性,所以只能写一个接口。
这就是为什么JDK动态代理代理的类必须要实现一个接口。
3.2、CGLIB动态代理
public class BookFacadeImpl1 { public void addBook() { System.out.println("新增图书..."); } }
public class BookFacadeCglib implements MethodInterceptor { private Object target;//业务类对象,供代理方法中进行真正的业务方法调用 //相当于JDK动态代理中的绑定 public Object getInstance(Object target) { this.target = target; //给业务对象赋值 Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类 enhancer.setSuperclass(this.target.getClass()); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类) //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦 enhancer.setCallback(this); // 创建动态代理类对象并返回 return enhancer.create(); } // 实现回调方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("预处理——————"); proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法 System.out.println("调用后操作——————"); return null; }
public static void main(String[] args) { BookFacadeImpl1 bookFacade=new BookFacadeImpl1(); BookFacadeCglib cglib=new BookFacadeCglib(); BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(bookFacade); bookCglib.addBook(); }
代理类BookFacadeCglib 实现MethodInterceptor接口。
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(bookFacade); 获取代理对象。
bookCglib.addBook(); 会去调intercept(),方法中 proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法。CGLIB通过子类的方法生成代理类。
原理:通过源码分析,得知生成代理类的类为MethodProxy,生成的代理继承了业务类,也就是说代理类是被代理类的子类,因此被代理类中被代理的方法不能是final修饰的。CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类。
3.3、JDK动态代理和CGLIB代理的区别
JDK动态代理只能对实现了接口的类生成代理,而不能针对类。 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。