前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring AOP 生产落地方式有哪些? 面向切面编程

Spring AOP 生产落地方式有哪些? 面向切面编程

作者头像
用户7741497
发布2022-03-24 13:47:44
2650
发布2022-03-24 13:47:44
举报
文章被收录于专栏:hml_知识记录

本文主要讲解SpringAOP的实现方式有哪些, 业务代码场景来自于上一篇文章 : 《基于 Spring AOP 实现的 通用实验组件 AB实验/AB测试》, 对该业务感兴趣的同学可以看看哈.

使用 @Aspect 注解(或者XML配置)

代码语言:javascript
复制
/**
 * 实验能力切面
 */
@Slf4j
@Aspect
@Component
public class ExperimentAspect {
    /**
     * 定义切点
     */
    @Pointcut("@within(com.eden.springbootwebdemo.web.experiment.Experimentable)")
    public void pointCut() {
    }

    /**
     * @param proceedingJoinPoint 被织入的目标
     * @return 方法执行结果
     */
    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        return ExperimentInterceptor.experimentInvoke(proceedingJoinPoint.proceed(), proceedingJoinPoint.getTarget(), proceedingJoinPoint.getSignature().getName());
    }

}

实现 MethodInterceptor 接口

为什么可以通过这种方式实现AOP ? 如果你也有这样的问题, 挺好的, 一起学习. 这个涉及到 Spring Bean 加载过程, 流程比较复杂, 后面会做专题分享

代码语言:javascript
复制
@Slf4j
public class ExperimentInterceptor implements MethodInterceptor {

    private static final ExperimentParamMetedata nonExperimentableMetedata = new ExperimentParamMetedata();


    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        return ExperimentInterceptor.experimentInvoke(methodInvocation.proceed(), methodInvocation.getThis(), methodInvocation.getMethod().getName());
    }

    public static Object experimentInvoke(Object originalValue, Object target, String targetMethodName) {
        ExperimentParamMetedata experimentParamMetedata;
        try {
            experimentParamMetedata = ExperimentInterceptor.inExperiment(target, targetMethodName, ExperimentSettingDemo.EXPERIMENT_SETTINGMAP);
        } catch (RuntimeException exception) {
            log.error("ExperimentAspect-未知异常", exception);
            return originalValue;
        }
        if (experimentParamMetedata.isExperimentable()) {
            try {
                return queryExperimentValue(experimentParamMetedata.getPropertyName(), experimentParamMetedata.getPropertyTypeClass());
            } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
                log.error("ExperimentAspect-结果解析异常", e);
                throw new RuntimeException("ExperimentAspect-结果解析异常", e);
            }
        }
        return originalValue;
    }


    /**
     * 判断当前目标方法是不是需要进行实验
     *
     * @param experimentSettingMap 实验配置
     * @return 方法可实验性校验结果
     */
    private static ExperimentParamMetedata inExperiment(Object target, String targetMethodName, Map<String, List<String>> experimentSettingMap) {
        Class<?> targetClass = AopUtils.isAopProxy(target) ? AopUtils.getTargetClass(target) : target.getClass();
        // 是否在实验中
        NonExperimentable nonExperimentable = AnnotationUtils.findAnnotation(targetClass, NonExperimentable.class);
        if (null != nonExperimentable) {
            return nonExperimentableMetedata;
        }
        // 实验配置是否有数据
        if (null == experimentSettingMap || experimentSettingMap.isEmpty()) {
            return nonExperimentableMetedata;
        }
        BeanInfo targetBeanInfo;
        try {
            targetBeanInfo = Introspector.getBeanInfo(target.getClass());
        } catch (IntrospectionException e) {
            throw new RuntimeException(e);
        }
        Optional<PropertyDescriptor> propertyDescriptorOptional = Arrays.stream(targetBeanInfo.getPropertyDescriptors())
                .filter(item -> item.getReadMethod().getName().equals(targetMethodName)).findFirst();
        if (propertyDescriptorOptional.isPresent()) {
            PropertyDescriptor propertyDescriptor = propertyDescriptorOptional.get();
            String propertyName = propertyDescriptor.getName();
            Field propertyField = ReflectionUtils.findField(targetClass, propertyName);
            if (propertyField != null) {
                Value valueAnnotation = propertyField.getDeclaredAnnotation(Value.class);
                if (null != valueAnnotation && ExperimentSettingDemo.EXPERIMENT_PROPERTY_NAME.contains(propertyName)) {
                    ExperimentParamMetedata experimentParamMetedata = new ExperimentParamMetedata();
                    experimentParamMetedata.setExperimentable(true);
                    experimentParamMetedata.setPropertyTypeClass(propertyDescriptor.getPropertyType());
                    experimentParamMetedata.setPropertyName(propertyName);
                    return experimentParamMetedata;
                }
            }
        }
        return nonExperimentableMetedata;
    }

    /**
     * 查询属性对应的实验值
     *
     * @param experimentPropertyName 实验属性名称
     * @param propertyTypeClass      属性类型
     * @return 实验值
     */
    public static Object queryExperimentValue(String experimentPropertyName, Class<?> propertyTypeClass) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        List<String> experimentReturnStringValues = ExperimentSettingDemo.EXPERIMENT_SETTINGMAP.get(experimentPropertyName);
        // 几个配置随机选一个返回
        int index = RandomUtils.nextInt(0, experimentReturnStringValues.size());
        return StringCastUtil.cast(experimentReturnStringValues.get(index), propertyTypeClass);
    }
}

扫包的实现方式有哪些

AspectJExpressionPointcut \ AnnotationMatchingPointcut \ JdkRegexpMethodPointcut

代码语言:javascript
复制
@Configuration
public class ExperimentInterceptorConfig {

    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
        ExperimentInterceptor interceptor = new ExperimentInterceptor();
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* com.eden.springbootwebdemo.service.*.*(..))");
        // AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(HfiTrace.class, true);
        // JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
        // pointcut.setPatterns("com.eden.springbootwebdemo.service.*");
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(interceptor);
        return advisor;
    }
}

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用 @Aspect 注解(或者XML配置)
  • 实现 MethodInterceptor 接口
  • 扫包的实现方式有哪些
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档