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

前言

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

为什么要实现AOP

AOP全名Aspect-Oriented Programming,中文直译为面向切面(方面)编程。何为切面,就比如说我们系统中的权限管理,日志,事务等我们都可以将其看成一个个切面。
在传统的OOP编程中,一些与业务无关的,日志,安全,事务,性能等代码与业务代码牢牢交织在一起。如下图所示:有订单管理,用户管理,订单管理等业务模块。每个模块都实现了自己的日志,安全,事务,性能统计。
使用AOP之前
如果我们修改了业务代码就需要修改相应的日志代码。
使用了AOP之后我们可以将日志,安全,事务,性能统计等代码作为一个切面。切入到业务代码中。
使用AOP之后

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动态代理实际上是应用于类的代理。如图所示:
    这里写图片描述
    这里写图片描述
  2. Java 动态代理
    Java 动态代理是应用于接口的代理。
    待处理的目标对象

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

1.实现Pointcut和MethodMatcher
XML中的配置:

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

这里写图片描述

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));
		}
		
	}
  1. 实现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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农飞哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值