Spring 中通常使用 AspectJ 来实现 AOP 操作,AspectJ 不是 Spring 的组成部分,是独立的 AOP 框架。
更多:AOP 基础

基于 XML 的 AOP

定义接口及实现

  1. public interface UserService {
  2. void save(int a);
  3. void get();
  4. void delete(int id);
  5. }
  6. public class UserServiceImpl implements UserService {
  7. public void save(int a) {
  8. System.out.println("save 方法执行");
  9. }
  10. public void get() {
  11. System.out.println("get 方法执行");
  12. int n = 1 / 0;
  13. }
  14. public void delete(int id) {
  15. System.out.println("delete 方法执行");
  16. }
  17. }

AOP增强类

  1. public class SpeLogger {
  2. public void beforeOrgLogger() {
  3. System.out.println("前置通知 Executed !");
  4. }
  5. public void afterReturnOrgLogger() {
  6. System.out.println("后置通知 Executed !");
  7. }
  8. public void throwOrgLogger() {
  9. System.out.println("异常通知 Executed !");
  10. }
  11. public void afterOrgLogger() {
  12. System.out.println("最终通知 Executed !");
  13. }
  14. public Object aroundOrgLogger(ProceedingJoinPoint proceedingJoinPoint) {
  15. Object data = null;
  16. try {
  17. System.out.println("前置通知");
  18. Object[] args = proceedingJoinPoint.getArgs();
  19. data = proceedingJoinPoint.proceed(args);
  20. System.out.println("后置通知");
  21. } catch (Throwable e) {
  22. System.out.println("异常通知");
  23. } finally {
  24. System.out.println("最终通知");
  25. }
  26. return data;
  27. }
  28. }

加入 IOC

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:aop="http://www.springframework.org/schema/aop"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. https://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/aop
  7. https://www.springframework.org/schema/aop/spring-aop.xsd">
  8. <!-- 接口 -->
  9. <bean id="userService" class="cn.com.lichenghao.service.impl.UserServiceImpl"></bean>
  10. <!-- 通知类 -->
  11. <bean id="speLogger" class="cn.com.lichenghao.aop.SpeLogger"></bean>
  12. <aop:config>
  13. <!-- 切点 -->
  14. <aop:pointcut id="logOperation" expression="execution(* cn.com.lichenghao.service.impl.*.*(..))"/>
  15. <!-- 切面 -->
  16. <aop:aspect ref="speLogger">
  17. <!-- 通知类型 前置、后置、异常、最终通知-->
  18. <!--
  19. <aop:before method="beforeOrgLogger" pointcut-ref="logOperation"></aop:before>
  20. <aop:after-returning method="afterReturnOrgLogger" pointcut-ref="logOperation"></aop:after-returning>
  21. <aop:after-throwing method="throwOrgLogger" pointcut-ref="logOperation"></aop:after-throwing>
  22. <aop:after method="afterOrgLogger" pointcut-ref="logOperation"></aop:after>
  23. -->
  24. <!-- 环绕通知 -->
  25. <aop:around method="aroundOrgLogger" pointcut-ref="logOperation"></aop:around>
  26. </aop:aspect>
  27. </aop:config>
  28. </beans>

测试下

  1. public class Test {
  2. public static void main(String[] args) {
  3. /**
  4. * 配置文件方式
  5. */
  6. ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
  7. UserService userService = (UserService)context.getBean("userService");
  8. userService.get();
  9. }
  10. }

基于注解的 AOP

定义增强类

  1. @Component("speLogger")
  2. @Aspect
  3. public class SpeLogger {
  4. @Pointcut(value = "execution(* cn.com.lichenghao.service.impl.*.*(..))")
  5. private void pt() {
  6. }
  7. @Before("pt()")
  8. public void beforeOrgLogger() {
  9. System.out.println("前置通知 Executed !");
  10. }
  11. @AfterReturning("pt()")
  12. public void afterReturnOrgLogger() {
  13. System.out.println("后置通知 Executed !");
  14. }
  15. @AfterThrowing("pt()")
  16. public void throwOrgLogger() {
  17. System.out.println("异常通知 Executed !");
  18. }
  19. @After("pt()")
  20. public void afterOrgLogger() {
  21. System.out.println("最终通知 Executed !");
  22. }
  23. //@Around("pt()")
  24. public Object aroundOrgLogger(ProceedingJoinPoint proceedingJoinPoint) {
  25. Object data = null;
  26. try {
  27. System.out.println("前置通知");
  28. Object[] args = proceedingJoinPoint.getArgs();
  29. data = proceedingJoinPoint.proceed(args);
  30. System.out.println("后置通知");
  31. } catch (Throwable e) {
  32. System.out.println("异常通知");
  33. } finally {
  34. System.out.println("最终通知");
  35. }
  36. return data;
  37. }
  38. }

加入IOC
application-annotation.xml

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:aop="http://www.springframework.org/schema/aop"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. https://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/aop
  8. https://www.springframework.org/schema/aop/spring-aop.xsd
  9. http://www.springframework.org/schema/context
  10. https://www.springframework.org/schema/context/spring-context.xsd">
  11. <!-- 包扫描 -->
  12. <context:component-scan base-package="cn.com.lichenghao"></context:component-scan>
  13. <!-- 启用aspectj注解 -->
  14. <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  15. </beans>

测试下

  1. public class Test {
  2. public static void main(String[] args) {
  3. /**
  4. * 注解方式
  5. */
  6. ApplicationContext context = new ClassPathXmlApplicationContext("application-annotation.xml");
  7. UserService userService = (UserService) context.getBean("userService");
  8. userService.save(1);
  9. }
  10. }

切入点表达式

用来表示对哪个类中的哪个方法进行增强
标准方式
访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
public void cn.com.lichenghao.service.impl.UserServiceImpl.get()
简化过程↓↓↓
👇
省略访问修饰符

  1. void cn.com.lichenghao.service.impl.UserServiceImpl.get()

👇
返回值使用通配符*,表示任意返回值

  1. * cn.com.lichenghao.service.impl.UserServiceImpl.get()

👇
包名使用通配符,代表包名,几级包名就写几个

  1. * *.*.*.*.impl.UserServiceImpl.get()

👇
包名通配符,*..代表包以及子包

  1. * *..service.impl.UserServiceImpl.get()

👇
类名和方法名可以使用通配符*

  1. * *..service.impl.*.get()
  2. * *..service.impl.*.*()

👇
参数列表可以写(基本类型直接写例如:int,引用类型写包全路径例如:java.lang.String)
👇
参数类型可以使用通配符*,前提是必须有参数

  1. * *..service.impl.*.*(*)

👇
参数类型可以使用..表示参数可以是任意类型

  1. * *..service.impl.*.*(..)

全通配
全通配方式:

  1. * *..*.*(..)

需要注意,如果这样配置,所有类的都满足这种方式,一般都不会这样配置

通常写法
业务类下的所有方法

  1. * cn.com.lichenghao.service.impl.*.*(..)