代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。 在Java中主要可以分为JDK和CGLIB的方法实现。
下面来看看一些实例吧!
一、JDK方式
1、接口类
- package com.func.axc.proxy;
- /**
- * 功能概要:
- *
- * @author linbingwen
- * @since 2016年6月2日
- */
- public interface Book {
- public void addBook();
- }
2、实现类
- package com.func.axc.proxy;
- /**
- * 功能概要:
- *
- * @author linbingwen
- * @since 2016年6月2日
- */
- public class BookJdk implements Book {
- @Override
- public void addBook() {
- System.out.println("添加一本新书");
- }
- }
- package com.func.axc.proxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- /**
- * 功能概要:
- *
- * @author linbingwen
- * @since 2016年6月2日
- */
- public class BookProxyJdk implements InvocationHandler {
- private Object target;
- /**
- * 绑定委托对象并返回一个代理类
- * @param target
- * @return
- */
- public Object bind(Object target) {
- this.target = target;
- //取得代理对象
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result=null;
- System.out.println("jdk实现的前置代理");
- //执行方法
- result=method.invoke(target, args);
- System.out.println("jdk实现的前置代理");
- return result;
- }
- }
4、测试
- BookProxyJdk proxy = new BookProxyJdk();
- Book book = (Book) proxy.bind(new BookJdk());
- book.addBook();
jdk实现的前置代理
添加一本新书
jdk实现的前置代理
二、CGLIB动态代理
1、无继承的类
- package com.func.axc.proxy;
- /**
- * 功能概要:
- *
- * @author linbingwen
- * @since 2016年6月2日
- */
- public class BookCglib {
- public void addBook() {
- System.out.println("添加一本新书");
- }
- }
- package com.func.axc.proxy;
- import java.lang.reflect.Method;
- import org.springframework.cglib.proxy.Enhancer;
- import org.springframework.cglib.proxy.MethodInterceptor;
- import org.springframework.cglib.proxy.MethodProxy;
- /**
- * 功能概要:
- *
- * @author linbingwen
- * @since 2016年6月2日
- */
- public class BookProxyCglib implements MethodInterceptor {
- private Object target;
- /**
- * 创建代理对象
- *
- * @param target
- * @return
- */
- public Object getInstance(Object target) {
- this.target = target;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.target.getClass());
- // 回调方法
- enhancer.setCallback(this);
- // 创建代理对象
- return enhancer.create();
- }
- @Override
- public Object intercept(Object arg0, Method arg1, Object[] arg2,
- MethodProxy arg3) throws Throwable {
- System.out.println("cglib实现的前置代理");
- //通过代理类调用父类中的方法
- Object result = arg3.invokeSuper(arg0, arg2);
- System.out.println("cglib实现的后置代理");
- return result;
- }
- }
3、测试
- BookProxyCglib cglib=new BookProxyCglib();
- BookCglib bookCglib=(BookCglib)cglib.getInstance(new BookCglib());
- bookCglib.addBook();
cglib实现的前置代理
添加一本新书
cglib实现的后置代理
三、JDK动态代理和CGLIB字节码生成的区别?
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
JDK动态代理和CGLib的性能比较
CGLib所创建的动态代理对象的性能比JDK所创建的代理对象性能高不少,大概10倍,但CGLib在创建代理对象时所花费的时间却比JDK动态代理多大概8倍,所以对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建新的实例,所以比较适合CGLib动态代理技术,反之则适用于JDK动态代理技术。另外,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行处理。所以,大家需要根据实际的情况选择使用什么样的代理了。
同样的,Spring的AOP编程中相关的ProxyFactory代理工厂内部就是使用JDK动态代理或CGLib动态代理的,通过动态代理,将增强(advice)应用到目标类中。