代理模式是SpringAOP的底层!
分类:
- 静态代理
- 动态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
- 接口
- 真实角色
- 代理角色
- 客户端访问代理
一、静态代理
// 接口:租房业务
public interface Rent {
public void rent();
}
// 真实角色-房东:具有租房功能
class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子🏠");
}
}
// 代理角色:代理房东出租房子,同样具有出租房子的功能。代理的附加功能可以看作对房东租房子功能的增强。
class Proxy implements Rent{
private Host host;
public Proxy() {}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
fare();
}
//代理的附加功能1
public void seeHouse(){
System.out.println("代理帮助房东介绍房子给用户👦");
}
//代理的附加功能2
public void fare(){
System.out.println("代理向房东收取代理费用💵");
}
}
// 客户:访问代理,解决需求
public class Client {
public static void main(String[] args) {
//房东亲自出租房子
Host host = new Host();
host.rent();
System.out.println("====================");
//代理帮助房东出租房子
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
输出:
好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共业务交给代理角色,实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实对象就会产生一个代理对象,代码量会翻倍~开发效率变低~
加深理解(AOP):
二、动态代理
动态代理的代理类是动态生成的,不是直接写好的!
动态代理分为两大类:基于接口的动态代理、基于类的动态代理
- 基于接口:JDK动态代理
- 基于类:cjlib动态代理
- java字节码实现:javasist
1. JDK动态代理
两个关键类:Proxy:代理、InvocationHandler:调用处理程序
// 租房业务
interface RentBusiness {
public void rent();
}
//房东:具有租房功能
class Host implements RentBusiness{
@Override
public void rent() {
System.out.println("房东要出租房子🏠");
}
}
//程序处理角色
class ProxyInvocationHandler implements InvocationHandler {
//被代理的角色
private Object represented;
public void setRepresented(Object represented){
this.represented = represented;
}
//生成得到代理类
public Object getProxy(){
//三个参数:classloader、interface、invocationHandler
return Proxy.newProxyInstance(this.getClass().getClassLoader(), represented.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置增强
seeHouse();
//动态代理的本质,就是使用反射机制实现
Object result = method.invoke(represented,args);
//后置增强
fare();
return result;
}
public void seeHouse(){
System.out.println("代理帮助房东介绍房子给用户👦");
}
public void fare(){
System.out.println("代理向房东收取代理费用💵");
}
}
public class Client {
public static void main(String[] args) {
//代理角色现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置被代理角色
pih.setRepresented(new Host());
//动态生成代理角色
RentBusiness Proxy = (RentBusiness)pih.getProxy();
//代理角色调用相关接口的方法时,会自动调用invoke方法,在接口方法执行前后做增强
Proxy.rent();
}
}
输出:
关键:
被代理的角色时object类型,所以该程序处理角色,可以对任意类型的被代理角色
进行代理,实现被代理角色任意方法的增强。
总结:
- Proxy 通过 Proxy.newProxyInstance 动态生成代理对象,可代理任意类型的对象,且拥有一个相关联的调用处理程序。
- InvocationHandler 是由代理实例调用的处理程序实现的接口
每个代理实例都有一个关联的调用处理程序,当代理实例调用方法时,会触法自动执行调用处理程序实现的 invoke 方法。
2. cglib动态代理
CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、
dynaop)中,用以提供方法拦截操作。
CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。
//房东:具有租房功能
public class Host {
public void rent() {
System.out.println("房东要出租房子🏠");
}
}
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Host.class);
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("代理帮助房东介绍房子给用户👦");
Object invoke = method.invoke(new Host(), objects);
System.out.println("代理向房东收取代理费用💵");
return invoke;
}
});
// 代理角色
Host agent = (Host) enhancer.create();
agent.rent();
}
}
输出:
需要引入如下两个jar包
Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口
。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用
(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法
,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。