代理模式是常用的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("添加一本新书");
}
}
3、代理类
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("添加一本新书");
}
}
2、代理类
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)应用到目标类中。