Spring面向切面编程
目录
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/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">
<!--注册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 {
@Test
public 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/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">
<!--注册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 {
@Test
public 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演示结束。。。。。。。。