目录
想要彻底理解AOP,我觉得你的先要了解框架的模块化思想,为此先记录框架在讲AOP
什么是java框架?为什么要出现框架?
我总结以下七点来讲述和帮助理解java框架思想
什么是AOP?
如何理解上面这句话呢?
最重要的来了,什么是面向切面编程?
我们现在将横向的关注点剥离成了一个一个的切面,呢我们如何用代码把切面织入进去呢?
呢么现在就有了新的问题:什么是AspectJ框架?他在AOP编程中起到了什么作用?
知道了概念之后我们开始使用
Spring AOP的实现原理
最后概念化AOP
想要彻底理解AOP,我觉得你的先要了解框架的模块化思想,为此先记录框架在讲AOP
什么是java框架?为什么要出现框架?
在我开始接触java框架知识后,我发现所谓的java框架就是把原本的一个整体分成了一小块一小块的部分,有种模块化思想,就是将分工变得更加明确,专业的人负责专门的事,让程序员更加专注于业务本身,将什么事务操作、日志操作抽取出去,模块化成一个小整体去专门负责做,然后将这个小整体做好后,像堆积木一样堆到事务该出现的位置,然后让一块一块的小积木堆成一个搭积;
我总结以下七点来讲述和帮助理解java框架思想
-
模块化思想: 框架通常采用模块化设计,将系统拆分成独立的功能模块。每个模块专注于特定的功能或关注点,如数据库访问、事务管理、日志记录、安全性等。这种模块化思想使得系统更易于理解、扩展和维护。
-
分工明确: 各个模块由专业的开发者或团队负责,这样可以充分发挥每个人的专业技能。例如,数据库模块由专门的数据库开发者负责,日志模块由专门的日志开发者负责。这种分工使得开发过程更加高效,同时降低了系统的复杂性。
-
抽象和封装: 框架通过抽象和封装隐藏了底层的复杂性,提供了高层次的接口供开发者使用。这样,开发者在使用框架时不需要关心底层的实现细节,只需关注业务逻辑的实现。这种抽象和封装有助于提高代码的可读性和可维护性。
-
面向切面编程(AOP): AOP 是一种重要的框架设计思想,它允许将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来。例如,事务管理、安全性、日志记录等关注点可以被模块化成切面,然后通过AOP织入到业务逻辑中。这使得系统的关注点更加清晰,同时避免了代码重复。
-
依赖注入(DI): 框架通常使用依赖注入来管理组件之间的依赖关系。这样,开发者不需要手动创建和管理对象之间的关系,而是由框架负责。DI提高了代码的灵活性和可测试性。
-
约定优于配置(Convention over Configuration): 框架倡导使用一些默认约定,减少配置的复杂性。通过约定,开发者只需要配置那些与默认约定不一致的部分,减少了样板代码的编写,提高了开发效率。
-
开放封闭原则: 框架遵循开放封闭原则,允许系统扩展而不需要修改已有的代码。新的功能通过扩展现有的模块或添加新的模块来实现,而不是修改已有的代码。这种原则有助于保持系统的稳定性和可维护性。
框架就是为了让一个复杂代码的逻辑更加清晰,分工更加明确;
什么是AOP?
Spring有两个极其重要的核心概念,一个是IOC/DI
,另一个就是AOP
。呢么什么是AOP呢?刚刚已经总结过了,AOP 是一种重要的框架设计思想,它允许将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来。例如,事务管理、安全性、日志记录等关注点可以被模块化成切面,然后通过AOP织入到业务逻辑中(就好比搭积木)。这使得系统的关注点更加清晰,还增加了代码的复用性。
如何理解上面这句话呢?
假设将我们编写的业务代码看成一个搭积木的过程,我们将交叉业务看成一个个小积木,当我们做完核心业务代码之后,然后将做好的小积木像搭积木一样,加入已经做好的小积木块,这样就避免了我们程序员在编写主要业务逻辑时,还要考虑数据库事务啊,访问数据库啊等,其他和业务无关的逻辑,让我们程序员负责写订单生成的就只负责写订单生成,负责写数据库访问的就只负责写数据库访问,使的分工明确,结构清晰,最后拼在一起即可构成高楼大厦;
这里的积木与积木之间是否能够拼在一起就能正常运行就涉及到解耦合思想了;
看上面这张图,我们将业务代码看成纵向的一个过程,将和业务主要代码无关的代码,如事务管理、安全性、日志记录等关注点可以被模块化成小积木块,等需要这些的时候,像刀一样横向的插入进去,然而能够把一个整体抽象成模块化的思想就是AOP的主要思想;
最重要的来了,什么是面向切面编程?
我们把事务管理、安全性、日志记录等和主从业务无关的关注点看成一个一个的切面,将他们从主要业务逻辑中分离出来。然后在需要用到他们的地方再将他们织入进去,当然这需要完全的解耦合;
我们现在将横向的关注点剥离成了一个一个的切面,呢我们如何用代码把切面织入进去呢?
呢我们就要说到spring对AOP的实现技术,AspectJ框架,在实际开发中,都是Spring+AspectJ来实现AOP。
常用的Spring对AOP的实现包括以下2种方式:
- 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
- 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
依赖如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0-M2</version>
</dependency>
呢么现在就有了新的问题:什么是AspectJ框架?他在AOP编程中起到了什么作用?
AspectJ是一个基于Java的面向切面编程(AOP)框架,它提供了一种强大的方式来实现横切关注点的模块化。我们刚刚不是已经将关注点剥离成了切面吗?呢JVM怎么知道你就是切面呢,他怎么知道这个切面要放到哪里呢,还有他怎么就知道代码执行到一半还要先执行切面呢?AspectJ的作用就出来了;
在AOP中,AspectJ主要起到以下作用:
-
切面定义: AspectJ允许开发者定义切面,切面是横切关注点的模块化单元。切面通常包含一组通知(advice)以及切点(pointcut)的定义。
-
通知(Advice): 通知是切面中的代码块,它定义了在目标方法的哪个时机执行。AspectJ支持五种类型的通知:前置通知(Before advice)、后置通知(After advice)、返回通知(After-returning advice)、异常通知(After-throwing advice)和环绕通知(Around advice)。
-
切点(Pointcut): 切点定义了在目标类中的哪些位置执行通知。AspectJ允许开发者使用表达式来定义切点,从而选择性地将通知应用到目标类的特定方法或代码块。
-
连接点(Join point): 连接点是在程序执行过程中可以插入切面的点。AspectJ将切点看作连接点,并在连接点上执行相应的通知。
-
织入(Weaving): 织入是将切面与目标类或对象关联的过程。AspectJ使用编译时织入、类装载时织入和运行时织入等方式来将切面织入到目标代码中。
-
增强(Introduction): AspectJ允许通过引入(Introduction)在不修改目标类代码的情况下为类添加新的方法或字段。这对于为已有的类添加新的行为非常有用。
-
类型匹配和织入控制: AspectJ提供了丰富的类型匹配和织入控制的功能,允许开发者精确地指定切面的应用范围。
总体而言,AspectJ通过定义切面、通知、切点等概念,使得开发者能够以一种更模块化和清晰的方式处理横切关注点。通过AspectJ,可以将与业务逻辑无关的关注点(如日志、事务、安全性等)独立出来,使得代码更易维护、扩展和理解。
知道了概念之后我们开始使用
我们引入一个案例:一个订单生成业务,我们需要再订单生成方法执行前织入一个切面MyAspect ,添加一些增强代码:
// 目标类
@service
public class OrderService {
// 目标方法
public void generate(){
System.out.println("订单已生成!");
}
}
切面类:
// 切面类
@Aspect
@Component
public class MyAspect {
// 切点表达式
@Before("execution(* com.lypnode.spring.service.OrderService.*(..))")
// 这就是需要增强的代码(通知)
public void advice(){
System.out.println("我是一个通知");
}
}
使用了AspectJ框架中的@Aspect这个注解之后,就表明这个类是个切面类,
spring.xml文件:
<?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.lypnode.spring.service"/>
<!--开启自动代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
<aop:aspectj-autoproxy proxy-target-class="true"/> 开启自动代理之后,凡事带有@Aspect注解的bean都会生成代理对象。
proxy-target-class="true" 表示采用cglib动态代理。
proxy-target-class="false" 表示采用jdk动态代理。默认值是false。即使写成false,当没有接口的时候,也会自动选择cglib生成代理类。
关于cglib动态代理和jdk动态代理下面我们会说:
测试类:
public class AOPTest {
@Test
public void testAOP(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.generate();
}
}
运行结果:
通过上面的总结和代码我么可以知道,AOP底层是调用了动态代理技术的,呢么就会生成代理类,可见上面的代码中,看似在调用orderService类,实则调用的是动态生成的代理类
至于代理类生成看我另一篇文章:
【Spring篇】JDK动态代理-CSDN博客
Spring AOP的实现原理
Spring的AOP实现原理其实很简单,就是通过动态代理实现的。Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理。
JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口
CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理。
Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理。当然你也可以自己制定;
proxy-target-class="true" 表示采用cglib动态代理。
proxy-target-class="false" 表示采用jdk动态代理。默认值是false。即使写成false,当没有接口的时候,也会自动选择cglib生成代理类。
最后概念化AOP
AOP 是一种重要的框架设计思想,动态代理是其实现机制之一。在 AOP 中,可以通过动态代理将横切关注点与核心业务逻辑分离,使代码更易维护和理解。它允许将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来。例如,事务管理、安全性、日志记录等关注点可以被模块化成切面,然后通过AOP织入到业务逻辑中。这使得系统的关注点更加清晰,同时避免了代码重复。
AOP可以拦截指定的方法并且对方法进行增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如数据库访问、事务管理、日志记录、安全性等。这种模块化思想使得系统更易于理解、扩展和维护。
将系统拆分成独立的功能模块。每个模块专注于特定的功能或关注点,如数据库访问、事务管理、日志记录、安全性等。这种模块化思想使得系统更易于理解、扩展和维护。
这是我对AOP面向切面编程的初步理解,内容有点复杂,逻辑有点乱,后续还会修改文章;