• 基于Advisor实现AOP


    Spring aop使用非常广泛就不说了。除了常见的使用@Aspect注解作为切面配合自定义注解作为切点实现AOP拦截外,还可以使用本文介绍的Advisor实现AOP。

    本文介绍基于Spring-aop依赖包下的Advisor接口实现AOP的方式。首先概述一下,这种实现方式主要是创建Advisor接口实例,并指定Advice和Pointcut,其中Advice接口实例扮演Advice通知的角色,Pointcut接口实例扮演切入点的角色,然后把Advisor实例注入到Spring中就可以实现AOP了。

    PointcutAdvisor接口

    PointcutAdvisor接口是Advisor的子接口,Advisor是Spring AOP的顶层抽象,用来管理Advice和Pointcut,所以毫无疑问PointcutAdvisor接口也是用来管理Advice和Pointcut的。

    Pointcut接口

    Pointcut接口有两个接口方法,分别用于加载ClassFilter和MethodMatcher接口实例,并通过这两个实例实现切入点的逻辑功能。

    1. public interface Pointcut {
    2. Pointcut TRUE = TruePointcut.INSTANCE;
    3. //加载ClassFilter实例
    4. ClassFilter getClassFilter();
    5. //加载MethodMatcher实例
    6. MethodMatcher getMethodMatcher();
    7. }

     ClassFilter接口

    ClassFilter的matches方法定义判断某个类是否需要被纳入切面

    1. public interface ClassFilter {
    2. boolean matches(Class clazz);
    3. ClassFilter TRUE = TrueClassFilter.INSTANCE;
    4. }

    MethodMatcher接口 

     MethodMatcher的matches方法则是定义判断某个方法是否需要被纳入切面。

    1. public interface MethodMatcher {
    2. boolean isRuntime();
    3. //静态检查给定方法是否匹配
    4. boolean matches(Method method, Class targetClass);
    5. //检查此方法是否存在运行时(动态)匹配
    6. boolean matches(Method method, Class targetClass, Object... args);
    7. MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
    8. }

    MethodInterceptor接口

    这个接口spring-aop依赖下org.aopalliance.intercept.Interceptor接口的子接口,而Interceptor接口又是Advice接口的子接口,所以MethodInterceptor接口也是Advice接口的子接口。(有点啰嗦)
    MethodInterceptor接口主要提供一个invoke方法,用于定义通知逻辑,即对切点执行的逻辑代码。

    1. public interface MethodInterceptor extends Interceptor {
    2. Object invoke(MethodInvocation invocation) throws Throwable;
    3. }

    上面把各个接口都介绍完了。现在整理一下流程做一下总结:

    1. 使用PointcutAdvisor接口实例(切面)把Pointcut接口实例(切点)和Advice接口实例(通知逻辑)整合起来并注入到Spring中,这样就能项目启动过程中实现AOP。
    2. 项目启动时会调用PointcutAdvisor接口的getPointcut和getAdvice方法,得到预先定义的切面逻辑实例(Pointcut接口实例)和织入方法逻辑实例(MethodInterceptor接口实例),得到实例后立即执行Pointcut接口实例中ClassFilter和MethodMatcher的match方法判断那些类或方法需要被纳入切面范围,实现动态构建切点信息。
    3. MethodInterceptor接口(Advice的子接口)的invoke方法则是通知逻辑,只需要被PointcutAdvisor接口加载即可,通知逻辑会在代码运行过程中对切点拦截后执行处理。

    上面第2点流程代码体现在org.springframework.aop.support.AopUtils#canApply 方法中

    1. public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) {
    2. Assert.notNull(pc, "Pointcut must not be null");
    3. if (!pc.getClassFilter().matches(targetClass)) {
    4. return false;
    5. }
    6. MethodMatcher methodMatcher = pc.getMethodMatcher();
    7. if (methodMatcher == MethodMatcher.TRUE) {
    8. // No need to iterate the methods if we're matching any method anyway...
    9. return true;
    10. }
    11. IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    12. if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
    13. introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    14. }
    15. Set> classes = new LinkedHashSet>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    16. classes.add(targetClass);
    17. for (Class clazz : classes) {
    18. Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
    19. for (Method method : methods) {
    20. if ((introductionAwareMethodMatcher != null &&
    21. introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
    22. methodMatcher.matches(method, targetClass)) {
    23. return true;
    24. }
    25. }
    26. }
    27. return false;
    28. }

    项目中集成基于Advisor实现AOP功能

    方式一:使用aspectj execution(切点) +自定义MethodInterceptor通知逻辑整合为默认的切面织入(DefaultPointcutAdvisor)

    Spring中给PointcutAdvisor接口提供一个一个默认的实现类DefaultPointcutAdvisor和Pointcut接口默认实现类AspectJExpressionPointcut,如果不想自己在定义切面和切点实现类可以直接使用默认实现。

    1. @Configuration
    2. public class PointcutAdvisorConfig {
    3. public static final String traceExecution = "execution(* cn.demo.service..*.*(..))";
    4. @Bean
    5. public DefaultPointcutAdvisor defaultPointcutAdvisor1() {
    6. //使用默认的切面advisor实例
    7. DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
    8. //使用默认的切点实例
    9. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    10. pointcut.setExpression(traceExecution);
    11. advisor.setPointcut(pointcut);
    12. //配置自定义的通知实例
    13. MyInterceptor interceptor = new MyInterceptor();
    14. advisor.setAdvice(interceptor);
    15. return advisor;
    16. }
    17. }
    18. //只需额外自定义通知实现类即可
    19. public class MyInterceptor implements MethodInterceptor {
    20. @Override
    21. public Object invoke(MethodInvocation invocation) throws Throwable {
    22. System.out.println(invocation.getMethod() + "==方法执行前==");
    23. Object proceed = invocation.proceed();
    24. System.out.println(invocation.getArguments() + "--方法执行后--");
    25. return proceed;
    26. }
    27. }

    方式二:使用自定义切点Pointcut+自定义MethodInterceptor通知逻辑整合默认的切面织入(DefaultPointcutAdvisor)或自定义PointcutAdvisor切面织入实例

    1. //定义切面类
    2. public class MapperMethodAdviceBean implements PointcutAdvisor, EnvironmentAware, ApplicationContextAware {
    3. private Environment environment;
    4. private ApplicationContext applicationContext;
    5. public MapperMethodAdviceBean() {
    6. }
    7. public void setEnvironment(Environment environment) {
    8. this.environment = environment;
    9. }
    10. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    11. this.applicationContext = applicationContext;
    12. }
    13. public Pointcut getPointcut() {
    14. return new MapperMethodPointCut();
    15. }
    16. public Advice getAdvice() {
    17. return new MapperMethodAdvise(this.environment, this.applicationContext);
    18. }
    19. public boolean isPerInstance() {
    20. return true;
    21. }
    22. }
    23. //定义切点类
    24. public class MapperMethodPointCut implements Pointcut {
    25. public MapperMethodPointCut() {
    26. }
    27. public ClassFilter getClassFilter() {
    28. return new MyClassFilter();
    29. }
    30. public MethodMatcher getMethodMatcher() {
    31. return new MyMethodMatcher();
    32. }
    33. public boolean isRuntime() {
    34. return false;
    35. }
    36. //定义ClassFilter接口实例类并定义切点判断逻辑
    37. class MyClassFilter implements ClassFilter{
    38. @Override
    39. public boolean matches(Class clazz) {
    40. // 如果mapper包下的包含Mapper注解就返回true 作为切点
    41. if (clazz != null && StringUtils.contains(clazz.getName(), "cn.demo.mapper")) {
    42. return aClass.getSuperclass().getName().equals(Proxy.class.getName()) && aClass.getInterfaces()[0].isAnnotationPresent(Mapper.class);
    43. }
    44. return false;
    45. }
    46. }
    47. //定义MethodMatcher接口实例类并定义切点判断逻辑
    48. class MyMethodMatcher implements MethodMatcher{
    49. //判断方法是否匹配
    50. @Override
    51. public boolean matches(Method method, Class targetClass, Object... args) {
    52. return true;
    53. }
    54. //判断方法是否匹配
    55. @Override
    56. public boolean matches(Method method, Class targetClass) {
    57. return true;
    58. }
    59. @Override
    60. public boolean isRuntime() {
    61. return false;
    62. }
    63. }
    64. }
    65. //定义通知逻辑
    66. public class MapperMethodAdvise implements MethodInterceptor {
    67. private Environment environment;
    68. private ApplicationContext applicationContext;
    69. public MapperMethodAdvise(Environment environment, ApplicationContext applicationContext) {
    70. this.environment = environment;
    71. this.applicationContext = applicationContext;
    72. }
    73. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    74. return methodInvocation.proceed();
    75. }
    76. }

     

    如果切面织入即PointcutAdvisor接口类也自己定义,则需要借助BeanDefinitionRegistryPostProcessor扩展接口把切面实例注入到Spring中才生效。

    1. //定义BeanDefinitionRegistryPostProcessor注册PointcutAdvisor实例的BD
    2. public class MapperMethodPropertyProcessor implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {
    3. public MapperMethodPropertyProcessor() {
    4. }
    5. public int getOrder() {
    6. return 0;
    7. }
    8. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
    9. BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperMethodAdviceBean.class).getBeanDefinition();
    10. beanDefinitionRegistry.registerBeanDefinition(MapperMethodAdviceBean.class.getName(), beanDefinition);
    11. }
    12. public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    13. }
    14. }
    15. //配置自定义的BeanDefinitionRegistryPostProcessor注册到Spring容器
    16. @Configuration
    17. public class MapperMethodProxyAutoConfiguration {
    18. public MapperMethodProxyAutoConfiguration() {
    19. }
    20. @Bean
    21. public MapperMethodPropertyProcessor mapperMethodPropertyProcessor() {
    22. return new MapperMethodPropertyProcessor();
    23. }
    24. }

  • 相关阅读:
    Nacos入门
    程序员的数学课15 递归:如何计算汉诺塔问题的移动步数?
    通过SSH远程登录华为设备
    PyQt5 QDialog对话框(QMessageBox,QInputDialog,QFontDialog,QFileDialog,QColorDialog)
    Java并发编程的艺术笔记-Java并发编程基础
    pycharm2020无法打开,点击无反应
    C++-Mongoose(1)-http-server
    化工行业供应商协同管理系统:助力企业打造良好营商环境,提升运营效率
    浅谈JVM(面试常考)
    codeforces每日5题(均1700)-第六天
  • 原文地址:https://blog.csdn.net/qq_29569183/article/details/126441749