spring切面的使用@AspectJ注解的3种配置

定义
与 AspectJ 相同的是,Spring AOP 同样需要对目标类进行增强,也就是生成新的 AOP 代理类;与 AspectJ 不同的是,Spring AOP 无需使用任何特殊命令对 Java 源代码进行编译,它采用运行时动态地、在内存中临时生成“代理类”的方式来生成 AOP 代理。

Spring 允许使用 AspectJ Annotation 用于定义方面(Aspect)、切入点(Pointcut)和增强处理(Advice),Spring 框架则可识别并根据这些 Annotation 来生成 AOP 代理。
简单地说,Spring 依然采用运行时生成动态代理的方式来增强目标对象,所以它不需要增加额外的编译,也不需要 AspectJ 的织入器支持;而 AspectJ 在采用编译时增强,所以 AspectJ 需要使用自己的编译器来编译 Java 文件,还需要织入器。
例子
普通执行类

package com.samter.common; 
/** 
 * 猴子 
 * @author Administrator 
 */  
public class Monkey {
    
            
    public void stealPeaches(String name){
    
      
        System.out.println("【猴子】"+name+"正在偷桃...");  
    }  
}

守护者类(声明为Aspect):

package com.samter.aspect;  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
  
/** 
 * 桃园守护者 
 * @author Administrator 
 */  
@Aspect  
public class Guardian {
    
      
      
    @Pointcut("execution(* com.samter.common.Monkey.stealPeaches(..))")  
    public void foundMonkey(){
    
    }  
  
    @Before(value="foundMonkey()")  
    public void foundBefore(){
    
      
        System.out.println("【守护者】发现猴子正在进入果园...");  
    }  
      
    @AfterReturning("foundMonkey() && args(name,..)")  
    public void foundAfter(String name){
    
      
        System.out.println("【守护者】抓住了猴子,守护者审问出了猴子的名字叫“"+name+"”...");  
    }  
      
}

xml配置文件

<?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"  
       xsi:schemaLocation="  
       http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
       http://www.springframework.org/schema/aop  
       http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"  
>  
  
    <!-- 定义Aspect -->  
    <bean id="guardian" class="com.samter.aspect.Guardian" />  
  
    <!-- 定义Common -->  
    <bean id="monkey" class="com.samter.common.Monkey" />  
  
    <!-- 启动AspectJ支持 -->  
    <aop:aspectj-autoproxy />  
</beans>

测试类

package com.samter.common;  
  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
public class Main {
    
      
    public static void main(String[] args) {
    
      
        ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");  
        Monkey monkey = (Monkey) context.getBean("monkey");  
        try {
    
      
            monkey.stealPeaches("孙大圣的大徒弟");  
        }  
        catch(Exception e) {
    
    }  
    }    
}

控制台输出:

【守护者】发现猴子正在进入果园...  
【猴子】孙大圣的大徒弟正在偷桃...  
【守护者】抓住了猴子,守护者审问出了猴子的名字叫“孙大圣的大徒弟”... 

解说:

  1写了一个猴子正在偷桃的方法。 
  2写了一个标志为@Aspect的类,它是守护者。它会在猴子偷桃之前发现猴子,并在猴子偷桃之后抓住猴子。 
  原理:

        A、@Aspect的声明表示这是一个切面类。 
        B、@Pointcut使用这个方法可以将com.samter.common.Monkey.stealPeaches(..)方法声明为poincut即切入点。作用,在stealPeaches方法被调用的时候执行2的foundMonkey方法。其中execution是匹配方法执行的切入点,也就是spring最常用的切入点定义方式。 
        C、@Before(value="foundMonkey()"):@Before声明为在切入点方法执行之前执行,而后面没有直接声明切入点,而是value="foundMonkey()",是因为如果@afterReturning等都有所改动的时候都必须全部改动,所以统一用Pointcut的foundMonkey代替,这样子有改动的时候仅需改动一个地方。其他@AfterReturning类同。 
  3是xml配置文件,里面有具体的注释。 

特别说明:Guardian类里面的@Pointcut(“execution(* com.samter.common.Monkey.stealPeaches(…))”),如果stealPeaches有参数则…表示所有参数,@AfterReturning(“foundMonkey() && args(name,…)”)的&& args(name,…)可以获取切入点方法stealPeaches的参数。

总结:这里列举了一个简单的例子,但是不难引申到应用中,当你写一个登陆系统的时候,你或许要记录谁成功登陆了系统,谁登陆系统密码错误等等的信息,这样子你用切面是再合适不过的了,总之当你的事务逻辑都设计到日志、安全检查、事务管理等等共同的内容的时候,用切面是要比你没有一个事务逻辑类都有相关代码或者相关引用好得多

@AspectJ的AOP功能的配置方式有三种:
1、通过 Spring 的 XML Schema 配置方式:
2、通过启动@AspectJ(此时schema中不需要相关的AOP配置)
3、@EnableAspectJAutoProxy(@Configuration注解)spring3.1及以上版本
不用xml配置文件的情况下,通过@Configuration来装配Spring bean,@EnableAspectJAutoProxy来启动spring AOP功能

在@Aspect中

package com.dxz.aop.demo6;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;

// 定义一个方面
@Aspect
@Configuration
public class AfterReturningAdviceTest {
    
    
    // 匹配 com.dxz.aop.demo6 包下所有类的下的所有方法的执行作为切入点
    @AfterReturning(returning = "rvt", pointcut = "execution(* com.dxz.aop.demo6.*.*(..))")
    public void log(Object rvt) {
    
    
        System.out.println("AfterReturningAdviceTest==获取目标方法返回值 :" + rvt);
    }
}

spring配置类

package com.dxz.aop.demo6;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
@Import({
    
    AfterReturningAdviceTest.class})/*@Aspect可以生效,相当于Configuration类作用,都是配置类*/  
public class AppConfig {
    
    

    @Bean(name = "chinese")
    public Chinese chinese() {
    
    
        return new Chinese();
    }
}

启动类

package com.dxz.aop.demo6;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test6 {
    
    

    public static void main(String[] args) {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Person outPut = (Person) context.getBean("chinese");
        outPut.sayHello("duan");
    }
}

结果

信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@c4437c4: startup date [Tue Dec 26 17:03:32 CST 2017]; root of context hierarchy
AfterReturningAdviceTest==获取目标方法返回值 :com.dxz.aop.demo6.Chinese@72967906
duan Hello , Spring AOP
AfterReturningAdviceTest==获取目标方法返回值 :duan Hello , Spring AOP
 

三、注解

2 注解说明
2.1 @Aspect

作用是把当前类标识为一个切面供容器读取

2.2 @Before
标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有

2.3 @AfterReturning

后置增强,相当于AfterReturningAdvice,方法正常退出时执行

2.4 @AfterThrowing

异常抛出增强,相当于ThrowsAdvice

2.5 @After

final增强,不管是抛出异常或者正常退出都会执行

2.6 @Around

环绕增强,相当于MethodInterceptor

2.7 @DeclareParents

引介增强,相当于IntroductionInterceptor

3 execution切点函数

execution函数用于匹配方法执行的连接点,语法为:

execution(方法修饰符(可选) 返回类型 方法名 参数 异常模式(可选))

参数部分允许使用通配符:

  • 匹配任意字符,但只能匹配一个元素

… 匹配任意字符,可以匹配任意多个元素,表示类时,必须和*联合使用

  • 必须跟在类名后面,如Horseman+,表示类本身和继承或扩展指定类的所有类

示例中的* chop(…)解读为:

方法修饰符 无

返回类型 *匹配任意数量字符,表示返回类型不限

方法名 chop表示匹配名称为chop的方法

参数 (…)表示匹配任意数量和类型的输入参数

异常模式 不限

更多示例:

void chop(String,int)

匹配目标类任意修饰符方法、返回void、方法名chop、带有一个String和一个int型参数的方法

public void chop(*)

匹配目标类public修饰、返回void、方法名chop、带有一个任意类型参数的方法

public String o(…)

匹配目标类public修饰、返回String类型、方法名中带有一个o字符、带有任意数量任意类型参数的方法

public void o(String,…)

匹配目标类public修饰、返回void、方法名中带有一个o字符、带有任意数量任意类型参数,但第一个参数必须有且为String型的方法

也可以指定类:

public void examples.chap03.Horseman.*(…)

匹配Horseman的public修饰、返回void、不限方法名、带有任意数量任意类型参数的方法

public void examples.chap03.man.(…)

匹配以man结尾的类中public修饰、返回void、不限方法名、带有任意数量任意类型参数的方法

指定包:

public void examples.chap03.*.chop(…)

匹配examples.chap03包下所有类中public修饰、返回void、方法名chop、带有任意数量任意类型参数的方法

public void examples…*.chop(…)

匹配examples.包下和所有子包中的类中public修饰、返回void、方法名chop、带有任意数量任意类型参数的方法
可以用这些表达式替换StorageAdvisor中的代码并观察效果

4 更多切点函数
除了execution(),Spring中还支持其他多个函数,这里列出名称和简单介绍,以方便根据需要进行更详细的查询

4.1 @annotation()

表示标注了指定注解的目标类方法

例如 @annotation(org.springframework.transaction.annotation.Transactional) 表示标注了@Transactional的方法

4.2 args()

通过目标类方法的参数类型指定切点

例如 args(String) 表示有且仅有一个String型参数的方法

4.3 @args()

通过目标类参数的对象类型是否标注了指定注解指定切点

如 @args(org.springframework.stereotype.Service) 表示有且仅有一个标注了@Service的类参数的方法

4.4 within()

通过类名指定切点

如 with(examples.chap03.Horseman) 表示Horseman的所有方法

4.5 target()

通过类名指定,同时包含所有子类

如 target(examples.chap03.Horseman) 且Elephantman extends Horseman,则两个类的所有方法都匹配

4.6 @within()

匹配标注了指定注解的类及其所有子类

如 @within(org.springframework.stereotype.Service) 给Horseman加上@Service标注,则Horseman和Elephantman 的所有方法都匹配

4.7 @target()

所有标注了指定注解的类

如 @target(org.springframework.stereotype.Service) 表示所有标注了@Service的类的所有方法

4.8 this()

大部分时候和target()相同,区别是this是在运行时生成代理类后,才判断代理类与指定的对象类型是否匹配

5 逻辑运算符
表达式可由多个切点函数通过逻辑运算组成

5.1 &&

与操作,求交集,也可以写成and

例如 execution(* chop(…)) && target(Horseman) 表示Horseman及其子类的chop方法

5.2 ||

或操作,求并集,也可以写成or

例如 execution(* chop(…)) || args(String) 表示名称为chop的方法或者有一个String型参数的方法

5.3 !

非操作,求反集,也可以写成not

例如 execution(* chop(…)) and !args(String) 表示名称为chop的方法但是不能是只有一个String型参数的方法

下一篇
@Enable*注解的源码,spring源码分析之定时任务Scheduled注解

猜你喜欢

转载自blog.csdn.net/u010010600/article/details/114116528
今日推荐