说到Spring作为Java程序员大家都了解,每天都在用,但你真的知道它的底层实现原理吗?今天我们就来探讨一下Spring AOP的底层实现原理
概念 什么是AOP (这里我只截取有用部分不码太概念的东西)
AOP Aspect Oriented Programming 的缩写 意为面向切面编程
可以在不修改源码的前提下,对程序进行增强
Spring框架的AOP底层实现
1.Spring框架的AOP技术底层也是采用的代理技术,代理方式提供了两种
2.JDK的动态代理
必须是面向接口的,只有实现了具体接口的类才能生成代理对象
3.CGLib动态代理
对没有实现接口的类 可以产生代理 产生这个类的子类的方式
因为cglib需要Spring的环境 包括我们的测试需要Junit这里我们在pom加入如下依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>5.2.0.RELEASE</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.0.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
JDK动态代理
首先我们要创建一个接口(Person.class) 一个类(Student.class) 类实现接口并重写里面的方法 代码如下
package com.it520.springAop.JDK;
/**
* @author 玛丽莲梦明
* @描述
* @date 2020-09-02
*/
public interface Person {
void studyJava();
void studyPython();
}
package com.it520.springAop.JDK;
/**
* @author 玛丽莲梦明
* @描述
* @date 2020-09-02
*/
public class Student implements Person {
@Override
public void studyJava() {
System.out.println("学习Java");
}
@Override
public void studyPython() {
System.out.println("学习Python");
}
}
如上 我们要对 studyJava()方法进行增强 比如我在学习Java之前要先吃饭 学习完Java之后要睡觉 这时候我们就要对目标对象的方法进行增强 代码如下 (注释写的很详细)
package com.it520.springAop.JDK;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author 玛丽莲梦明
* @描述 JDK动态代理测试类
* @date 2020-09-02
*/
public class JdkProxyTest {
@Test
public void TEST_01() throws Exception {
// 这个就是目标对象 我们要对它进行增强
Person person = new Student();
// classLoader 后面会专门写一篇博客介绍ClassLoader的工作原理
ClassLoader classLoader = this.getClass().getClassLoader();
// 通过反射获取该类实现的所有接口
Class[] interfaces = person.getClass().getInterfaces();
// 将目标对象作为参数传给 MyInvocationHandler
MyInvocationHandler handler = new MyInvocationHandler(person);
// 生成代理对象 该对象已经增强
Person o = (Person) Proxy.newProxyInstance(classLoader, interfaces, handler);
o.studyJava();
}
/**
* 创建内部类 去实现 InvocationHandler
*/
class MyInvocationHandler implements InvocationHandler {
// 需要传入的目标对象 这里我们使用接口 因为接口更加灵活
Person person;
// 创建构造方法 并传入目标对象
public MyInvocationHandler(Person person) {
this.person = person;
}
/**
* @param proxy 是当前对象即代理对象
* @param method 当前调用的方法 (目标方法)
* @param args 实参
* @return 代理对象
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我先吃饭");
this.person.studyJava();
System.out.println("我要睡觉");
return null;
}
}
}
输出结果如下
我先吃饭
学习Java
我要睡觉
这里我们看到 我们的目标对象 person实现了功能的增强
Cglib动态代理
不多解释 代码如下
package com.it520.springAop.Cglib;
import com.it520.springAop.JDK.Student;
import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author 玛丽莲梦明
*/
public class CglibProxyTest {
@Test
public void TEST_01() {
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(Student.class);
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override // 代理对象方法执行 回调函数就会执行
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 通过反射获取方法名
if (method.getName().equals("studyJava")) {
System.out.println("我先吃饭");
}
// MethodProxy底层对Method进行了一层代理
Object invoke= methodProxy.invokeSuper(o, objects);
System.out.println("我要睡觉");
return invoke;
}
});
// 生成代理对象
Student o = (Student) enhancer.create();
o.studyJava();
}
}
输入结果如下
我先吃饭
学习Java
我要睡觉
以上代码并没有什么难度,但看懂以后,恭喜你对AOP的实现原理已经肆意妄为了
代理工厂 (ProxyFactory)
AOP 中 ProxyFactory
的子类有 ProxyCreatorSupport
AdvisedSupport
ProxyConfig
其中核心是 ProxyCreatorSupport
此类主要初始化了具体动态代理方案 其他 AdvisedSupport
ProxyConfig
主要是围绕 AOP 相关配置进行封装
上面那句话是我在别的博客抄的 但很能体现出ProxyFactory在AOP中的地位,直接贴源码看着乱七八糟的,我们来简单的模拟一下ProxyFactory的实现
首先定义 前置通知接口 BeforeAdvise
package com.it520.springAop.factory;
/**
* @author 玛丽莲梦明
* @描述
* @date 2020-09-02
*/
public interface BeforeAdvise {
/**
* 前置通知
*/
void doBefore();
}
然后定义后置通知接口
package com.it520.springAop.factory;
/**
* @author 玛丽莲梦明
* @描述
* @date 2020-09-02
*/
public interface AfterAdvise {
/**
* 后置通知
*/
void doAfter();
}
再然后定义我们的主类 ProxyFactory
package com.it520.springAop.factory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author 玛丽莲梦明
* @描述
* @date 2020-09-02
*/
public class ProxyFactory {
// 这是一个目标对象 即万物
private Object target;
// 前置通知
private BeforeAdvise beforeAdvise;
// 后置通知
private AfterAdvise afterAdvise;
// 这就是我们创建代理对象的主要方法
public Object createProxyObject() {
ClassLoader classLoader = this.getClass().getClassLoader();
Class[] interfaces = target.getClass().getInterfaces();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 开启前置通知
if (beforeAdvise != null) {
beforeAdvise.doBefore();
}
/**
* 执行目标方法 并拿到返回值 作为该方法的返回值
* @param target 目标对象
* @param args 实参
*/
Object invoke = method.invoke(target, args);
// 开启后置通知
if (afterAdvise != null) {
afterAdvise.doAfter();
}
// 返回代理对象
return invoke;
}
};
/**
* 这个就是生成的代理对象
*/
Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, handler);
return newProxyInstance;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public BeforeAdvise getBeforeAdvise() {
return beforeAdvise;
}
public void setBeforeAdvise(BeforeAdvise beforeAdvise) {
this.beforeAdvise = beforeAdvise;
}
public AfterAdvise getAfterAdvise() {
return afterAdvise;
}
public void setAfterAdvise(AfterAdvise afterAdvise) {
this.afterAdvise = afterAdvise;
}
}
定义完成 我们来测试一下 代码中注解写的很全面我就不再赘述了
package com.it520.springAop.factory;
import com.it520.springAop.JDK.Person;
import com.it520.springAop.JDK.Student;
import org.junit.jupiter.api.Test;
/**
* @author 玛丽莲梦明
* @描述
* @date 2020-09-02
*/
public class ProxyFactoryTest {
@Test
public void TEST_01() {
ProxyFactory factory = new ProxyFactory();
// 设置目标对象 这里我们还用之前JDK动态代理的那个Student对象
factory.setTarget(new Student());
// 设置前置通知内容 可以是任意方法 比如记录日志
factory.setBeforeAdvise(new BeforeAdvise() {
@Override
public void doBefore() {
System.out.println("我是前置通知");
}
});
// 设置后置通知内容
factory.setAfterAdvise(new AfterAdvise() {
@Override
public void doAfter() {
System.out.println("我是后置通知");
}
});
// 必须用接口去接收返回值
Person proxyObject = (Person) factory.createProxyObject();
// 执行接口中方法 代理方法就会执行
proxyObject.studyJava();
proxyObject.studyPython();
}
}
执行后的打印结果如下
我是前置通知
学习Java
我是后置通知
我是前置通知
学习Python
我是后置通知
解释
ProxyFactory类并没有指定具体要增强哪个对象,全部都是动态获取,也就是说我们定义的任何类传入都会得到增强,但增强的内容需要我们自己定义,这也就是Spring ProxyFactory实现的原理
另外说一点 对哪个方法增强 我们可以在invoke方法中使用 method.getName()获取对应的方法(参照Cglib增强部分代码) 选择性增强