1. 静态代理
代理与被代理对象都要同时实现某个接口
首先是接口定义:
public interface MagicTaskService { void doSomething(); void doSomethingElse(); }
被代理对象实现该接口:
public class MagicTaskServiceImpl implements MagicTaskService { @Override public void doSomething() { System.out.println("原生对象doSomething"); } @Override public void doSomethingElse() { System.out.println("原生对象doSomethingElse"); } }
代理对象实现接口,构造器传入 接口,我们在被代理对象的基础上增加 代理方法的实现:
public class MagicTaskServiceProxy implements MagicTaskService { private MagicTaskService magicTaskService; public MagicTaskServiceProxy(MagicTaskService magicTaskService){ this.magicTaskService=magicTaskService; } @Override public void doSomething() { System.out.println("代理对象执行doSomething"); magicTaskService.doSomething(); } @Override public void doSomethingElse() { System.out.println("代理对象执行doSomethingElse"); magicTaskService.doSomethingElse(); } }
main 方法测试:
public static void main(String[] args) { MagicTaskService magicTaskService=new MagicTaskServiceImpl(); MagicTaskServiceProxy proxy=new MagicTaskServiceProxy(magicTaskService); proxy.doSomething(); proxy.doSomethingElse(); }
执行结果:
代理对象执行doSomething 原生对象doSomething 代理对象执行doSomethingElse 原生对象doSomethingElse
静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2.缺点:
– 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式
2.动态代理
动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,该方法需要接收三个参数 。最终的返回值是个Object,我们利用生成代理对象后,需要强转为 我们需要的对象,然后调用 代理对象的方法就可以了。
public class MagicProxyFactory { private Object target; public MagicProxyFactory(Object target) { this.target = target; } public Object getProxyInstance(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("动态代理执行"); // 执行目标对象方法 Object value=method.invoke(target,args); return value; } }); } }
方法测试:
public static void main(String[] args) { MagicTaskService magicTaskService=new MagicTaskServiceImpl(); MagicTaskService proxy=(MagicTaskService)new MagicProxyFactory(magicTaskService).getProxyInstance(); proxy.doSomething(); proxy.doSomethingElse(); }
执行结果:
动态代理执行
原生对象doSomething
动态代理执行
原生对象doSomethingElse
总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。动态代理是利用反射机制实现的代理。
3.Cglib 代理
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理。
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
-
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
-
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
-
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
-
Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。
Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。
创建一个 未实现任何接口的被代理对象:
public class MagicTaskServiceNew { public void doSomething() { System.out.println("未实现接口的原生对象doSomething"); } public void doSomethingElse() { System.out.println("未实现接口的原生对象doSomethingElse"); } }
创建Cglib 代理类:
public class MagicCglibProxyFactory implements MethodInterceptor { private Object target; public MagicCglibProxyFactory(Object target){ this.target=target; } // 给目标对象创建代理对象 public Object getProxyInstance(){ // 工具类 Enhancer en=new Enhancer(); // 设置 父类 en.setSuperclass(target.getClass()); // 设置回调 en.setCallback(this); // 创建子类即代理对象 return en.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("执行Cglib 代理开始"); Object value=method.invoke(target,objects); return value; } }
测试:
public static void main(String[] args) { MagicTaskServiceNew magicTaskServiceNew=new MagicTaskServiceNew(); MagicTaskServiceNew proxy=(MagicTaskServiceNew)new MagicCglibProxyFactory(magicTaskServiceNew).getProxyInstance(); proxy.doSomething(); proxy.doSomethingElse(); }
执行结果:
执行Cglib 代理开始 未实现接口的原生对象doSomething 执行Cglib 代理开始 未实现接口的原生对象doSomethingElse
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
参考资料:
http://www.cnblogs.com/cenyu
https://dzone.com/articles/cglib-missing-manual