从零开始造Spring07---AOP(介绍以及实现Pointcut和MethodLocatingFactory)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本文是学习《从零开始造Spring》的学习笔记。

前言


本文是学习《从零开始造Spring》的学习笔记。


为什么要实现AOP

AOP全名Aspect-Oriented Programming,中文直译为面向切面(方面)编程。何为切面,就比如说我们系统中的权限管理,日志,事务等我们都可以将其看成一个个切面。

在传统的OOP编程中,一些与业务无关的,日志,安全,事务,性能等代码与业务代码牢牢交织在一起。如下图所示:有订单管理,用户管理,订单管理等业务模块。每个模块都实现了自己的日志,安全,事务,性能统计。

dddaa93990841626e6fdde2d085ba73f_70.png

如果我们修改了业务代码就需要修改相应的日志代码。

使用了AOP之后我们可以将日志,安全,事务,性能统计等代码作为一个切面。切入到业务代码中。

786f6721cec3a8aae7bd9f3b358c2138_70.png


AOP的基本概念

Joint Point (连接点)

被拦截到的点,在Spring AOP中通常是方法类型(a point during the execution of a program, such as the execution of a method or the handling of an exception)

Pointcut(切入点)

对连接点进行拦截的定义,在程序中主要体现为书写的切点表达式

(a predicate that matches join points)

例如:execution(* org.litespring.service.v5.*.placeOrder(…))

Advice(通知)

AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around等通知

(action taken at a particular join point

Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.

Before advice(前置通知)

在连接点之前的通知(Advice that executes before a join point)

After returning advice(后置通知)

在连接点之后的通知,没有抛出异常(Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception)

After throwing advice(异常通知)

如果方法抛出异常的通知(Advice to be executed if a method exits by throwing an exception)

After (finally) advice (finally 通知)

不过连接点的方法是否正常或者抛出异常,都会执行的通知

(Advice to be executed regardless of the means by which a join point exits (normal or exceptional return) )

Around advice (环绕通知)

在连接点之前或者之后都可以执行的通知。可以全局的在方法前后自定义行为。

(Advice that surrounds a join point such as a method invocation, Around advice can perform custom behavior before and after the method invocation)

详情参见:spring-framework源码第4弹------Spring AOP的简单实现(学习tiny-spring)


AOP的简单应用

详情参见:Spring 学习二-----AOP的原理与简单实践


怎么实现AOP

AOP是在运行期通过动态代理生成代理类的方式对方法进行增强的。

首先我们在petstore-v5.xml 中增加如下配置


<bean id="tx" class="com.jay.spring.tx.TransactionManager"/>
   <aop:config>
      <aop:aspect ref="tx">
         <aop:pointcut id="placeOrder" expression="execution(* com.jay.spring.service.v5.*.placeOrder(..))"/>
         <aop:before pointcut-ref="placeOrder" method="start"/>
         <aop:after-returning  pointcut-ref="placeOrder" method="commit"/>
         <aop:after-throwing pointcut-ref="placeOrder" method="rollback"/>
      </aop:aspect>
   </aop:config>


测试代码


ApplicationContext ctx = new ClassPathXmlApplicationContext("petstore-v5.xml");
PetStoreService petStore = (PetStoreService)ctx.getBean("petStore");


1.CGLib动态代理

在运行时动态生成一个类,继承待增强的类,在placeOrder中添加需要增强的代码,CGLib动态代理实际上是应用于类的代理。如图所示:

0a106db5e3f06034916234e3b0cba821_70.png


2.Java 动态代理

Java 动态代理是应用于接口的代理。

9aba687a34b85204a0bf2d5ffc87cef1_70.png

---------------------------------------完美的分割-----------------------------------------


1.实现Pointcut和MethodMatcher

XML中的配置:

<aop:pointcut id="placeOrder"
    expression="execution(* org.litespring.service.v5.*.placeOrder(..))" />

9799a2cd5a1088d696d1b181b03e33fd_70.png


PointCut 接口依赖于MethodMatcher 接口,MethodMatcher 接口主要是通过给定的类的方法,判断该方法是否符合pointcut的表达式。


关键代码


public void setExpression(String expression){
  this.expression = expression;
  }
  public boolean matches(Method method/*, Class<?> targetClass*/) {
  checkReadyToMatch();
  ShadowMatch shadowMatch = getShadowMatch(method);
  if (shadowMatch.alwaysMatches()) {
    return true;
  }
  return false;
  }
  private ShadowMatch getShadowMatch(Method method) {
  ShadowMatch shadowMatch = null;
  try {
    shadowMatch = this.pointcutExpression.matchesMethodExecution(method);
  }
  catch (ReflectionWorldException ex) {
    throw new RuntimeException("not implemented yet");
    /*try {
    fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
    if (fallbackExpression != null) {
      shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
    }
    }
    catch (ReflectionWorldException ex2) {
    fallbackExpression = null;
    }*/
  }
  return shadowMatch;
  }
  private void checkReadyToMatch() {
  if (getExpression() == null) {
    throw new IllegalStateException("Must set property 'expression' before attempting to match");
  }
  if (this.pointcutExpression == null) {    
    this.pointcutClassLoader = ClassUtils.getDefaultClassLoader();
    this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
  }
  }
  private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
  PointcutParser parser = PointcutParser
    .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
      SUPPORTED_PRIMITIVES, classLoader);
  /*PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
  for (int i = 0; i < pointcutParameters.length; i++) {
    pointcutParameters[i] = parser.createPointcutParameter(
      this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
  }*/
  return parser.parsePointcutExpression(replaceBooleanOperators(getExpression()),
    null, new PointcutParameter[0]);
  }


代码简单解释:

根据给定的expression字符串表达式,生成一个PointcutExpression 对象,然后,根据这个对象去匹配传入的方法。然后将匹配的结果放入ShadowMatch 对象中。如果匹配成功则返回shadowMatch.alwaysMatches() 为true, 否则为false。

测试代码:

@Test
  public void testPointcut() throws Exception{
  String expression = "execution(* org.litespring.service.v5.*.placeOrder(..))";
  AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
  pc.setExpression(expression);
  MethodMatcher mm = pc.getMethodMatcher();
  {
    Class<?> targetClass = PetStoreService.class;
    Method method1 = targetClass.getMethod("placeOrder");  
    Assert.assertTrue(mm.matches(method1));
    Method method2 = targetClass.getMethod("getAccountDao");  
    Assert.assertFalse(mm.matches(method2));
  }
  {
    Class<?> targetClass = org.litespring.service.v4.PetStoreService.class;   
    Method method = targetClass.getMethod("getAccountDao");  
    Assert.assertFalse(mm.matches(method));
  }
  }


实现MethodLocatingFactory

<bean id="tx" class="com.jay.spring.tx.TransactionManager"/>
   <aop:config>
      <aop:aspect ref="tx">
         <aop:pointcut id="placeOrder" expression="execution(* com.jay.spring.service.v5.*.placeOrder(..))"/>
         <aop:before pointcut-ref="placeOrder" method="start"/>
         <aop:after-returning  pointcut-ref="placeOrder" method="commit"/>
         <aop:after-throwing pointcut-ref="placeOrder" method="rollback"/>
      </aop:aspect>
   </aop:config>


MethodLocatingFactory 类的主要作用是通过Bean的名称(“tx”)和方法名(“start”)定位到这个Method,然后通过反射调用!

关键代码:

public void setBeanFactory(BeanFactory beanFactory) {
        if (!StringUtils.hasText(this.targetBeanName)) {
            throw new IllegalArgumentException("Property 'targetBeanName' is required");
        }
        if (!StringUtils.hasText(this.methodName)) {
            throw new IllegalArgumentException("Property 'methodName' is required");
        }
        Class<?> beanClass = beanFactory.getType(this.targetBeanName);
        if (beanClass == null) {
            throw new IllegalArgumentException("Can't determine type of bean with name '" + this.targetBeanName + "'");
        }
        this.method = BeanUtils.resolveSignature(this.methodName, beanClass);
        if (this.method == null) {
            throw new IllegalArgumentException("Unable to locate method [" + this.methodName +
                    "] on bean [" + this.targetBeanName + "]");
        }
    }
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        BeanDefinition bd = this.getBeanDefinition(name);
        if (bd == null) {
            throw new NoSuchBeanDefinitionException(name);
        }
        resolveBeanClass(bd);
        return bd.getBeanClass();
    }
public void resolveBeanClass(BeanDefinition bd) {
        if (bd.hasBeanClass()) {
            return;
        } else {
            try {
                bd.resolveBeanClass(this.getBeanClassLoader());
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("can't load class:"+bd.getBeanClassName());
            }
        }
    }


源码下载:

https://github.com/XWxiaowei/spring-learn/tree/testcase-v6-aop-1/liu-spring-demo


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
Java Spring
Spring AOP切点表达式(Pointcut)详解
Spring 的 AOP 中的一个核心概念是切点(Pointcut),切点表达式定义通知(Advice)执行的范围。
4353 0
|
缓存 Java Spring
【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)(上)
【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)(上)
【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)(上)
|
XML Java 数据格式
【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)(中)
【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)(中)
|
XML Java 数据格式
AOP:组合使用多个切入点表达式(Combining pointcut expressions)
AOP:组合使用多个切入点表达式(Combining pointcut expressions)
1284 0
|
Java
SpringBoot AOP @Pointcut切入点execution表达式排除类中的具体方法
SpringBoot AOP @Pointcut切入点execution表达式排除类中的具体方法
462 0
|
XML 监控 Java
Spring AOP】@Aspect结合案例详解(一): @Pointcut使用@annotation + 五种通知Advice注解(已附源码)
在微服务流行的当下,在使用SpringCloud/Springboot框架开发中,AOP使用的非常广泛,尤其是@Aspect注解方式当属最流行的,不止功能强大,性能也很优秀,还很舒心!所以本系列就结合案例详细介绍@Aspect方式的切面的各种用法,力求覆盖日常开发中的各种场景。本文带来的案例是:打印Log,主要介绍@Pointcut切点表达式的@annotation方式,以及 五种通知Advice注解:@Before、@After、@AfterRunning、@AfterThrowing、@Around。
|
Java 程序员 Spring
如何理解AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?
a. 连接点(Joinpoint):程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。
1902 0
|
缓存 Java Spring
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(上)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(上)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(上)
|
Java Spring 容器
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(下)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(下)
|
安全 Java Spring
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(中)
【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】(中)