Spring探索(六):AOP二世

1.@AspectJ形式的Spring AOP
引入了AspectJ的Pointcut描述语言,直接使用POJO来定义Aspect以及相关的Advice。
Spring AOP使用AspectJ的类库进行Pointcut的解析和匹配,但最终的实现机制还是Spring AOP的架构,即使用代理模式处理横切逻辑的织入。

@AspectJ形式的Pointcut声明包含两部分:Pointcut Expression和Pointcut Signature。AspectJ的Pointcut表达式支持通过&&,||,!逻辑运算符,进行Pointcut表达式之间的逻辑运算。运算符可以应用于具体的Pointcut表达式以及相应的Pointcut Signature。

 @Aspect
 public class MyAspect{
	@Pointcut("execution(void method1())*") //Pointcut Expression
	public void method1Execution(){} //pointcut signature

	@Pointcut("execution(void method2())*")
	public void method2Execution(){}

	@Pointcut("execution(void method1()) || execution(void method2())")
	public void bothMethodExec(){}

	@Pointcut("method1Execution() || method2Execution()")
	public void bothMethodExec2(){}
}

2.@AspectJ形式Pointcut表达式的标识符
因为Spring AOP只支持方法级别的Joinpoint,所以只能使用AspectJ的Pointcut表述语言的少数几种标识符。

public class AspectJExpressionPointcut extends AbstractExpressionPointcut
		implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

	private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();

	static {
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
	}
.....
}

以@开头的标识符,都只能指定注解类型参数。

3.@AspectJ形式的Pointcut在Spring AOP中的真实面目
实际上,@AspectJ形式声明的所有Pointcut表达式,在Spring AOP内部都会通过解析,转换为具体的Pointcut对象。因为Spring AOP有自己的Pointcut定义结构,所以,AspectJ形式声明的这些Pointcut表达式,最终会转化为一个专门面向AspectJ的Pointcut实现。
在这里插入图片描述

org.springframework.aop.aspectj.annotation.AspectJProxyFactory
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
这两个类会通过反射获取Aspect中的@Pointcut定义的AspectJ形式的Pointcut定义后,在Spring AOP框架内部构造一个对应的AspectJExpressionPointcut对象,然后AspectJExpressionPointcut会委托AspectJ类库中的PointcutParser来解析Pointcut表达式。

4.@AspectJ形式的Advice
@Before
@AfterReturning
如果需要访问方法的返回值,可以通过returning属性将返回值绑定到Advice定义所在的方法。

 @AfterReturning(pointcut="execution(boolean *.execute(String,..))",returning="relValues")
 public void taskExecutionCompleted(JoinPoint joinPoint,boolean relValues){}

@AfterThrowing
如果要访问具体的异常信息和其他信息,可以按如下定义:

 @AfterThrowing(pointcut="execution(boolean *.execute(String,..))",throwing="e")
 public void afterThrowing(JoinPoint joinPoint,RuntimeExecution e){ }

@After Finally-Advice
不管方法是正常执行返回,还是执行过程中抛出异常而非正常返回,都会触发其上的After Advice执行,,所以,After Advice适合用于释放某些系统资源的场景。

@Around
对于上面的四个Advice的方法定义的第一个参数可以是JoinPoint类型,且是可选的,但对于Around Advice的方法定义来说,它的第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型(extends JoinPoint),且必须指定,通常情况下,需要通过ProceedingJoinPoint的proceed()方法继续调用链路的执行。

以上都是方法级别的注解,只能用于标注方法定义。

@DeclareParents 用于标注Introduction类型的Advice

某些情况下,需要在Advice定义中访问Joinpoint处的方法参数,可以通过org.aspectj.lang.JoinPoint获取。将方法的第一个参数声明为Joinpoint类型,借助于它的getSignature()方法取得当前Joinpoint处的方法签名,getTarget()获取当前目标对象,getArgs()访问相应Joinpoint处方法的参数值。还有一种那个方式也可以,通过args标识符绑定。
注意:除了Around Advice和Introduction不可以使用JoinPoint外,余下的Advice都可以使用。

Introduction

public interface ITask {}

public interface ICounter {
    void resetCounter();
    int getCounter();
}

public class TaskImpl implements ITask {}

public class CounterImpl implements ICounter{
    private int counter;

    @Override
    public void resetCounter() {
        counter = 0;
    }

    @Override
    public int getCounter() {
        counter++;
        return counter;
    }
}

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

/**
 * <br>以AspectJava形式声明Introduction</br>
 */
@Aspect
public class IntroductionAspect {

    /**
     * @DeclareParents所归属的域定义类型是将为目标对象新增加的对象类型
     * value指定将要应用到的目标对象,可以使用通配符指定一批目标对象
     * defaultImpl 指定新增加的接口定义的实现类
     */
    @DeclareParents(value = "com.hong.service.TaskImpl",defaultImpl = CounterImpl.class)
    public ICounter counter;
}
<!--使用AspectJ形式声明Introduction,与上面使用ProxyFactoryBean和DelegatePerTargetObjectIntroductionInterceptor实现相同的效果-->
	<!--开启AspectJ AOP自动代理-->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	<bean id="task" class="com.hong.service.TaskImpl" scope="prototype"/>
	<bean id="counterIntroduction" class="com.hong.aspect.IntroductionAspect"/>
 @Test
    public void test3(){
        Object task1 = container.getBean("task");
        Object task2 = container.getBean("task");
        //不同的目标对象对应不同的ICounter实例
        System.out.println(((ICounter)task1).getCounter());//1
        System.out.println(((ICounter)task1).getCounter());//2
        System.out.println(((ICounter)task2).getCounter());//1
    }

5.Advice的执行顺序
如果多个Advice引用的Pointcut定义恰好匹配同一个JoinPoint的时候, 在这同一个JoinPoint上,这些Advice该按照什么顺序执行?
(1)当这些Advice都声明在同一个Aspect内(使用@Aspect注解声明的POJO类)的时候,他们将按照声明顺序执行.

@Aspect
public class MultiAdviceAspect {
    @Pointcut("execution(* com.hong.service..*.*(..))")
    public void taskExecution(){ }

    @Before("taskExecution()")
    public void beforeOne(){
        System.out.println("before one");
    }

    @Before("taskExecution()")
    public void beforeTwo(){
        System.out.println("before two");
    }

    @AfterReturning("taskExecution()")
    public void afterReturningOne(){
        System.out.println("after returning one");
    }

    @AfterReturning("taskExecution()")
    public void afterReturningTwo(){
        System.out.println("after returning two");
    }
}
<bean id="counter" class="com.hong.service.CounterImpl"/>
<bean class="com.hong.aspect.MultiAdviceAspect"/>
    @Test
    public void test4() {
        ICounter counter = (ICounter) container.getBean("counter");
        counter.getCounter();
        /**
         *   before one
             before two
             1
             after returning one
             after returning two
         */
    }

(2)当这些Advice声明在不同的Aspect内的时候,需要用org.springframework.core.annotation.Order注解或者实现org.springframework.core.Ordered接口,标明执行顺序,否则,Advice的执行顺序是不确定的.

6.Aspect的实例化模式
对于注册到容器的各个Aspect,它们默认的实例化模式是singleton,除此外,Spring AOP还支持perthis,pertarget模式.

7.基于Schema的AOP

<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--基于schema的AOP-->
	<aop:config proxy-target-class="true">
	<!--下面的3个子元素必须按照下面的顺序定义-->
		<aop:pointcut id="" expression=""/>
		<aop:advisor advice-ref=""/>
		<aop:aspect/>
	</aop:config>
</bean>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值