在学习了Springboot的启动源码之后,了解了Spring中IOC容器创建和初始化的过程,IOC和AOP是Spring框架的两大特性,现在开始学习AOP的原理,揭开AOP的神秘面纱。
众所周知,Spring的IOC和AOP特性节省了很多程序员的开发时间,使企业级项目的开发变得更加容易。IOC的思想主要是利用反射的技术加载那些在XML配置文件中定义或者利用注解修饰的配置类和一些单例的Bean对象,并且对这些对象的依赖提供自动注入的功能,将这些对象的管理由代码转移到Spring框架,减少了程序员new对象的次数。
而AOP主要是提供了一种面向切面的编程,可以将一些与业务无关的代码与业务代码分离开来(如日志记录、权限认证、事务处理等),使程序员在编写代码的时候可以专注于业务代码的开发,并且可以将这些多个业务模块公共的方法封装成一个可重用的模块,提高代码的复用性,降低多个业务模块之间的耦合。由于这个可重用模块中的某个方法会被用于协助多个业务模块,将每个方法都想象成一个点,多个点就可以组成一个面,因此AOP又称为面向切面编程。
AOP思想使OOP(面向对象编程)思想的一种补充,传统的OOP允许定义从上到下的关系(可以在父类中编写多个子类需要的公共方法供这些子类调用),但不适合定义从左到右的关系(如果来了一个新类,这个类中的方法也是子类所需要的公共方法,如何在不修改子类代码的情况下让子类能够使用新类中的方法(闭包原则))。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“切面”,从而使得编译器可以在编译期间织入有关“切面”的代码。
Springboot使用AOP的实例
第一步:导入相关依赖
<!-- aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
第二部:创建一个业务类
@Component
public class ServiceTest {
public void voidNoneArgsPublicFunction(){
System.out.println("voidNoneArgsPublicFunction方法执行");
}
public Object objectNoneArgsPublicFunction(){
System.out.println("objectNoneArgsPublicFunction方法执行");
return null;
}
public void voidPublicFunction(String arg){
System.out.println("voidPublicFunction方法执行");
}
public Object objectPublicFunction(String arg){
System.out.println("objectPublicFunction方法执行");
return null;
}
public void voidThrowPublicFunction(String arg){
System.out.println("voidThrowPublicFunction方法执行");
throw new RuntimeException("voidThrowPublicFunction方法执行");
}
}
第三部:创建一个切面
// 切面注解
@Aspect
// 将切面注册到IOC容器中
@Component
public class AOPTest {
// 选择一个切入点,表示org.dsmserver.springtest.test.ServiceTest类下面的任意函数都为切入点,具体可以参考Pointcut语法
@Pointcut("execution(* org.dsmserver.springtest.test.ServiceTest.*(..))")
public void LogAspect(){}
// 前置通知
@Before("LogAspect()")
public void doBefore(JoinPoint joinPoint){
System.out.println(joinPoint.toShortString() + "的前置通知");
}
// 不带返回值函数的后置通知
@After("LogAspect()")
public void doAfter(JoinPoint joinPoint){
System.out.println(joinPoint.toShortString() + "的无返回后置通知");
}
// 带返回值函数的后置通知
@AfterReturning("LogAspect()")
public void doAfterReturning(JoinPoint joinPoint){
System.out.println(joinPoint.toShortString() + "的带返回后置通知");
}
// 抛出异常后的后置通知
@AfterThrowing("LogAspect()")
public void deAfterThrowing(JoinPoint joinPoint){
System.out.println(joinPoint.toShortString() + "的异常后置通知");
}
// 环绕通知
@Around("LogAspect()")
public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println(joinPoint.toString() + "的环绕通知");
return joinPoint.proceed();
}
}
第四步:调用ServiceTest类中的方法
@SpringBootTest
class SpringtestApplicationTests {
@Autowired
ServiceTest serviceTest;
@Test
void contextLoads() {
System.out.println("----------------------------------------------");
serviceTest.voidNoneArgsPublicFunction();
System.out.println("----------------------------------------------");
serviceTest.objectNoneArgsPublicFunction();
System.out.println("----------------------------------------------");
serviceTest.objectPublicFunction(null);
System.out.println("----------------------------------------------");
serviceTest.voidPublicFunction(null);
System.out.println("----------------------------------------------");
serviceTest.voidThrowPublicFunction(null);
System.out.println("----------------------------------------------");
}
}
运行结果如下图所示:
----------------------------------------------
execution(ServiceTest.voidNoneArgsPublicFunction())的环绕通知
execution(ServiceTest.voidNoneArgsPublicFunction())的前置通知
voidNoneArgsPublicFunction方法执行
execution(ServiceTest.voidNoneArgsPublicFunction())的带返回后置通知
execution(ServiceTest.voidNoneArgsPublicFunction())的无返回后置通知
----------------------------------------------
execution(ServiceTest.objectNoneArgsPublicFunction())的环绕通知
execution(ServiceTest.objectNoneArgsPublicFunction())的前置通知
objectNoneArgsPublicFunction方法执行
execution(ServiceTest.objectNoneArgsPublicFunction())的带返回后置通知
execution(ServiceTest.objectNoneArgsPublicFunction())的无返回后置通知
----------------------------------------------
execution(ServiceTest.objectPublicFunction(..))的环绕通知
execution(ServiceTest.objectPublicFunction(..))的前置通知
objectPublicFunction方法执行
execution(ServiceTest.objectPublicFunction(..))的带返回后置通知
execution(ServiceTest.objectPublicFunction(..))的无返回后置通知
----------------------------------------------
execution(ServiceTest.voidPublicFunction(..))的环绕通知
execution(ServiceTest.voidPublicFunction(..))的前置通知
voidPublicFunction方法执行
execution(ServiceTest.voidPublicFunction(..))的带返回后置通知
execution(ServiceTest.voidPublicFunction(..))的无返回后置通知
----------------------------------------------
execution(ServiceTest.voidThrowPublicFunction(..))的环绕通知
execution(ServiceTest.voidThrowPublicFunction(..))的前置通知
voidThrowPublicFunction方法执行
execution(ServiceTest.voidThrowPublicFunction(..))的异常后置通知
execution(ServiceTest.voidThrowPublicFunction(..))的无返回后置通知
java.lang.RuntimeException: voidThrowPublicFunction方法执行
从结果中可以看出,最先执行的是环绕通知,环绕通知中必须执行返回joinPoint.proceed();的执行结果,若将joinPoint.proceed();换成null;则会出现下面的情况:
----------------------------------------------
execution(ServiceTest.voidNoneArgsPublicFunction())的环绕通知
----------------------------------------------
execution(ServiceTest.objectNoneArgsPublicFunction())的环绕通知
----------------------------------------------
execution(ServiceTest.objectPublicFunction(..))的环绕通知
----------------------------------------------
execution(ServiceTest.voidPublicFunction(..))的环绕通知
----------------------------------------------
execution(ServiceTest.voidThrowPublicFunction(..))的环绕通知
----------------------------------------------
从结果中可以看出不仅函数没有执行,前置通知也没有执行。因此环绕通知可以控制目标方法的执行,这也是环绕通知和前置、后置通知最大的区别。