在25章讲过代理模式,这里再开一章来详细讲解代理模式,因为在spring的aop核心技术就是动态代理,有必要把动态代理机制理解透彻:
代理模式的特征是代理类与委托类(被代理类)有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类的对象本身并不真正实现服务,而是通过调用委托类对象的相关方法,来实现功能,故还是由委托类完成核心的操作。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; //这里的proxy指的被代理的对象,调用委托类的方法
动态代理涉及到一个类:Proxy 是完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类;protected InvocationHandler h; //引用了InvocationHandler接口
private static class ProxyAccessHelper { //内部类
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{ // 生成实现类
//Class<?>[] interfaces:得到全部的接口
public interface Interface1 {
public void anyMethod();
}
public class Impl1 implements Interface1{
public void anyMethod(){
System.out.println("实现委托类功能");
}
}
public class ProxyImpl1 implements InvocationHandler{
private Object target; //委托类,确保执行时是同一target;
public Object createProxy(Object target) {
this.target = target; //确保是同一target
//利用反射机制完成
//Proxy.newProxyInstance(loader, interfaces, h);
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
//实现接口而来的this调用处理器
}
//jvm底层会将这个方法 完完全全的与它要代理的 方法绑定
//犹如:new B().fun() 执行了new BProxy().fun()一样;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
System.out.println("委托类方法"+method.getName()+"执行前");
result=method.invoke(target, args); //调用method的invoke方法
System.out.println("动态生成的代理类:"+proxy.getClass().toString());
return result;
}
}
测试:Impl1 i = new Impl1();
System.out.println("委托类对象:"+i.toString());
ProxyImpl1 pi = new ProxyImpl1();
//要用接口接收
Interface1 ip = (Interface1) pi.createProxy(new Impl1());
//用Impl接收会报com.sun.proxy.$Proxy0 cannot be cast to org.demo.api.Impl1
//Impl1 ip2 = (Impl1) pi.createProxy(new Impl1());
ip.anyMethod();
输出:
委托类对象:org.demo.api.Impl1@1dd3812
委托类方法anyMethod执行前
实现委托类功能
动态生成的代理类:class com.sun.proxy.$Proxy0
最后生成了一$Proxy0的这么一个类(不是对象,对象是有@符号,用toString也表明是一个类), 如果改为System.out.println("动态生成的代理类:"+proxy.toString()); 会报
Exception in thread "main" java.lang.StackOverflowError:at com.sun.proxy.$Proxy0.toString(Unknown Source)当应用程序递归太深而发生堆栈溢出时,抛出该错误。就是说传进来的这个对象找不到,而只能知道它属于$Proxy0类;
找遍了计算机也没找到$Proxy0.class文件,也不知道里面到底是什么内容。。
JDK的动态代理实现接口(interface1),如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。cglib是针对 类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
修改委托类(没有实现interface1接口):
public class Impl1 {
public void anyMethod(){
System.out.println("实现委托类功能");
}
}
代理类要实现MethodInterceptor 接口: 添加cglib.jar 或者用spring.core.jar也可以,只是里面有些参数意义不是很明确;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//import org.springframework.cglib.proxy.Enhancer;
//import org.springframework.cglib.proxy.MethodInterceptor;
//import org.springframework.cglib.proxy.MethodProxy;
public class ProxyImpl1 implements MethodInterceptor {
private Object target; //委托类,确保执行时是同一target;
public Object createProxy(Object target) {
//同样创建代理对象
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("委托类方法"+method.getName()+"执行前");
proxy.invokeSuper(obj, args); //与jvm相似
System.out.println("动态生成的代理类,是个内部类?:"+obj.getClass().toString());
return null; //返回为null与jvm有点不一样
}
// public Object intercept(Object arg0, Method arg1, Object[] arg2,
// MethodProxy arg3) throws Throwable {
// System.out.println("委托类方法"+arg1.getName()+"执行前");
// arg3.invokeSuper(arg0, arg2); //与jvm相似
// System.out.println("动态生成的代理类,是个内部类?:"+arg0.getClass().toString());
// return null;
// }
}
测试:
public static void main(String[] args) {
Impl1 i = new Impl1();
System.out.println("委托类对象:"+i.toString());
ProxyImpl1 pi = new ProxyImpl1();
//要用委托类接收
Impl1 ip2 = (Impl1) pi.createProxy(new Impl1());
ip2.anyMethod();
}
输出:
委托类对象:org.demo.api.Impl1@153f67e
委托类方法anyMethod执行前
实现委托类功能
动态生成的代理类,是个子类:class org.demo.api.Impl1$$EnhancerByCGLIB$$5bfa5f1a
测试中要用委托类来接收,因为这个例子中没有实现接口,也就没有接口;如果最先实现了接口会是怎么样呢?
测试发现,也是实现了动态代理。