Java动态代理和cglib动态代理
一、何为动态代理?
建议看动态代理前,先看看反射 点击这里
先看一个小案例,假设有咖啡类A,B,C。有添加物类a,b,c,。现在对咖啡类进行增强。( 比如像向啡中加糖,牛奶等这个意思)。
对一个类进行增强可以有三种方式:
1.继承,可以直接继承父类的属性和方法,在新增自己的属性和方法。那么我们对每种咖啡进行增强,一共需要写3*3个继承类来完成。增强的对象和内容都不可变。
2.装饰者模式,在需要增强的类中组合一个需要增强的属性。那么我们需要写三个组合咖啡类,每个类都添加一个添加物接口属性,向添加物接口传不同的实现就可以对该咖啡类进行不同的增强。增强的内容可变,但增强的对象是不可变的。
3.动态代理,通过一个代理工厂,向其传入需要增强的咖啡类和添加物类,其自动生成一个增强后的代理类。我们只需要实现代理工厂这个类即可。
二、java动态代理的实现
java中有一个类Proxy,其有一个静态方法Object proxy = Proxy.newProxyInstance(ClassLoader classLoader , Class[] interfaces , InvocationHandler); 通过此方法就可以得到一个代理对象。下面说一下这三个参数。
ClassLoader 类加载器,负责加载代理的类加载器。关于类加载器详细解答 点击这里
Class[] 目标类所实现的接口们,用Class对象数组表示。
Invocationhandler 处理器,当调用代理类proxy的方法时,实际上是调用处理器的 invoke()方法。下面在对invoke方法进行详解。
Object invoke(Object proxy , Method method , Object[] args ) ;
proxy:代理对象。 method: 执行的代理对象的方法。 args: 调用代理对象的方法时传入的参数。
下面贴代码实现
目标类接口
package cn.edu; public interface Waiter { public void server(); }
前置通知接口
package cn.edu; public interface AdviceBefore { public void before(); }
后置通知接口
package cn.edu; public interface AdviceAfter { public void after(); }
代理工厂
package cn.edu; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 代理工厂,用于生产代理对象 * * @author WangHang * */ public class ProxyFactory { private Object target; // 目标对象 private AdviceBefore adviceBefore; // 前置通知 private AdviceAfter adviceAfter; // 后置通知 // 生成代理对象核心方法 public Object getProxyInstance() { ClassLoader loader = ProxyFactory.class.getClassLoader(); // 类加载器 Class[] interfaces = target.getClass().getInterfaces(); // 类所实现的接口 InvocationHandler h = new InvocationHandler() { // 处理器 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (adviceBefore != null) { adviceBefore.before(); // 调用前置通知 } Object result = method.invoke(target, args); // 调用目标方法 if (adviceAfter != null) { // 调用后置通知 adviceAfter.after(); } return result; } }; Object proxy = Proxy.newProxyInstance(loader, interfaces, h); // 通过给定的三个参数生成动态代理对象 return proxy; // 返回代理对象 } public AdviceBefore getAdviceBefore() { return adviceBefore; } public void setAdviceBefore(AdviceBefore adviceBefore) { this.adviceBefore = adviceBefore; } public AdviceAfter getAdviceAfter() { return adviceAfter; } public void setAdviceAfter(AdviceAfter adviceAfter) { this.adviceAfter = adviceAfter; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } }
测试,接口的实现用的匿名内部类
package cn.edu; import org.junit.Test; public class ProxyTest { @Test public void fun() { Waiter waiter = new Waiter() { //生成目标对象 @Override public void server() { System.out.println("服务中。。。"); } }; AdviceBefore before = new AdviceBefore() { //生成前置通知 @Override public void before() { System.out.println("欢迎光临!"); } }; AdviceAfter after = new AdviceAfter() { @Override public void after() { System.out.println("谢谢光临!"); //生成后置通知 } }; ProxyFactory proxyFactory = new ProxyFactory(); //生成工厂 proxyFactory.setTarget(waiter); //设置目标对象 proxyFactory.setAdviceBefore(before); //设置前置通知 proxyFactory.setAdviceAfter(after); //设置后置通知 Waiter proxy = (Waiter)proxyFactory.getProxyInstance(); //获得代理对象 proxy.server(); //调用目标对象的方法 } }
运行结果:
三、cglib动态代理的实现
java动态代理的实现要求目标类实现接口,如果没有接口就无法完成动态代理。cglib(Code Genaration Liarbry)是一个强大的Code生成类库 ,它可以在程序运行期间扩展Java类。它不要求目标类实现接口,它采用的是继承的方式来扩展目标类。
下面用cglib实现代理,其余类不用变,只需更改代理工厂,并且给Waiter添加一个实现类WaiterImpl,cglib代理是需要类的,用匿名类是会报错的。更改的部分如下。
Waiter实现类
package cn.edu.cglibProxy; public class WaiterImpl implements Waiter { @Override public void server() { // TODO Auto-generated method stub System.out.println("服务中。。。"); } }
代理工厂类
package cn.edu.cglibProxy; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; /** * 代理工厂,用于生产代理对象 * * @author WangHang * */ public class ProxyFactory { private Object target; // 目标对象 private AdviceBefore adviceBefore; // 前置通知 private AdviceAfter adviceAfter; // 后置通知 // 生成代理对象核心方法 public Object getProxyInstance() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { adviceBefore.before(); Object result = method.invoke(target, args); adviceAfter.after(); return result; } }); Object proxy = enhancer.create(); return proxy; // 返回代理对象 } public AdviceBefore getAdviceBefore() { return adviceBefore; } public void setAdviceBefore(AdviceBefore adviceBefore) { this.adviceBefore = adviceBefore; } public AdviceAfter getAdviceAfter() { return adviceAfter; } public void setAdviceAfter(AdviceAfter adviceAfter) { this.adviceAfter = adviceAfter; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } }
测试类
package cn.edu.cglibProxy; import org.junit.Test; public class ProxyTest { @Test public void fun() { Waiter waiter = new WaiterImpl(); AdviceBefore before = new AdviceBefore() { //生成前置通知 @Override public void before() { System.out.println("欢迎光临!"); } }; AdviceAfter after = new AdviceAfter() { @Override public void after() { System.out.println("谢谢光临!"); //生成后置通知 } }; ProxyFactory proxyFactory = new ProxyFactory(); //生成工厂 proxyFactory.setTarget(waiter); //设置目标对象 proxyFactory.setAdviceBefore(before); //设置前置通知 proxyFactory.setAdviceAfter(after); //设置后置通知 Waiter proxy = (Waiter)proxyFactory.getProxyInstance(); //获得代理对象 proxy.server(); //调用目标对象的方法 } }
测试结果
四、总结
通过上面的两个例子,可以体会到动态代理的优势。静态代理是由程序员手动编写好源代码,在程序运行时已经存在了class文件,而动态代理则是在运行时根据需要动态生成的,不仅减少了工作量,使用时也更加灵活。