JavaEE Spring基于注解的AOP

描述

使用核心配置文件加注解的方式,实现 Spring AOP 的五种通知类型。

题目

搭建 Spring 的开发环境,使用核心配置文件加注解的方式,实现 Spring AOP 的五种通知类型。

题目解决思路

  1. 创建 Maven 项目
  2. pom.xml 导入相关依赖。
  3. 创建目标对象和切面对象,并加上相关注解。
  4. 完成核心配置文件的相关配置。

项目结构

46_JavaEE Spring.png

前期准备

pom 文件:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.haicoder</groupId> <artifactId>SpringDemo14</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--IOC依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.7.RELEASE</version> </dependency> <!--AOP依赖--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> <!--Junit依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>

代码具体实现

核心配置文件:

<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--IOC注解扫描--> <context:component-scan base-package="net.haicoder"/> <!--AOP注解扫描--> <aop:aspectj-autoproxy/> </beans>

切面类:

@Component @Aspect public class LogAspect { // 统一定义切入点 @Pointcut("execution(* net.haicoder.service.impl.UserServiceImpl.save(..))") public void pointcut(){} /** * 1. 前置通知方法 */ @Before("pointcut()") public void before(){ System.out.println("【前置通知】:写入日志..."); } /** * 2. 后置通知方法 */ @AfterReturning("pointcut()") public void afterReturning(){ System.out.println("【后置通知】:写入日志..."); } /** * 3. 异常通知方法 */ @AfterThrowing("pointcut()") public void afterThrowing(){ System.out.println("【异常通知】:写入日志..."); } /** * 4. 最终通知方法 */ @After("pointcut()") public void after(){ System.out.println("【最终通知】:写入日志..."); } }

业务层接口:

public interface UserService { void save(); }

业务层实现类:

@Service public class UserServiceImpl implements UserService{ @Override public void save() { System.out.println("【用户操作】:保存用户!"); } }

测试类:

public class DemoTest { public static void main(String[] args) { System.out.println("嗨客网(www.haicoder.net)\n"); ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) act.getBean("userServiceImpl"); userService.save(); } }

运行结果

演示一(前置、后置、最终)

目标对象没有异常时,运行结果如下:

47_JavaEE Spring.png

演示二(前置、后置、异常、最终)

需要在目标方法中,模拟一个异常,修改 UserServiceImpl 代码如下:

public class UserServiceImpl implements UserService{ @Override public void save() { int i = 10/0; System.out.println("【用户操作】:保存用户!"); } }

运行结果如下:

48_JavaEE Spring.png

演示三(环绕通知)

在切面类中增加环绕通知方法,如下:

@Component @Aspect public class LogAspect { // 统一定义切入点 @Pointcut("execution(* net.haicoder.service.impl.UserServiceImpl.save(..))") public void pointcut(){} /** * 环绕通知方法 */ @Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint){ try{ System.out.println("【前置通知】:写入日志..."); //执行目标对象的方法 Object result = joinPoint.proceed(); System.out.println("【后置通知】:写入日志..."); return result; }catch (Throwable throwable){ throwable.printStackTrace(); System.out.println("【异常通知】:写入日志..."); throw new RuntimeException(throwable); }finally { System.out.println("【最终通知】:写入日志..."); } } }

目标对象没有异常时,运行结果如下:

49_JavaEE Spring.png

在目标方法中,模拟一个异常,修改 UserServiceImpl 代码如下:

public class UserServiceImpl implements UserService{ @Override public void save() { int i = 10/0; System.out.println("【用户操作】:保存用户!"); } }

目标对象有异常时,运行如下:

50_JavaEE Spring.png

总结

以上案例使用注解方式实现 Spring AOP 的五种通知类型。通过以上案例,可以发现演示一和演示二都会出现后置通知和最终通知的执行顺序混乱的问题,而环绕通知则不会出现此问题,因此为避免以上混乱问题,建议使用 环绕通知XML 配置方式