动态代理代理模式 & 静态代理动态代理动态生成的代理类切面编程AOP
在前一篇文章中,把springboot的基本流程梳理了一遍。但里面有一个问题没有往深入了说:springboot作为一个javaBean的大盒子,这些bean是什么时候被加载到盒子里的,又是怎么样被加载进去的,哪些会被加载进去。在讲这些东西之前,还有一个非常重要的东西需要拿出来说一说,那就是动态代理技术。
动态代理
代理模式 & 静态代理
代理模式的类图:

在上图的代理模式中,总共有4个角色:
ISubject:接口,定义了代理或者被代理的行为。
ConcreteSubject:实现行为的具体的事物,可以称作被代理者。
SubjectProxy:代理者,典型的结构就是这个代理者的类包含了一个ConcreteSubject类的实例。在实现了ISubject接口的基础上另外定义了一些周边的行为函数,比如途中的preAction/postAction。
客户端,代理模式的使用者。
上面的都是IT专业用语,这里我用一个汽车经销商的例子来说明一下,并用代码来实现一下。
接口
车这个概念就是可以定义为一个接口,这个概念中有一个action就是售卖——sell。那么就可以定义出这个接口来了
public interface car {
void sellCar();
}被代理者
车只是一个抽象概念,拿来卖的话,还需要一个具体的车型。假设有两种车型:别克和奔驰。
public class buick implements car {
public void sellCar(){
System.out.println("buick: I'm a buick car");
}
}public class benz implements car {
public void sellCar(){
System.out.println("benz: I'm a benz car");
}
}代理者
现在一般车厂都不是自己进行销售,都是委托经销商来销售,那么经销商(代理)可能就需要有这些车(上面提到的代理者类中需要有一个被代理者的实例),并同样实现这样一个行为:销售——sell。
而经销商在销售之前,可能会有自己的一些宣传和促销行为。在销售之后可能需要你附加买一些七七八八的东西。这都不是车这个实物具备的行为,所以需要定义在代理类里面。
public class yongtong_seller implements car {
car seller_car;
public yongtong_seller(car seller_car){
this.seller_car = seller_car;
}
public void sellCar(){
guanggao(seller_car);
seller_car.sellCar();
zengsong(seller_car);
}
private void guanggao(car seller_car){
if (seller_car instanceof buick){
System.out.println("buick adv by yongtong");
}else if(seller_car instanceof benz){
System.out.println("bez adv by yongtong");
}
}
private void zengsong(car seller_car){
if (seller_car instanceof buick){
System.out.println("buick discont by yongtong");
}else if(seller_car instanceof benz){
System.out.println("bez discont by yongtong");
}
}
}客户端,也就是调用者。
//static proxy
System.out.println("###########static proxy demo begin#############");
buick buick_car = new buick();
yongtong_seller ys = new yongtong_seller(buick_car); //定义一个行的经销商,需要售出一台别克车型。
ys.sellCar();
System.out.println("###########static proxy demo end#############");输出结果为:
###########static proxy demo begin#############
buick adv by yongtong
buick: I'm a buick car
buick discont by yongtong
###########static proxy demo end#############上面的这种称作静态代理。
我们可以看到,在静态代理里,每个代理都需要去创建,而且需要实现某个接口。那么问题来了,如果这个经销商不只是卖车,我还要卖火车,如果使用静态代理模式的话,就需要再创建一套接口和实物。
public interface train {
void sellTrian();
}public class train_A implements train {
public void sellTrian(){
System.out.println("I'm trian_A");
}
}public class train_B implements train {
public void sellTrian(){
System.out.println("I'm trian_B");
}
}同样的,增加一个经销商:
public class trainAseller implements train {
train trainToSell;
public trainAseller(train train){
this.trainToSell = train;
}
public void sellTrian(){
before();
trainToSell.sellTrian();
after();
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}所以说,在静态代理里,每增加一个被代理者,除了需要定义这个被代理这的接口和实物类(car/buick,benz;train/train_A.train_B)之外,还需要定义个实现接口的代理类。如果我们业务系统中有非常多的这种接口和实物类,定义器代理类来也是一个非常大的工作量。
动态代理
所以,相对于静态代理,spring中使用到了动态代理的方式(还有其他的方式,这里我只拿jdk中的动态代理实现:InvocationHandler类来说一说)
定义一个动态代理类,这个类需要实现InvocationHandler接口,而不是像静态代理中的直接实现实物接口。在这个InvocationHandler接口中,只有一个函数invoke,在自己的动态代理类中重写这个方法。
public class ProxySeller implements InvocationHandler {
Object target; //实际被代理对象
public ProxySeller(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
guanggao();
Object result = method.invoke(target, args);
zengsong();
return result;
}
private void guanggao(){
if (target instanceof buick) {
System.out.println("I'm Seller, buick adv");
}else if(target instanceof benz){
System.out.println("I'm Seller, benz adv");
}else{
System.out.println("train");
}
}
private void zengsong(){
if (target instanceof buick) {
System.out.println("I'm jiucheng, buick discount");
}else if(target instanceof benz){
System.out.println("I'm jiucheng, benz discount");
}else{
System.out.println("train");
}
}
}在客户端调用时,直接使用同一个代理类,就可以同时代理上述的汽车和火车了,而不需要和静态代理一样创建不同的代理类。
//dynamic proxy
System.out.println("###########dynamic proxy demo begin#############");
//创建一个动态代理器,我要为这个对象做代理
InvocationHandler busiHandler = new ProxySeller(buick_car); //代理汽车
car seller = (car) Proxy.newProxyInstance(buick.class.getClassLoader(), buick.class.getInterfaces(), busiHandler);
InvocationHandler busiHandler_train = new ProxySeller(train_tosell); //代理火车
train train_seller = (train) Proxy.newProxyInstance(train_A.class.getClassLoader(), train_A.class.getInterfaces(), busiHandler_train);
seller.sellCar();
train_seller.sellTrian();
System.out.println("###########dynamic proxy demo end#############");动态生成的代理类
使用动态代理,实际上是生成了一个类,一般情况下看不到,所以需要修改下系统配置。
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");这样就可以在target里面形成一个com.sun.proxy目录下生成一个$Proxy#,#表示数字,从0开始的动态类。
我们可以打开这个类看一下。
package com.sun.proxy;
import com.zl.demo.business.car;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements car {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}public final void sellCar() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.zl.demo.business.car").getMethod("sellCar");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}这个代码一时半会看不明白,我自己画了一张调用的对比图,大家可以参考一下。

动态代理类关系与调用图

我理解就是通过在自定义的proxySeller类中,调用Proxy.newInstance函数创建一个动态的$Proxy类,这个类就是之前的静态代理中的类:
我这里有两个类:$Proxy0, $Proxy1,其中一个是train的接口代理。
public final class $Proxy1 extends Proxy implements train {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;然后再调用实际的静态代理类中的sellCar方法,这个代理方法中,再通过反射调用到实际的业务接口类:
m3 = Class.forName("com.zl.demo.business.car").getMethod("sellCar");简单来说,就是在静态代理的基础上又包装了一层,利用反射机制动态生成一个静态代理类。
切面编程AOP
spring框架的一大基石:面向切面编程(AOP)就是基于动态代理的方式来实现的。我理解的切面编程的应用场景:

在业务系统中,有若干的业务线,就像上图中画的,每个业务流程都是一条线。在每个业务流程中都会需要有日志输出的请求。以往的做法是在每条业务线中添加日志逻辑。这样就会设计到最起码4个业务类的编写和修改。那么切面编程的逻辑就是把这些逻辑抽取出来,形成一个切面类:就像上图画的,像在各个业务线中切上一刀,形成一个切面,所有的日志逻辑都可以写在这里,这种编写框架就可以称作切面编程。
在java体系中,所有的类都会被编译成字节码放到虚拟机中运行,那么切面类的代码运行的方式有很多中,可以把切面类的逻辑插入到业务逻辑中的静态aop,也可以生成一个代理类(上述逻辑提到的动态代理),还可以生成子类,把业务逻辑和切面逻辑共同处理,大致有如下几种方式。
