首页 > 编程笔记

Spring基于AspectJ的AOP开发

在 spring-aspects 模块中引入了 AspectJ 工程。Spring 2.0 以后,Spring 新增了对 AspectJ 的支持。在新版本的 Spring 框架中,建议使用 AspectJ 方式开发 AOP。

@AspectJ 可以通过注解声明切面。为了能够使用 @AspectJ 注解,必须要开启 Spring 的配置支持。@AspectJ 注解支持以 XML 的方式进行切面声明配置,如 <aop:aspectj-autoproxy/> 标签配置,也可以通过 Java 配置的方式进行切面声明配置。

@EnableAspectJAutoProxy 注解用于开启 AOP 代理自动配置。AspectJ 的重要注解如表 1 所示。

表1 AspceTJ的重要注解
注  解 说  明
@Aspect 定义一个切面,如果要被Spring管理,还需要配合使用@Component
@Pointcut 定义切点
@Before 前置增强
@ AfterReturning 后置增强,在方法返回后执行
@ AfterThrowing 后置增强,在抛出异常后执行
@After 后置增强
@Around 环绕增强

@Pointcut 切点注解是匹配一个执行表达式。表达式类型如表 2 所示。

表2 @Pointcut表达式类型
表 达 式 说  明
execution 匹配一个执行方法的连接点,如@Pointcut("execution(public * *(..))")
within 限定连接点属于某个确定类型的类,如@Pointcut("within(com.xyz.someapp.trading..*)")
this 匹配的连接点所属的对象引用是某个特定类型的实例,如@Pointcut("this(com. xyz .service .AccountService)")
target 匹配的连接点所属的目标对象必须是指定类型的实例,如@Pointcut("target(com. xyz .service .AccountService)")
args 匹配指定方法的参数,如@Pointcut("args(java.io.Serializable)")
@target 匹配指定的连接点,该连接点所属的目标对象的类有一个指定的注解,如
@Pointcut("@target(org. springframework, transaction, annotation. Transactional)")
@args 连接点在运行时传过来的参数的类必须要有指定的注解,如@Pointcut("@args (com. xyz. security. Classified)")
@within 匹配必须包括某个注解的类中的所有连接点,如@Pointcut("@within(org. springframework, transaction, annotatio n. Transactional)")
@annotation 匹配有指定注解的连接点,如 @Pointcut("@annotation(org.springframework, transaction, annotation.Transactional)")

@Pointcut 切点表达式可以组合使用&&|| ! 三种运算符。示例代码如下:

@Pointcut("com.xyz.myapp.SystemArchiteacture.dataAccessOperation()&& args(account,..)")

@Around 通过注解的方法可以传入 ProceedingJoinPoint 参数,ProceedingJoinPoint 类实例可以获取切点方法的相关参数及实例等。

基于 XML 配置的 AOP

下面的例子是基于 XML 方式配置的切面。

(1)定义一个类,在类中定义一些切点,代码如下://声明切面类
public class AspectTest {
    //方法执行前操作
        public void before() {
                System.out.println("before");
        }
    //方法执行后操作
        public void after() {
                System.out.println("after");
        }
    //方法环绕操作
        public void around() {
                System.out.println("around");
        }
}
(2)定义目标对象,代码如下:
//定义目标类
public class UserService {
        private Integer id;
        private String name;
        public Integer getId() {
                return id;
        }
        public void setId(Integer id) {
                this.id = id;
        }
        public String getName() {
                return name;
        }
        public void setName(String name) {
                this.name = name;
        }
        //执行方法
        public void getUser() {
                System.out.println("id:"+this.id);
                System.out.println("name:"+this.name);
        }
}
(3)基于 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
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:aspectj-autoproxy/>
    <bean id="aspectTest" class="com.spring.boot.AspectTest"/>
    <bean id="userService" class="com.spring.boot.UserService">
        <property name="id" value="1"/>
        <property name="name" value="zhangsan"/>
    </bean>
    <aop:config>
        <aop:pointcut expression="execution(public * com.spring.boot.UserService.getUser(..))"  id="pointcut"/>
        <aop:aspect order="1" ref="aspectTest">
            <aop:before method="before" pointcut-ref="pointcut"/>
        </aop:aspect>
        <aop:aspect order="2" ref="aspectTest">
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
        <aop:aspect order="3" ref="aspectTest">
            <aop:after method="around" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>
测试程序,代码如下:public class SpringXmlTest {
    public static void main(String[] args) {
        //通过spring.xml获取Spring应用上下文
        ApplicationContext context = new ClassPathXmlApplication
Context("spring.xml");
        UserService userService = context.getBean("userService",
UserService.class);
        userService.getUser(); //打印结果
    }
}
在以上代码中,<aop:aspectj-autoproxy>标签开启了全局AspectJ,<aop:config>标签定义了Pointcut和Aspect。

基于 @Aspect 注解的 AOP

基于 @Aspect 注解的切面编程完全可以通过注解的形式完成。

(1)定义一个 @User 注解,代码如下:
//定义@User注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface User {
}
(2)定义切面类,代码如下
//定义切面
@Aspect
@Component
public class AspectTest {
    //定义切点,表示使用了@User注解的方法将会被增强处理
        @Pointcut("@annotation(com.spring.boot.User)")
        public void pointCut() {}
    //在切点方法之前执行
        @Before("pointCut()")
        public void before() {
                System.out.println("before");
        }
    //处理真实的方法
        @Around("pointCut()")
        public void around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable {
                System.out.println("around before");
                proceedingJoinPoint.proceed();
                System.out.println("around after");
        }
    //在切点方法之后执行
        @After("pointCut()")
        public void after() {
                System.out.println("after");
        }
}
(3)新建配置类,代码如下:
//配置类开启切面配置
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.spring.boot")
public class SpringConfigTest {
        @Bean
        public UserService userService() {
                return new UserService();
        }
    public static void main(String[] args) {
        //通过配置类获取Spring应用上下文
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfigTest.class);
        UserService userService = context.getBean(UserService.class);
        userService.setId(1);
        userService.setName("zhangsan");
        userService.getUser();                                  //打印属性值
    }
}
(4)在目标类中增加 @User 注解,代码如下:
@Service
public class UserService {
        private Integer id;
        private String name;
        public Integer getId() {
                return id;
        }
        public void setId(Integer id) {
                this.id = id;
        }
        public String getName() {
                return name;
        }
        public void setName(String name) {
                this.name = name;
        }
    //添加了@User注解的方法
        @User
        public void getUser() {
                System.out.println("id:"+this.id);
                System.out.println("name:"+this.name);
        }
}

优秀文章