一起来写个SpringBoot[7] — — 实现AOP

项目地址: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的"预知"功能。
在这里插入图片描述

在这里插入图片描述
下一节:一起来写个SpringBoot[8] — — 整合系统-完结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值