大家好,我是香香。
昨天我们介绍了 Spring 两大核心特性中的 IOC(控制反转),今天我们来介绍另一个核心特性 AOP(面向切面编程)。
AOP(面向切面编程)
在软件开发领域,我们经常会遇到一些横切关注点(的问题,比如日志记录、事务管理、安全性等。
这些问题往往涉及多个模块,而且与业务逻辑相互交织,给代码的维护和扩展带来了困难。
为了解决上述问题,Spring 框架提供了一个强大的工具—— AOP。
1. 什么是 AOP?
AOP 是一种编程范式,它允许开发者将关注点分离出来,并将其应用于不同的对象上。
在 Spring 中,AOP 通过切面(Aspect)和连接点(Join Point)的概念来实现。 切面定义了横切关注点的行为,而连接点则是程序执行过程中能够被切面拦截的特定点。
通过将切面与连接点结合起来,我们可以实现对关注点的统一处理,而无需修改原始业务逻辑代码。
2. Spring中的AOP实现方式
在 Spring 中,AOP 的实现主要依赖于动态代理技术。
Spring AOP 提供了两种代理方式:基于接口的代理
(JDK动态代理)和基于类的代理
(CGLIB动态代理)。
当目标对象实现了接口时,Spring 会使用JDK动态代理;否则,将使用 CGLIB 动态代理。
3. AOP的核心概念
-
切面(Aspect):切面是定义横切关注点以及它们的行为的类。它由切点和通知组成。 -
切点(Pointcut):切点是一个表达式,用于匹配连接点。通过使用切点表达式,我们可以指定需要拦截的特定方法或类。 -
通知(Advice):通知定义了在切点处执行的具体操作。Spring 提供了几种常见的通知类型,包括前置通知、后置通知、异常通知、环绕通知等。 -
连接点(Join Point):连接点是程序执行过程中能够被切面拦截的特定点。在 Spring 中,连接点可以是方法的调用、方法的执行、异常的抛出等。 -
织入(Weaving):织入是将切面应用到目标对象上的过程。Spring 支持编译时织入、类加载时织入和运行时织入三种方式。
4. 使用Spring AOP的步骤
-
引入 Spring 框架和 AOP 相关的依赖。 -
创建切面类,定义切点和通知。 -
配置 AOP,在 Spring 配置文件中声明切面和目标对象。 -
测试 AOP,执行目标方法,观察切面是否生效。
5. Spring AOP的应用场景
-
日志记录:通过 AOP 可以方便地实现日志记录功能,无需修改原始业务逻辑代码。 -
事务管理:AOP 可以在方法执行前后添加事务的开启和提交操作,简化了事务管理的代码。 -
安全性控制:通过 AOP 可以实现权限校验、加密解密等安全性控制的功能。 -
性能监控:通过 AOP 可以监控方法的执行时间、资源占用情况等,用于性能优化和瓶颈定位。
6. AOP 操作(基于 AspectJ 注解)
1、「什么是AspectJ ?」
Spring 框架一般都是基于 AspectJ 实现 AOP 操作。AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spring 框架一起使用,进行 AOP 操作。
2、「基于AspectJ实现AOP操作」
(1)基于xml配置文件实现
(2)基于注解方式实现(使用)
3、「在项目工程里面引入AOP相关依赖」
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.9</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
4、「切入点表达式」
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构:
execution (【权限修饰符】【返回类型】【类全路径】【方法名称】(【参数列表】))
举例1:对com.jin.dao.UserDao类里面的add进行增强。
execution(* com.jin.dao.*.*(..))
举例2:对com.jin.dao.UserDao类里面的所有的方法进行增强。
execution(* com.jin.dao.UserDao.*(..))
举例3:对com.jin.dao类里面所有类,类里面的所有的方法进行增强。
execution(* com.jin.dao.*.*(..))
重点实现:
1、创建类,在类里面定义方法
//被增强的类
public class User {
public void add(){
System.out.println("add ...");
}
2、创建增强类(编写增强逻辑)
(1)在增强类里面,创建方法,让不同方法代表不同通知类型
//增强类
public class UserProxy {
//前置通知
public void before(){
System.out.println("before ...");
}
}
3、进行通知的配置
(1)在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: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.jin.aopanno"></context:component-scan>
</beans>
(2)使用注解创建User和UserProxy对象
//被增强的类
@Component
public class User {
...
}
//增强类
@Component
public class UserProxy {
...
}
(3)在增强类上面添加注解@Aspect
//增强类
@Component
@Aspect //生成代理对象
public class UserProxy {
...
}
(4)在spring配置文件中开启生成代理对象
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4、配置不同类型的通知
(1)在增加类的里面,通知方法上面添加通知类型注解,使用切入点表达式配置
//增强类
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
//@Before注解表示作为前置通知
@Before(value = "execution(* com.jin.aopanno.User.add(..))")
public void before(){
System.out.println("before ...");
}
@AfterReturning(value = "execution(* com.jin.aopanno.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning ...");
}
@After(value = "execution(* com.jin.aopanno.User.add(..))")
public void after(){
System.out.println("after ...");
}
@AfterThrowing(value = "execution(* com.jin.aopanno.User.add(..))")
public void afterThrowing(){
System.out.println("AfterThrowing ...");
}
@Around(value = "execution(* com.jin.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前 ...");
proceedingJoinPoint.proceed();
System.out.println("环绕之后 ...");
}
}
5、相同的切入点抽取
//增强类
@Component
@Aspect //生成代理对象
public class UserProxy {
//相同切入点抽取
@Pointcut(value = "execution(* com.jin.aopanno.User.add(..))")
public void pointdemo(){}
//前置通知
//@Before注解表示作为前置通知
@Before(value = "pointdemo()")
public void before(){
System.out.println("before ...");
}
}
6、有多个增强类多同一个方法进行增强,设置增强类优先级
(1)在增强类撒谎给你面添加注解@Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect //生成代理对象
@Order(1) //优先级(数字值越小优先级越高)
public class PersonProxy {
...
}
7、完全使用注解开发
(1)创建配置类,不需要创建xml配置文件
@ComponentScan(basePackages = {"com.jin.pojo"})
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) //默认EnableAspectJAutoProxy为false
public class SpringConfig {
}
(2)测试代码
@Test
public void MyTest01(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = context.getBean("user", User.class);
user.add();
}
6. 总结
通过对Spring AOP的详细解析,我们了解到AOP是一种强大的编程范式,能够有效地解决横切关注点的问题。在Spring框架中,AOP通过切面、连接点、通知等概念实现了对关注点的统一处理。我们可以利用AOP实现日志记录、事务管理、安全性控制等功能,提高代码的可维护性和可扩展性。
注:仅作者个人知识分享,如有错误可指正!
原文始发于微信公众号(Coder香):【每日一题】Java 基础篇 – 深入理解 Spring AOP:解析面向切面编程的利器
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/217601.html