项目地址:https://github.com/xiaogou446/jsonboot
使用branch:feature/implementAop
命令行:git checkout feature/implementAop
实现AOP
切面定义
在正式开始实现AOP之前,我们得先了解并定义AOP的相关注解以及信息,本地定义的注解有 @Aspect,该注解标注的类代表是一个切片,如果一个方法满足切面中的切入点的匹配规则,将会用于切面拦截器的拦截实现。@Pointcut代表是一个切入点,切入点定义了匹配的规则,一般使用文件路径进行匹配,标注在方法上。@Before标注在方法上,代表这个方法是目标方法前置拦截器内容实现。@After标注在方法上,代表这个方法是目标方法的后置拦截器内容实现。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Aspect {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Pointcut {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Before {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface After {
String value() default "";
}
其次还有个连接点的概念,JoinPoint,在平时使用Spring AOP时,会将JoinPoint作为参数传入,获取执行对象的信息,参数和切面的信息。我们也定义一下JoinPoint。
public class JoinPointImpl implements JoinPoint {
//切面bean
private final Object aspectBean;
//执行目标
private final Object target;
//执行参数
private Object[] args;
public JoinPointImpl(Object aspectBean, Object target, Object[] args){
this.aspectBean = aspectBean;
this.target = target;
this.args = args;
}
@Override
public Object getAspectBean() {
return this.aspectBean;
}
@Override
public Object getTarget() {
return this.target;
}
@Override
public Object[] getArgs() {
if (Objects.isNull(args)){
args = new Object[0];
}
Object[] array = new Object[args.length];
System.arraycopy(args, 0, array, 0, args.length);
return array;
}
}
切面实现
以上我们定义了切面,切入点,前后置处理器的注解定义,这里对这些注解进行一个实现,我们以拦截器的方式来实现注解。先定义一个拦截器抽象类,AOP的实现算是拦截器中的一种,也可以使用该接口拓展自定义的拦截器。拦截器的supports方法代表该拦截器是否适配目标对象,在AOP中进行切入点和路径的匹配。intercept是拦截的具体方法,内部真正执行 @Before和 @After的运行实现和目标方法的运行实现,MethodInvocation就是封装目标对象方法的实体。
public abstract class Interceptor {
/**
* 该拦截器是否适配该bean
*
* @param object bean
* @return 是否适配
*/
public boolean supports(Object object){
return false;
}
/**
* 方法执行器
*
* @param methodInvocation 方法执行器
* @return 返回数据
*/
public abstract Object intercept(MethodInvocation methodInvocation);
}
定义AOP相关的拦截器继承Interceptor,为定义的 @Before和 @After标注的方法进行扫描收录到map中,以及切入点表达式。具体的**intercept()**的实现中,先定义连接点,将连接点当作参数传入,遍历 @Before标注的方法,进行反射运行。再执行目标对象的方法,最后执行 @After注解标注的方法,以此来完成 @Before注解和 @After注解的具体实现。
public class InternallyAspectInterceptor extends Interceptor{
/**
* 切面bean
*/
private final Object aspectBean;
/**
* 切入点set
*/
private final HashSet<String> pointCutUrls = new HashSet<>();
/**
* 前置方法
*/
private final List<Method> beforeMethod = new ArrayList<>();
/**
* 后置方法
*/
private final List<Method> afterMethod = new ArrayList<>();
public InternallyAspectInterceptor(Object aspectBean){
this.aspectBean = aspectBean;
init();
}
/**
* 初始化方法, 将方法进行分类方法
*/
private void init(){
for (Method method : aspectBean.getClass().getMethods()){
Pointcut pointcut = method.getAnnotation(Pointcut.class);
if (!Objects.isNull(pointcut) && StringUtils.isNotBlank(pointcut.value())){
pointCutUrls.add(pointcut.value());
}
Before before = method.getAnnotation(Before.class);
if (!Objects.isNull(before)){
beforeMethod.add(method);
}
After after = method.getAnnotation(After.class);
if (!Objects.isNull(after)){
afterMethod.add(method);
}
}
}
@Override
public boolean supports(Object bean) {
return pointCutUrls.stream().anyMatch(url -> PatternMatchUtil.simpleMatch(url, bean.getClass().getName())) && (!beforeMethod.isEmpty() || !afterMethod.isEmpty());
}
@Override
public Object intercept(MethodInvocation methodInvocation) {
JoinPoint joinPoint = new JoinPointImpl(aspectBean, methodInvocation.getTargetObject(), methodInvocation.getArgs());
beforeMethod.forEach(method -> {
//与调用的方法参数要一致
ReflectionUtil.executeMethodNoResult(aspectBean, method, joinPoint);
});
Object result = methodInvocation.proceed();
afterMethod.forEach(method -> {
ReflectionUtil.executeMethodNoResult(aspectBean, method, result, joinPoint);
});
return result;
}
}
切面的具体实现完成了,剩下的任务就是完成AOP的代理和融入bean的实现流程中。
AOP定义
Spring AOP执行的本质是动态代理,将原对象封装为一个代理对象,在代理对象调用目标方法时,会回调到定义动态代理时设置的invoke方法中,invoke方法中的内容我们自己可以进行设置,在真正语句执行的前后调用 @Before和 @After注解定义的方法,就形成了AOP。Spring AOP中使用的动态代理也分为两种,一种为JDK动态代理,一种为cglib动态代理,JDK动态代理在实现时需要引用目标类的接口,通过接口来实现代理类,而cglib动态代理是以继承目标对象为基础,实现代理类,由于是继承,所以会有一定的限制,比如private final等定义的方法无法进行aop等。所以根据两种动态代理的特性,根据目标类是否实现了接口,需要选用不同的动态代理方式,这里就以JDK动态代理为路线,cglib动态代理只用照样画葫芦就行。
在Spring的生命周期里,AOP在BeanPostProcessor的后置初始化postProcessAfterInitialization中实现,所以我们也定义一个postProcessAfterInitialization用来处理切面匹配信息。
public interface BeanPostProcessor {
/**
* 初始化的后置处理器,在这个地方执行了aop代理
*
* @param bean 执行初始化的目标bean
* @return 后置处理后的对象
*/
Object postProcessAfterInitialization(Object bean);
}
定义抽象类,实现JDK动态代理和CGLIB动态代理共有的初始化方法,封装方法再根据不同动态代理的特性进行区分。在初始化过程中,系统通过 @Aspect注解找到所有有标注该注解的切面,遍历切面,为切面生成对应的拦截器,通过supports方法进行切入点表达式与目标对象路径的匹配,如果匹配上,代表目标对象是在代理范围内,为该对象进行生成代理,如果不符合,则返回原对象。
public abstract class AbstractAopProxyBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessAfterInitialization(Object bean) {
Object proxyBean = bean;
//找到@Aspect注解所标注的切面
Set<Object> aspectBeans = BeanFactory.getBeansByName(Aspect.class.getName());
//遍历AspectBeans的列表,并判断目标bean是否符合该aspect
for (Object aspectBean : aspectBeans) {
//aop拦截器
InternallyAspectInterceptor interceptor = new InternallyAspectInterceptor(aspectBean);
//判断该拦截器的切入点是否支持目标对象
if (interceptor.supports(bean)){
//进行代理封装
proxyBean = wrapperBean(bean, interceptor);
}
}
return proxyBean;
}
/**
* 封装/代理 bean
*
* @param target 目标bean
* @param interceptor 拦截器
* @return 代理bean
*/
public abstract Object wrapperBean(Object target, Interceptor interceptor);
}
具体的代理过程wrapperBean方法jdk动态代理和cglib动态实现不同,以jdk动态代理为例。调用到JdkInvocationHandler.wrap进行生成代理。
public class JdkAopProxyBeanPostProcessor extends AbstractAopProxyBeanPostProcessor {
@Override
public Object wrapperBean(Object target, Interceptor interceptor) {
return JdkInvocationHandler.wrap(target, interceptor);
}
}
JdkInvocationHandler是真正的代理类,它实现了InvocationHandler,在wrap方法中生成代理对象,进行返回,在代理对象调用方法时,会回调用设置的 invoke() 方法中,invoke方法就是对目标对象,方法以及参数进行了一个封装,后就交给了上述定义的intercepor进行实现调用。
public class JdkInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
private final Object target;
/**
* 执行的拦截器
*/
private final Interceptor interceptor;
private JdkInvocationHandler(Object target, Interceptor interceptor){
this.target = target;
this.interceptor = interceptor;
}
public static Object wrap(Object target, Interceptor interceptor){
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(target, interceptor);
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces()
, jdkInvocationHandler);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
MethodInvocation methodInvocation = new MethodInvocation(target, method, args);
return interceptor.intercept(methodInvocation);
}
}
整合实现
以上就将生成代理,拦截器实现aop给定义完成了。当前是要将以上但BeanPostProcessor给融入生命周期中。为此,我们也定义了一个简单工厂,通过执行aop的对象是否具有接口,来返回不同的动态代理,还记得吗,有接口的使用返回JDK动态代理,没有接口实现的使用CGLIB动态代理。
public class BeanPostProcessorFactory {
/**
* 根据bean是否具有接口来选择对应的代理生成器
*
* @param bean 要进行代理的目标bean
* @return 对应类型的代理类生成器
*/
public static BeanPostProcessor getBeanPostProcessor(Object bean){
if (bean.getClass().getInterfaces().length> 0){
return new JdkAopProxyBeanPostProcessor();
} else {
return new CglibAopProxyBeanPostProcessor();
}
}
}
设置getProxyBean方法,优先去二级缓存中取对象,如果二级对象中取到对象了,代表已经完成过代理这个过程了,这里的对象不一定是代理对象,只是代表经历了生成代理这个过程。如果没有经历过代理,就根据bean是否有接口去获取对应的处理器,进行代理的封装。将返回的对象存入到二级缓存中。
/**
* 获取代理bean
*
* @param beanName 目标bean的名称
* @param bean 目标bean
* @return 获取bean 或者生成代理bean
*/
private static Object getProxyBean(String beanName, Object bean){
//判断二级缓存中是否存在对象,如果存在代表该对象已经校验过是否生成代理,直接返回对象
if (!Objects.isNull(EARLY_BEANS.get(beanName))){
return bean;
}
//判断是否可以生成代理对象 这里的bean一定是三级缓存中没有判断代理的bean 如果满足aop却不是代理则生成代理返回
BeanPostProcessor beanPostProcessor = BeanPostProcessorFactory.getBeanPostProcessor(bean);
bean = beanPostProcessor.postProcessAfterInitialization(bean);
//将生成的bean存到二级缓存中,如果需要代理则存入的是代理bean, 如果不需要,则是正常bean
EARLY_BEANS.put(beanName, bean);
return bean;
}
这就将三级缓存实例化对象补全了,如果发生了循环依赖,会对依赖对象优先进行代理,确保注入的对象是代理类,被注入的代理类和后续补全属性的目标类,本质上的引用是同一个。在二次进行代理判断时,可以在二级缓存中取到代理对象,从而直接返回代理对象,再存到一级缓存中,删除之前的缓存。完成整个步骤!
/**
* 从三级缓存中获取类的实例
*
* @param beanName 定义的beanName,可以是Component上的标记,也可以是@Autowired,Qualifier上的别名
* @param aClass 实例化的类
*/
public static Object buildBeans(String beanName, Class<?> aClass){
if (aClass.isInterface()){
throw new NotFountTargetBeanException("该依赖没有找到目标类:" + beanName);
}
//先从一级缓存中获取bean
Object bean = BEANS.get(beanName);
if (bean != null){
return bean;
}
//再从二三级缓存中获取bean 解决循环依赖
if ( (bean = EARLY_BEANS.get(beanName)) == null && (bean = BASIC_OBJECTS.get(beanName)) == null){
bean = ReflectionUtil.newInstance(aClass);
//
BASIC_OBJECTS.put(beanName, bean);
//将bean的名称存在正在创建的列表中
CURRENT_IN_CREATION.add(beanName);
}else if (CURRENT_IN_CREATION.contains(beanName)){
//循环依赖 这时候进入的不是二级缓存中获取的就是三级缓存中获取的对象
return getProxyBean(beanName, bean);
}
//如果在二三级缓存中存在 却没有在加载流程中,则继续完成加载。
//进行依赖注入
DependencyInjection.injectionDependency(bean);
//进行aop 判断是否需要aop 判断二级缓存中是否已经有代理对象
bean = getProxyBean(beanName, bean);
//存入一级缓存,删除二三级缓存
BEANS.put(beanName, bean);
EARLY_BEANS.remove(beanName);
BASIC_OBJECTS.remove(beanName);
CURRENT_IN_CREATION.remove(beanName);
return bean;
}
测试
另外如果在测试的过程中,出现自动打印的情况,可以参照https://blog.csdn.net/qq_41762594/article/details/115346764,关闭Idea的"预知"功能。