《Spring揭秘》第十章——Spring AOP二世

新特性

1. 可以使用POJO声明Aspect和Advice

2. 新的Pointcut表达方式

@AspectJ形式的Spring AOP

示例1:编程方式织入

@Aspect
public class PerformanceTraceAspect {
    private final Log logger = LogFactory.getLog(PerformanceTraceAspect.class);
    
    @Pointcut("execution(public void *.method1()) || execution(public void *.method2())")
    public void pointcutName(){}
    
    @Around("pointcutName()")
    public Object performanceTrace(ProceedingJoinPoint joinPoint) {
        StopWatch watch = new StopWatch();
        try {
            watch.start();
            return joinPoint.proceed();
        } catch (Throwable e) {
            logger.error("", e);
            return null;
        } finally {
            watch.stop();
            if (logger.isInfoEnabled()) {
                logger.info("PT in method["
                        + joinPoint.getSignature().getName() 
                        + "]>>>>>" + watch.toString());
            }
        }
    }
}


public class Foo {
    public void method1() {
        System.out.println("method1 execution.");
    }
    
    public void method2() {
        System.out.println("method2 execution.");
    }
}

public class Main {
    
    public static void main(String[] args) {
        AspectJProxyFactory weaver = new AspectJProxyFactory();
        weaver.setProxyTargetClass(true);
        weaver.setTarget(new Foo());
        weaver.addAspect(PerformanceTraceAspect.class);
        Object proxy = weaver.getProxy();
        ((Foo) proxy).method1();
        ((Foo) proxy).method2();
    }
}

执行结果如下:

method1 execution.
八月 30, 2018 3:21:00 下午 com.zte.mao.PerformanceTraceAspect performanceTrace
信息: PT in method[method1]>>>>>StopWatch '': running time (millis) = 37; [] took 37 = 100%
method2 execution.
八月 30, 2018 3:21:00 下午 com.zte.mao.PerformanceTraceAspect performanceTrace
信息: PT in method[method2]>>>>>StopWatch '': running time (millis) = 0; [] took 0 = 0%

示例2:自动代理织入

<?xml version="1.0" encoding="UTF-8"?>
<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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" >
        <property name="proxyTargetClass" value="true"></property>
    </bean>
    
    <bean id="performanceAspect" class="com.zte.mao.PerformanceTraceAspect"></bean>
    
    <bean id="target" class="com.zte.mao.Foo"></bean>
</beans>
public class Main {
    
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("springContext.xml");
        Object proxy = context.getBean("target");
        ((Foo) proxy).method1();
        ((Foo) proxy).method2();
    }
}

执行结果如下:

八月 30, 2018 3:45:03 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4534b60d: startup date [Thu Aug 30 15:45:03 CST 2018]; root of context hierarchy
八月 30, 2018 3:45:03 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [springContext.xml]
method1 execution.
八月 30, 2018 3:45:04 下午 com.zte.mao.PerformanceTraceAspect performanceTrace
信息: PT in method[method1]>>>>>StopWatch '': running time (millis) = 26; [] took 26 = 100%
method2 execution.
八月 30, 2018 3:45:04 下午 com.zte.mao.PerformanceTraceAspect performanceTrace
信息: PT in method[method2]>>>>>StopWatch '': running time (millis) = 0; [] took 0 = 0%

将springContext.xml中的内容替换为下面内容,也可以实现自动代理织入:

    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    
    <!-- <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" >
        <property name="proxyTargetClass" value="true"></property>
    </bean> -->
    
    <bean id="performanceAspect" class="com.zte.mao.PerformanceTraceAspect"></bean>
    
    <bean id="target" class="com.zte.mao.Foo"></bean>

@AspectJ形式的Pointcut

@Aspect
public class CommonAspect {
    
    @Pointcut("execution(void method1())")  // pointcut_expression
    private void method1Exec() {}           // pointcut_signature
    
    @Pointcut("execution(void method2())")
    private void method2Exec() {}
    
    @Pointcut("execution(void method1()) || execution(void method1())")
    private void bothMethodExec() {}
    
    @Pointcut("method1Exec() || method2Exec()")
    private void bothMethodExec2() {}
}

@AspectJ形式Pointcut表达式的标志符

execution(modifier-pattern? re-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
with(declaring-type-pattern) // 只接受类型声明,匹配指定类型下的所有Jointpoint
this(ObjectType) // this表示目标对象的代理对象,当代理对象的类型是ObjectType时,匹配ObjectType类型的所有Jointpoint
target(ObjectType) // target表示目标对象,当目标对象是ObjectType时,匹配ObjectType类型目标对象内定义的所有Jointpoint
args() // 指定参数类型、指定参数数量的Jointpoint
@within() // 标注了某种类型的注解,静态匹配
@target() // 标注了某种类型的注解,动态匹配
@args() // 检查方法参数的参数类型是否包含某种类型的注解
@annotation() // 所有对象的所有方法级别的Jointpoint,方法是否包含某种类型的注解

 实际上,@AspectJ形式声明的所有Pointcut表达式,在Spring中都会通过解析,转化成具体的Pointcut对象。

 

public class AspectJProxyFactory extends ProxyCreatorSupport {

    public void addAspect(Class<?> aspectClass) {
        String aspectName = aspectClass.getName();
        AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
        MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(am, aspectClass, aspectName);
        addAdvisorsFromAspectInstanceFactory(instanceFactory);
    }

    private AspectMetadata createAspectMetadata(Class<?> aspectClass, String aspectName) {
        AspectMetadata am = new AspectMetadata(aspectClass, aspectName);
        if (!am.getAjType().isAspect()) {
            throw new IllegalArgumentException("Class [" + aspectClass.getName() + "] is not a valid aspect type");
        }
        return am;
    }
}

public AspectMetadata(Class<?> aspectClass, String aspectName) {
    this.aspectName = aspectName;

    Class<?> currClass = aspectClass;
    AjType<?> ajType = null;
    while (currClass != Object.class) {
        AjType<?> ajTypeToCheck = AjTypeSystem.getAjType(currClass);
        if (ajTypeToCheck.isAspect()) {
            ajType = ajTypeToCheck;
            break;
        }
        currClass = currClass.getSuperclass();
    }
    if (ajType == null) {
        throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect");
    }
    if (ajType.getDeclarePrecedence().length > 0) {
        throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP");
    }
    this.aspectClass = ajType.getJavaClass();
    this.ajType = ajType;

    switch (this.ajType.getPerClause().getKind()) {
        case SINGLETON:
            this.perClausePointcut = Pointcut.TRUE;
            return;
        case PERTARGET:
        case PERTHIS:
            AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
            ajexp.setLocation(aspectClass.getName());
            ajexp.setExpression(findPerClause(aspectClass));
            ajexp.setPointcutDeclarationScope(aspectClass);
            this.perClausePointcut = ajexp;
            return;
        case PERTYPEWITHIN:
            // Works with a type pattern
            this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
            return;
        default:
            throw new AopConfigException(
                    "PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
    }
}

 @AspectJ形式的Advice

@Before

@AfterReturning

@AfterThrowing

@After

@Aroud

@DeclareParents Introduction类型的Advice,该注解对应标注对象的域(Field)而不是方法(Method)

Before示例:

public class Foo {
    public void method1() {
        System.out.println("method1 execution.");
    }
    
    public void method2() {
        System.out.println("method2 execution.");
    }
    
    public void method3() {
        System.out.println("method3 execution.");
    }
    
    public void method4(String name) {
        System.out.println("method4 execution, name=" + name);
    }
}

@Aspect
public class BeforeAspect {
    
    @Pointcut("execution(public void *.method1()))")
    public void pointcutName(){}
    
    @Before("pointcutName()")
    public void beforeTest1() {
        System.out.println("@Before test1");
    }
    
    @Before("execution(public void *.method2()))")
    public void beforeTest2() {
        System.out.println("@Before test2");
    }
    
    @Before("execution(public void *.method4(String)))")
    public void beforeTest3(JoinPoint joinPoint) {
        System.out.println("@Before test3 : " + joinPoint.getArgs()[0]);
    }
    
    @Before(value = "execution(public void *.method4(String)) && args(name)")
    public void beforeTest4(String name) {
        System.out.println("@Before test4 : " + name);
    }
}

public class Main {
    
    public static void main(String[] args) {
        AspectJProxyFactory weaver = new AspectJProxyFactory();
        weaver.setProxyTargetClass(true);
        weaver.setTarget(new Foo());
        weaver.addAspect(BeforeAspect.class);
        Object proxy = weaver.getProxy();
        ((Foo) proxy).method1();
        ((Foo) proxy).method2();
        ((Foo) proxy).method3();
        ((Foo) proxy).method4("myName");
    }
}

执行结果:

@Before test1
method1 execution.
@Before test2
method2 execution.
method3 execution.
@Before test3 : myName
@Before test4 : myName
method4 execution, name=myName

 

posted on 2018-08-31 16:22  maoshiling  阅读(335)  评论(0编辑  收藏  举报

导航