目录
1.AOP概述及Spring AOP实现原理
AOP概述
AOP的应用场景
AOP的作用
Spring AOP概述
Spring AOP的实现原理
Spring AOP中Advice的分类
2. 通过xml配置实现AOP
实现步骤:
新增模块:
导入相关依赖:
新增实体类User
新增业务类UserService
定义切面类LogAspect
添加Spring核心配置文件
AOP配置说明
注册切面类
配置切面
Spring配置文件中完成切面配置
编写测试类,验证效果
运行结果:
当前的目录结构:
编辑
3. 通过注解实现AOP
Spring AOP注解
通过以下注解来定义通知
用例设计(采用注解):
实现步骤:
新增模块:
导入相关依赖:
新增实体类User
新增业务类UserService
定义切面类LogAspect
如何获取连接点信息
@AfterReturning
@Around
Spring配置文件中配置AspectJ自动代理
编写测试类
运行结果:
当前的目录结构如下:
1.AOP概述及Spring AOP实现原理
AOP概述
AOP的应用场景
AOP的作用
不改变原有代码前提下,增加新功能
横切关注点:跨越应用程序多个模块。与业务逻辑无关处设置需关注部分,就是横切关注点。如日志,安全,缓存,事务等等 …
切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):切面通知 执行的 “地点”的定义。
连接点(JointPoint):与切入点匹配的执行点。
Spring AOP概述
Spring AOP是Spring框架中的一个模块,是基于AOP思想通过整合AspectJ框架实现的面向切面编程技术
AspectJ是一个面向切面编程的具体实现框架,定义了AOP的语法
Spring AOP的实现原理
Spring AOP是基于动态代理技术实现的。
在程序运行过程中,SpringAOP内部会基于JDK动态代理技术和cglib动态代理技术为目标对象生成代理对象。
当程序调用目标对象方法时,不是直接调用目标对象方法,而是先调用代理对象的方法,再调用目标对象的方法,从而对目标方法进行增强。
Spring AOP中Advice的分类
2. 通过xml配置实现AOP
(1)当应用程序调用业务逻辑层组件方法之前,在控制台中打印出“开始调用方法”
(2)当应用程序调用业务逻辑层组件方法之后,在控制台中打印出“结束调用方法”
(3)当应用程序调用业务逻辑层组件方法出现了异常,在控制台中打印出“调用方法出现异常”
实现步骤:
新增模块:
导入相关依赖:
xml配置如下:
<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.flowerfog</groupId><artifactId>Spring6</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>spring-aop-xml</artifactId><packaging>war</packaging><name>spring-aop-xml Maven Webapp</name><url>http://maven.apache.org</url><dependencies><!--spring context依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.2</version></dependency><!--spring和AspectJ整合依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.2</version></dependency><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency><!--lombok插件--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency></dependencies><build><finalName>spring-aop-xml</finalName></build>
</project>
新增实体类User
package org.flowerfog.pojo;import lombok.Data;@Data
public class User {private Integer id;private String name;private String account;private String password;private Integer status;
}
新增业务类UserService
package org.flowerfog.Service;import org.flowerfog.pojo.User;public class UserService {public void add(User user) {System.out.println("新增用户" + user);}public void removeById(Integer id) {System.out.println("删除Id=" + id + "的用户");}
}
定义切面类LogAspect
package org.flowerfog.comm;public class LogAspect {// 前置通知public void before(){System.out.println("开始调用方法");}// 后置通知public void after(){System.out.println("结束调用方法");}// 异常通知public void exception(Exception ex){System.out.println("调用方法出席异常"+ex.getMessage());}
}
添加Spring核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
AOP配置说明
注册切面类
配置切面
标签元素 | 作用 |
<aop:config> | AOP配置顶层节点,一个配置文件中可以包含多个<aop:config>节点 |
<aop:aspect> | 配置切面,该元素会将Spring容器中的Bean转换成切面Bean |
<aop:pointcut> | 配置切点,通过id属性指定切点唯一标识,通过expression属性指定切点表达式 |
Spring配置文件中完成切面配置
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--注册Bean--><bean id="userService" class="org.flowerfog.Service.UserService" /><bean id="logAspect" class="org.flowerfog.comm.LogAspect" /><!--AOP配置,需导入aop约束--><aop:config><!--配置切点--><aop:pointcut id="pointcut" expression="execution(* org.flowerfog.Service.*.*(..))"/><!--配置切面Bean--><aop:aspect ref="logAspect"><aop:before method="before" pointcut-ref="pointcut" /><aop:after method="after" pointcut-ref="pointcut" /><aop:after-throwing method="exception" pointcut-ref="pointcut" throwing="ex" /></aop:aspect></aop:config>
</beans>
编写测试类,验证效果
import org.flowerfog.Service.UserService;
import org.flowerfog.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserServiceTest {@Testpublic void add() {// 加载Spring核心配置文件ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");// 获取bean对象UserService userService = (UserService) ctx.getBean("userService");User user = new User();user.setName("张三");user.setAccount("111");user.setPassword("123456");user.setStatus(0);userService.add(user);}
}
运行结果:
当前的目录结构:
3. 通过注解实现AOP
Spring AOP注解
@Aspect定义切面
切面类需注册到Spring容器@Aspect注解才能生效
@Pointcut定义切点
切面类中定义一个空方法并使用@Pointcut注解来定义切点
然后在@Pointcut注解中定义切点表达式用来匹配切入的目标类和方法
空方法的方法名就是切点的唯一标识id
通过以下注解来定义通知
用例设计(采用注解):
(1)当应用程序调用业务逻辑层组件方法之前,在控制台中打印出“开始调用方法”
(2)当应用程序调用业务逻辑层组件方法之后,在控制台中打印出“结束调用方法”
(3)当应用程序调用业务逻辑层组件方法出现了异常,在控制台中打印出“调用方法出现异常”
实现步骤:
①新建项目模块
②整合Spring AOP框架依赖
③定义切面类以及通知方法
④添加注解完成切面配置
⑤运行测试类完成切面测试
新增模块:
导入相关依赖:
<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.flowerfog</groupId><artifactId>Spring6</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>spring-aop-annotation</artifactId><packaging>war</packaging><name>spring-aop-annotation Maven Webapp</name><url>https://maven.apache.org</url><dependencies><!--spring context依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.2</version></dependency><!--spring和AspectJ整合依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.2</version></dependency><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency><!--lombok插件--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency></dependencies><build><finalName>spring-aop-annotation</finalName></build>
</project>
新增实体类User
package org.flowerfog.pojo;import lombok.Data;@Data
public class User {private Integer id;private String name;private String account;private String password;private Integer status;
}
新增业务类UserService
package org.flowerfog.Service;import org.flowerfog.pojo.User;public class UserService {public void add(User user) {System.out.println("新增用户" + user);}public void removeById(Integer id) {System.out.println("删除Id=" + id + "的用户");}
}
定义切面类LogAspect
package org.flowerfog.comm;import org.aspectj.lang.annotation.*;// 定义切面
@Aspect
public class LogAspect {// 定义切点,其id为pointcut@Pointcut("execution(* org.flowerfog.Service.*.*(..))")public void pointcut(){}// 前置通知@Before("pointcut()")public void before(){System.out.println("开始调用方法");}// 后置通知@After("pointcut()")public void after(){System.out.println("结束调用方法");}// 异常通知@AfterThrowing(value = "pointcut()",throwing = "ex")public void exception(Exception ex){System.out.println("调用方法出现异常"+ex.getMessage());}
}
如何获取连接点信息
@AfterReturning
LogAspect当前代码如下:
package org.flowerfog.comm;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.util.ObjectUtils;import java.lang.reflect.Method;// 定义切面
@Aspect
public class LogAspect {// 定义切点,其id为pointcut@Pointcut("execution(* org.flowerfog.Service.*.*(..))")public void pointcut(){}// 前置通知@Before("pointcut()")public void before(){System.out.println("开始调用方法");}// 后置通知@After("pointcut()")public void after(){System.out.println("结束调用方法");}// 异常通知@AfterThrowing(value = "pointcut()",throwing = "ex")public void exception(Exception ex){System.out.println("调用方法出现异常"+ex.getMessage());}// 返回通知@AfterReturning("pointcut()")public void afterReturn(JoinPoint joinPoint){MethodSignature signature=(MethodSignature)joinPoint.getSignature(); // 获得目标对象的签名,及当前拦截到的方法Method method=signature.getMethod();String longString = joinPoint.toLongString();// 获得连接点的扩展全命名Object[] args = joinPoint.getArgs();// 返回连接点的参数数组Object target = joinPoint.getTarget();// 获得目标对象Object aThis = joinPoint.getThis();// 获得正在执行的目标对象JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();// 获得连接点的静态部分SourceLocation sourceLocation = joinPoint.getSourceLocation();// 获得连接点对应的方法源地址String shortString = joinPoint.toShortString();// 获得连接点的缩写命名String kind = joinPoint.getKind();// 返回连接点的类型System.out.println("method:"+method+"\r\nlongString:"+longString);if (!ObjectUtils.isEmpty(args)) {for (Object arg : args) {System.out.println(arg);}}System.out.println("调用方法ok");}}
@Around
最终LogAspect代码如下:
package org.flowerfog.comm;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.util.ObjectUtils;import java.lang.reflect.Method;// 定义切面
@Aspect
public class LogAspect {// 定义切点,其id为pointcut@Pointcut("execution(* org.flowerfog.Service.*.*(..))")public void pointcut(){}// 前置通知@Before("pointcut()")public void before(){System.out.println("开始调用方法");}// 后置通知@After("pointcut()")public void after(){System.out.println("结束调用方法");}// 异常通知@AfterThrowing(value = "pointcut()",throwing = "ex")public void exception(Exception ex){System.out.println("调用方法出现异常"+ex.getMessage());}// 返回通知@AfterReturning("pointcut()")public void afterReturn(JoinPoint joinPoint){MethodSignature signature=(MethodSignature)joinPoint.getSignature(); // 获得目标对象的签名,及当前拦截到的方法Method method=signature.getMethod();String longString = joinPoint.toLongString();// 获得连接点的扩展全命名Object[] args = joinPoint.getArgs();// 返回连接点的参数数组Object target = joinPoint.getTarget();// 获得目标对象Object aThis = joinPoint.getThis();// 获得正在执行的目标对象JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();// 获得连接点的静态部分SourceLocation sourceLocation = joinPoint.getSourceLocation();// 获得连接点对应的方法源地址String shortString = joinPoint.toShortString();// 获得连接点的缩写命名String kind = joinPoint.getKind();// 返回连接点的类型System.out.println("method:"+method+"\r\nlongString:"+longString);if (!ObjectUtils.isEmpty(args)) {for (Object arg : args) {System.out.println(arg);}}System.out.println("调用方法ok");}@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {//开始时间long beginTime = System.currentTimeMillis();//执行目标方法Object result = joinPoint.proceed();//执行时长(毫秒)long time = System.currentTimeMillis()-beginTime;System.out.println("执行时长(毫秒):"+time);return result;}}
Spring配置文件中配置AspectJ自动代理
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--注册Bean--><bean id="userService" class="org.flowerfog.Service.UserService" /><bean id="logAspect" class="org.flowerfog.comm.LogAspect" /><!--启用AspectJ自动代理--><aop:aspectj-autoproxy />
</beans>
编写测试类
import org.flowerfog.Service.UserService;
import org.flowerfog.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserServiceTest {@Testpublic void add() {// 加载Spring核心配置文件ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");// 获取bean对象UserService userService = (UserService) ctx.getBean("userService");User user = new User();user.setName("李四");user.setAccount("222");user.setPassword("123456");user.setStatus(0);userService.add(user);}
}
运行结果:
当前的目录结构如下:
至此, AOP演示结束。。。。。。。。