1. SpringAOP介绍
AOP即面向切面编程,切面泛指系统中与主业务无关的代码,比如安全检查、日志记录、事物等等。AOP可以让主业务逻辑代码与切面代码分别开发,在运行时再进行合并。AOP减少了代码的纠缠,使业务逻辑代码更加清晰。
术语 | 说明 |
---|---|
切面 | 切面泛指交叉业务逻辑。比如事务处理、日志处理就可以理解为切面。常用的切面有通知与顾问。实际就是对主业务逻辑的一种增强 |
织入 | 织入是指将切面代码插入到目标对象的过程。 |
连接点 | 连接点指切面可以织入的位置。 |
切入点 | 切入点指切面具体织入的位置。 |
通知(Advice) | 通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。 |
顾问(Advisor) | 顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。 不仅指定了切入时间点,还可以指定具体的切入点 |
2. 基于XML方式实现
2.1. 添加依赖
<dependencies>
<!-- Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.17.RELEASE</version>
</dependency>
<!-- Spring-Aspect切面依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- junit方便测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.2. 创建切面类
创建切面类实现不同时机的通知方法
package com.wxw.aspectj;
public class AspectJBean {
public void before(){
System.out.println("AspectJBean前置通知!!!");
}
public void afterReturning(){
System.out.println("AspectJBean后置通知!!!");
}
public void afterThrowing(){
System.out.println("AspectJBean异常通知!!!");
}
public void after(){
System.out.println("AspectJBean最终通知!!!");
}
}
2.3. 创建目标类
package com.wxw.controller;
public class UserController {
public void queryUser(){
System.out.println("【UserController】的【queryUser】方法执行了!!!");
}
}
2.4. 配置文件
<?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 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="userController" class="com.wxw.controller.UserController"></bean>
<bean id="aspectJBean" class="com.wxw.aspectj.AspectJBean"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pointcut"
expression="execution(public * com.wxw.controller.UserController.*(..)) " />
<!-- 配置切面 -->
<aop:aspect ref="aspectJBean">
<!-- 配置相应通知 -->
<aop:before pointcut-ref="pointcut" method="before" />
<aop:after-returning pointcut-ref="pointcut" method="afterReturning" />
<aop:after-throwing pointcut-ref="pointcut" method="afterThrowing"/>
<aop:after pointcut-ref="pointcut" method="after" />
</aop:aspect>
</aop:config>
</beans>
2.5. 运行测试
package com.wxw.test;
import com.wxw.controller.UserController;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDemo {
@Test
public void test01(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = applicationContext.getBean("userController", UserController.class);
userController.queryUser();
}
}
AspectJBean前置通知!!!
【UserController】的【queryUser】方法执行了!!!
AspectJBean后置通知!!!
AspectJBean最终通知!!!
进程已结束,退出代码为 0
3. 基于注解方式实现
3.1. 添加依赖
<dependencies>
<!-- Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.17.RELEASE</version>
</dependency>
<!-- Spring-Aspect切面依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- junit方便测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3.2. 创建切面类
创建切面类实现不同时机的通知方法
package com.wxw.aspectj;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect // 显示的表明当前类是一个切面类
@Component // 将该对象加载到IoC容器中
public class AspectJBean {
@Before("execution(* com.wxw.controller.UserController.*(..))")
public void before(){
System.out.println("AspectJBean前置通知!!!");
}
@AfterReturning("execution(* com.wxw.controller.UserController.*(..))")
public void afterReturning(){
System.out.println("AspectJBean后置通知!!!");
}
@AfterThrowing("execution(* com.wxw.controller.UserController.*(..))")
public void afterThrowing(){
System.out.println("AspectJBean异常通知!!!");
}
@After("execution(* com.wxw.controller.UserController.*(..))")
public void after(){
System.out.println("AspectJBean最终通知!!!");
}
}
3.3. 创建目标类
package com.wxw.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public void queryUser(){
System.out.println("【UserController】的【queryUser】方法执行了!!!");
}
}
3.4. 运行测试
package com.wxw;
import com.wxw.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class JavaConfig {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
UserController bean = applicationContext.getBean(UserController.class);
bean.queryUser();
}
}
AspectJBean前置通知!!!
【UserController】的【queryUser】方法执行了!!!
AspectJBean最终通知!!!
AspectJBean后置通知!!!
进程已结束,退出代码为 0
4. 切入点表达式
表达式类型 | 说明 |
---|---|
execution | 定位到目标对象的方法上 |
within | 定位到具体的类型上 |
this | 代理对象的类型 |
target | 目标对象的类型 |
args | 参数的类型 |
@args | 传入的参数有被该注解修饰 |
@within | 类型修饰的注解 |
@annotation | 方法修饰的注解 |
execution表达式语法: execution([访问权限类型] 返回值类型 [全限定类名] 方法名(参数名) [抛出的异常类型])
符合 | 含有 |
---|---|
* | 0到多个符合 |
… | 方法参数中表示任意个参数,用在报名后表示当前包及其子包 |
+ | 用在类名后表示当前类及其子类,用在接口后表接口及其实现 |
示例:
execution(public * *(. .))
指定切入点为:任意公共方法。
execution(* set *(. .))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(. .))
指定切入点为:定义在service包里的任意类的任意方法。
execution(* com.xyz.service. .*.*(. .))
指定切入点为:定义在service包或者子包里的任意类的任意方法。“..”出现在类名中时,
后面必须跟“*”,表示包、子包下的所有类。
execution(* *.service.*.*(. .))
指定只有一级包下的serivce子包下所有类(接口)中的所有方法为切入点
execution(* *. .service.*.*(. .))
指定所有包下的serivce子包下所有类(接口)中的所有方法为切入点
4. 通知类型
通知类型 | 说明 |
---|---|
前置通知 | 目标方法执行之前调用 |
后置通知 | 目标方法执行完成之后调用 |
环绕通知 | 目标方法执行前后都会调用方法,且能增强结果 |
异常处理通知 | 目标方法出现异常调用 |
最终通知 | 无论程序执行是否正常,该通知都会执行。类似于try…catch中finally代码块 |