SpringAOP(二)代理模式:动态代理
- 动态代理和静态代理的角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的(我们写一个模版,通过模版调用接口自动生成)
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口 — JDK动态代理
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序
动态代理的好处:
- 可以使着真实角色的操作更加的纯粹,不用去关注一些公共的业务
- 公共的就交给代理角色,实现业务的分工
- 公共业务发生拓展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要实现了同一个接口即可
package com.psy.demo03;
//租房
public interface Rent {
public void rent();
}
package com.psy.demo03;
//房东(真实角色)
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子");
}
}
package com.psy.demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//使用这个类自动生成代理类
public class ProxyInvocationHandle implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//实现接口的方法(处理代理实例并返回结果)
// invoke是接口的函数,getProxy的时候传了一个this,里面调用了invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的本质,就是使用反射机制实现
Object invoke = method.invoke(rent, args);
fare();
return invoke;
}
private void fare() {
System.out.println("收中介费");
}
private void seeHouse() {
System.out.println("中介带你看房子");
}
//生成得到代理类(利用reflect包下的Proxy类去生成)
public Object getProxy(){
// invoke是接口的函数,getProxy的时候传了一个this,里面调用了invoke
Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
return o;
}
}
package com.psy.demo03;
/**
* 动态代理:
* 真实角色不是写死的,可以调用多个真实角色(不同接口的)
* <p>
* 静态代理只能是一个代理类对应一个接口类,因为静态代理类中使用的真实角色的方法是固定的。
* 即,如果我传入的其他类型的真实角色中没有rent方法,那么就会报错(没有复用性)。
* <p>
* 在实际中,肯定不能只有一个接口(固定传入一个真实角色,),所以需要动态代理(动态的传
* 入不同类型的真实角色,方法是通过反射实现的,所以不用提前写好方法名(方法名可以不固定))。
* 方法名不固定+真实角色不固定=代理角色不固定=动态代理(动起来了)
* 相反,静态代理中真实角色必须是固定的,因为代理角色中掉赢了该角色的方法,所以真实角色需要固定
*/
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色
ProxyInvocationHandle proxyInvocationHandle = new ProxyInvocationHandle();
proxyInvocationHandle.setRent(host);
Rent proxy = (Rent) proxyInvocationHandle.getProxy();
// invoke是接口的函数,getProxy的时候传了一个this,里面调用了invoke
proxy.rent();
}
}
将动态代理类变成万能的(将被代理的类变成了通用的target,传什么用什么)
package com.psy.demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//使用这个类自动生成代理类
public class ProxyInvocationHandle implements InvocationHandler {
//被代理的接口(将之改造成通用的接口目标类)
private Object target;
public void setObject(Object target) {
this.target = target;
}
//实现接口的方法(处理代理实例并返回结果)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的本质,就是使用反射机制实现
Object invoke = method.invoke(target, args);
fare();
return invoke;
}
private void fare() {
System.out.println("收中介费");
}
private void seeHouse() {
System.out.println("中介带你看房子");
}
//生成得到代理类(利用reflect包下的Proxy类去生成)
public Object getProxy(){
Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return o;
}
}
总结:
静态代理:
抽象角色:接口或者抽象类(主要是真实角色的行为)
真实角色:实现抽象角色接口
代理角色:代理真实角色,代理角色类中有真实角色类的实例,然后代理角色就能使用真实角色的方法(公共业务),此外还能外加一些其他的方法。
客户端:客户端并不能直接通过真实角色的实例去调用真实角色的方法,只能通过代理角色的实例去调用该方法
动态代理(核心是生成代理角色的类):
角色和静态代理一样。但是代理角色是动态生成的。
那怎么样才能称之为代理呢?
1、抽象角色的方法名不固定
2、真实角色不固定
如何实现上述两个不固定呢?
1、抽象方法不固定
ProxyInvocationHandle(相对于静态代理来说新增的类,此类主要作用是生成代理角色)实现了InvocationHandler接口,该接口有invoke方法,此方法主要是通过反射机制实现抽象角色的方法(该方法主要是在客户端通过代理对象调用,然后Java动态代理会截获方法调用)。就可以实现抽象方法名的不固定。
2、真实角色的不固定
ProxyInvocationHandle中将实例属性从真实角色类改成Object即可,然后在客户端的时候通过set方法进行set就行,这就实现了真实角色的不固定。