Spring AOP的代理模式详解
代理模式:代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
抽象主题角色:声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题
代理主题(Proxy)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯地将调用传递给真实主题对象。
真实主题角色:定义了代理角色所代表地真实对象
JDK动态代理:
面向接口生成代理,原理就是类加载器根据接口,在虚拟机内部创建接口实现类
Proxy.newProxyInstance(classloader,interfaces[], invocationhandler );
首先编写一个接口Fly:
public interface Fly {
public void gotoFly();
public void stopFly();
}
public class Bird implements Fly{
@Override
public void gotoFly() {
System.out.println("鸟儿张开翅膀要飞起来了。。。。");
}
@Override
public void stopFly() {
System.out.println("准备停飞。。。。");
}
public void eatBug(){
System.out.println("鸟要吃虫子,补充体力。。。");
}
}
</pre><pre name="code" class="java">import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
public class ProxyTest {
@Test
public void demo1(){
// JDK 自动代理 的原理是 根据 类加载器和接口 创建代理类(此代理类是接口的实现类,所以必须使用接口
// 1、 创建真是业务对象的引用
Fly fly = new Bird();
Fly proxy = (Fly) Proxy.newProxyInstance(fly.getClass().getClassLoader(), fly.getClass().getInterfaces(), new InvocationHandler() {
// 2、使用真是业务对象类加载器和接口,在内存中创建代理对象
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 拦截方法
if(method.getName().equals("gotoFly")){
System.out.println("被拦截了,鸟飞不走了。。。");
return null;
}
// 不拦截就invoke
return method.invoke(proxy, args);
}
});
proxy.gotoFly();
}
}
因为创建出来的这个代理类,一定是接口的子类,所以JDK动态代理一定要有接口,并且真实的业务类要实现该接口。
为什么说创建出来的代理类一定是接口的子类,看这个在最后 proxy 调用发放的时候 eclipse提示的信息就可以看出来,只有 Fly() 接口里面的两个方法 gotoFly() 和 stopFly() 并没有 Bird() 类中的 独有的方法。
所以说这个创建出来的代理类就是这个接口的子类。
其实可以大致推出来在java虚拟机里创建的代理类的样子
public void $Proxy0 implements Fly(){
public void gotoFly(){
handler.invoke(this,method,args());
}
}
当执行proxy.gotoFly();这个方法的时候,其实就会调用 Proxy.newProxyInstance(....{
invoke(.....)
})
方法。
所以说可以 在
Proxy.newProxyInstance(....{
invoke(.....)
})
这个方法里控制对真是对象方法的访问。
cglib 动态代理
使用范围:对于不适用接口的业务类,无法使用JDK动态代理
原理:CGlib采用非常底层的字节码技术,可以为一个类创建子类,解决无接口类的代理类问题。
下面看一下CGlib动态代理的过程;
首先编写一个业务类,不实现任何的接口:
/**
* 该业务类没有实现任何接口
* @author dell
*
*/
public class Cat {
public void run(){
System.out.println("猫可以跑步。。。捉老鼠");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
public class ProxyTest {
@Test
public void demo2(){
// cglib 动态代理 在目标业务类没有实现接口的情况下
// 1、创建真实业务对象的引用
Cat cat = new Cat();
// 2、创建真实业务类的子类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cat.getClass());
// 3、设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
if(method.getName().equals("run")){
System.out.println("cat的run方法被拦截了。。。。");
Object invoke = method.invoke(proxy, args);
System.out.println("真实方法拦截之后。。。。");
return invoke;
}
// 不拦截
return method.invoke(proxy, args);
}
});
Cat proxy = (Cat) enhancer.create();
proxy.run();
}
}