Spring07之AOP的代理模式
AOP的核心是动态代理模式。
要掌握AOP,就要掌握代理模式。
代理模式有两种,分别为静态代理和动态代理。
学习动态代理之前,先掌握静态代理。
一、静态代理
一共有四个角色:
1.抽象角色
一般使用接口或者抽象类来解决。例如租房这件事情。
抽象角色
public interface Rent {
void rent();
}
2.真实角色
被代理的角色。例如房东。
public class Host implements Rent {
@Override
public void rent() {
System.out.println("我是房东,我有房子");
}
}
3.代理角色
代理真实角色,并且增加一些附加操作。例如中介。
public class Proxy {
private Rent rent; //多态!
public Proxy(Rent rent) {
this.rent = rent;
}
public void rent(){
seeHouse();
rent.rent();
fare();
}
public void seeHouse(){
System.out.println("看房子");
}
public void fare(){
System.out.println("收取中介费");
}
}
4.客户(测试)
访问代理角色的人。
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
二、JDK原生动态代理
1、理解动态代理为何要这样写
我们的需求
动态代理,就是被代理类随机,根据加载到内存中的被代理类,来创建代理类对象。
由此需求,我们对于代理类的模板要求如下:
如何解决
第一、模板可根据反射获取被代理类中的方法名、方法参数、方法返回值。
第二、将代理类和被代理类绑定起来
第三、代理类对象执行方法,实质上是被代理类对象在执行。
假设代理类对象是A。被代理类对象是B。
A.method1(arg1,arg2)的内部其实是
A.method(){
B.method1(arg1,arg2);
}
因此A中需要这样一个方法处理程序,通过
A.method1(arg1,arg2) 获取到方法名,方法参数
然后在method里复造这个method1这个方法,并暗箱操作,让B来调用这个方法。
这个方法处理程序就是InvocationHandler。
由于内部调用了B对象,因此在InvocationHandler中B应该作为属性出现,并通过构造器或方法赋值。
2、案例一:租房案例
四个角色加一个方法处理程序
1、抽象角色
public interface Rent {
void rent();
}
2、真实角色
房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("我是房东,我有房子");
}
}
3.代理角色
public class ProxyRent {
public static Object getProxyInstance(Object obj){
MyInvocationHandler handler = new MyInvocationHandler(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
4.方法处理程序
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(Object obj) {
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object returnValue = method.invoke(obj, args);
fare();
return returnValue;
}
public void seeHouse(){
System.out.println("看房子");
}
public void fare(){
System.out.println("收取中介费");
}
}
5.客户(测试)
public class Client {
public static void main(String[] args) {
Host host = new Host();
Rent proxyInstance = (Rent) ProxyRent.getProxyInstance(host); //强制类型转换时,只能转换成父接口,不能转换成实现了这个接口的子类
proxyInstance.rent();
}
}
注意:
强制类型转换时,只能转换成父接口,不能转换成实现了这个接口的子类。
ProxyRent.getProxyInstance(host)这个方法返回的是实现了Rent接口的代理类的对象。只能强制转换成Rent类型的对象。
2、案例二之超人案例
1、抽象角色
public interface Human {
String getBelieve();
void eat(String food);
}
2、真实角色
public class SuperMan implements Human {
@Override
public String getBelieve() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
3、代理角色
public class ProxyFactory {
//1.参数传入被代理类的对象,返回一个代理类的对象
public static Object getProxyInstance(Object obj){
MyInvocationHandler handler = new MyInvocationHandler(obj);
//Proxy.newProxyInstance() 该方法专门用于创建并返回代理类的对象
//参数1:被代理类对象的运行时类的类加载器;类加载器用于将字节码文件加载到内存中,动态创建代理类对象,自然需要一个类加载器。
//参数2:被代理类对象的运行时类的实现的接口;指明代理类需要实现什么接口。
//参数3:实现了InvocationHandler接口的类的对象;
// 放一个对象在这里的目的是:
// 该方法动态创建并返回的代理类对象,不管调用啥方法,都会自动调用handler里的invoke().解决了问题3.
// 因此被代理类要想实现的功能就应该放在invoke()里面。
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
4、方法处理程序
public class MyInvocationHandler implements InvocationHandler {
//obj:被代理类的对象
private Object obj;
public MyInvocationHandler(Object obj){
this.obj=obj;
}
//当我们通过代理类的对象调用方法a时,就会自动调用如下方法:invoke()
//将被代理类要执行的方法a的功能声明在invoke()中
/*
* 参数1:代理类对象。先有代理类对象,再是调用方法。
* 参数2:代理类对象调用的方法名
* 参数3:方法的参数。例如"火锅",就作为参数传了进来
*
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法。
//obj:被代理类的对象。本质上还是被代理类在调用该方法。
Object returnValue = method.invoke(obj, args);
//method方法的返回值也是invoke()方法的返回值
return returnValue;
}
}
5、测试
public class ProxyTest {
public static void main(String[] args) {
//创建被代理类对象
SuperMan superMan = new SuperMan();
//获取代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
/*
代理类对象调用被代理类对象的方法:
调用时,会自动进入handler里面的invoke()。
invoke(Object proxy, Method method, Object[] args)
proxy已经被绑定了,method就是 eat ,args就是"火锅"。
然后invoke()里面还是被代理类对象在调用该方法。
* */
proxyInstance.eat("火锅");
System.out.println(proxyInstance.getBelieve());
}
}