摘要:本文学习了Spring面向切面的使用方式。
环境
Windows 10 企业版 LTSC 21H2
Java 1.8
Tomcat 8.5.50
Maven 3.6.3
Spring 5.2.25.RELEASE
1 依赖
添加依赖:
pom.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.25.RELEASE</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.25.RELEASE</version> </dependency>
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>
|
2 配置方式
2.1 XML配置
在XML配置文件中使用aop:config标签配置切面,需要在XML文件中添加aop命名空间。
创建实体类:
java1 2 3 4 5 6 7 8
| public class User { private Integer id; private String name; public void info() { System.out.println("id=" + id + ", name=" + name); } }
|
创建切面类:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class DemoAspect { public void before() { System.out.println("执行前置通知before()方法"); } public void after() { System.out.println("执行后置通知after()方法"); } public void afterReturning(Object result) { System.out.println("执行返回通知afterReturning()方法"); } public void afterThrowing(Throwable exception) { System.out.println("执行异常通知afterThrowing()方法"); } public Object around(ProceedingJoinPoint joinPoint) { Object result = null; try { System.out.println("执行环绕通知around()方法的before前置通知"); result = joinPoint.proceed(joinPoint.getArgs()); System.out.println("执行环绕通知around()方法的afterReturning返回通知"); } catch (Throwable e) { System.out.println("执行环绕通知around()方法的afterThrowing异常通知"); throw new RuntimeException(e); } finally { System.out.println("执行环绕通知around()方法的after后置通知"); } return result; } }
|
创建配置文件:
spring.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="user" class="com.example.bean.User"/>
<bean id="demoAspect" class="com.example.aop.DemoAspect"/>
<aop:config> <aop:pointcut expression="execution(* *.*(..))" id="pointcut"/> <aop:aspect ref="demoAspect" order="1"> <aop:before method="before" pointcut-ref="pointcut"/> <aop:after method="after" pointcut-ref="pointcut"/> <aop:after-returning method="afterReturning" returning="result" pointcut-ref="pointcut"/> <aop:after-throwing method="afterThrowing" throwing="exception" pointcut-ref="pointcut"/> <aop:around method="around" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> </beans>
|
创建启动类:
java1 2 3 4 5 6 7 8 9 10 11 12
| public class DemoApplication { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); User user = context.getBean(User.class); user.info(); context.close(); } }
|
执行结果:
log1 2 3 4 5 6 7
| 执行前置通知before()方法 执行环绕通知around()方法的before前置通知 id=null, name=null 执行环绕通知around()方法的afterReturning返回通知 执行环绕通知around()方法的after后置通知 执行返回通知afterReturning()方法 执行后置通知after()方法
|
2.2 半注解配置
通过注解简化XML配置。
在XML配置文件中使用aop:aspectj-autoproxy标签启用切面代理,将标注了切面注解的类自动生效并拦截目标方法。
修改实体类:
java1 2 3 4 5 6 7 8 9
| @Component public class User { private Integer id; private String name; public void info() { System.out.println("id=" + id + ", name=" + name); } }
|
修改切面类:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| @Component @Aspect @Order(1) public class DemoAspect { @Pointcut(value = "execution(* *.*(..))") public void pointcut() { } @Before(value = "pointcut()") public void before() { System.out.println("执行前置通知before()方法"); } @After(value = "pointcut()") public void after() { System.out.println("执行后置通知after()方法"); } @AfterReturning(value = "pointcut()", returning = "result") public void afterReturning(Object result) { System.out.println("执行返回通知afterReturning()方法"); } @AfterThrowing(value = "pointcut()", throwing = "exception") public void afterThrowing(Throwable exception) { System.out.println("执行异常通知afterThrowing()方法"); } @Around(value = "pointcut()") public Object around(ProceedingJoinPoint joinPoint) { Object result = null; try { System.out.println("执行环绕通知around()方法的before前置通知"); result = joinPoint.proceed(joinPoint.getArgs()); System.out.println("执行环绕通知around()方法的afterReturning返回通知"); } catch (Throwable e) { System.out.println("执行环绕通知around()方法的afterThrowing异常通知"); throw new RuntimeException(e); } finally { System.out.println("执行环绕通知around()方法的after后置通知"); } return result; } }
|
修改配置文件:
spring.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.example"/>
<aop:aspectj-autoproxy/> </beans>
|
执行启动类:
java1 2 3 4 5 6 7 8 9 10 11 12
| public class DemoApplication { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); User user = context.getBean(User.class); user.info(); context.close(); } }
|
执行结果:
log1 2 3 4 5 6 7
| 执行环绕通知around()方法的before前置通知 执行前置通知before()方法 id=null, name=null 执行返回通知afterReturning()方法 执行后置通知after()方法 执行环绕通知around()方法的afterReturning返回通知 执行环绕通知around()方法的after后置通知
|
2.3 全注解配置
使用基于注解的配置方式,代替XML配置,能够大大简化配置工作,这是最常用的实现方式。
使用@EnableAspectJAutoProxy注解可以启用切面代理,代替在XML配置文件中使用aop:aspectj-autoproxy标签。
创建配置类:
java1 2 3 4 5
| @Configuration @EnableAspectJAutoProxy @ComponentScan("com.example") public class DemoConfig { }
|
修改启动类:
java1 2 3 4 5 6 7 8 9 10 11 12
| public class DemoApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class); User user = context.getBean(User.class); user.info(); context.close(); } }
|
执行结果:
log1 2 3 4 5 6 7
| 执行环绕通知around()方法的before前置通知 执行前置通知before()方法 id=null, name=null 执行返回通知afterReturning()方法 执行后置通知after()方法 执行环绕通知around()方法的afterReturning返回通知 执行环绕通知around()方法的after后置通知
|
3 常用切点
3.1 方法执行切点
使用execution匹配符合切点表达式的方法。
语法:
txt1
| execution(权限修饰符 返回值类型 类名.方法名(参数列表) 异常)
|
说明:
- 权限修饰符:可以省略,表示匹配所有。
- 返回值类型:不可以省略,使用
*表示匹配所有。
- 类名:不可以省略,使用
*表示匹配所有。
- 方法名:不可以省略,使用
*表示匹配所有。
- 参数列表:不可以省略,使用
..表示匹配所有。
示例:
execution(public * com.example.*.save*(..)):匹配com.example包下所有类中以save开头的public方法
execution(* *.*(..)):匹配所有方法
3.2 方法注解切点
使用@annotation匹配带有指定注解的方法。
示例:
@annotation(com.example.annotation.DemoAnnotation):匹配带有@DemoAnnotation注解的方法。
@annotation(com.example.annotation.DemoLog):匹配带有@DemoLog注解的方法。
3.3 类注解切点
使用@within匹配带有指定注解的类的所有方法。
示例:
@within(org.springframework.stereotype.Controller):匹配带有@Controller注解的类的所有方法。
@within(org.springframework.stereotype.Service):匹配带有@Service注解的类的所有方法。
3.4 组合切点
支持使用逻辑运算符组合匹配方法:
&&:与,同时满足两个切点表达式。
||:或,满足任意一个切点表达式。
!:非,不满足切点表达式。
条