4-1-2 Spring基础-Spring AOP面向切面编程
什么是Spring AOP
Aspect Oriented Programming 面向切面编程
AOP的做法是将通用,与业务无关的功能抽象封装为切面类
切面可配置在目标方法执行前,后运行,真正做到即插即用
在不修改源码的情况下对程序行为进行修改
一个AOP的Demo
pom文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>com.mingm.spring</groupId> 8 <artifactId>aop</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 <repositories> 11 <repository> 12 <id>aliyun</id> 13 <name>aliyun</name> 14 <url>https://maven.aliyun.com/repository/public</url> 15 </repository> 16 </repositories> 17 <dependencies> 18 <dependency> 19 <groupId>org.springframework</groupId> 20 <artifactId>spring-context</artifactId> 21 <version>5.2.6.RELEASE</version> 22 </dependency> 23 <!--aspectjweaver是Spring AOP的底层依赖--> 24 <dependency> 25 <groupId>org.aspectj</groupId> 26 <artifactId>aspectjweaver</artifactId> 27 <version>1.9.5</version> 28 </dependency> 29 </dependencies> 30 </project>
applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:context="http://www.springframework.org/schema/context" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns="http://www.springframework.org/schema/beans" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop.xsd"> 12 <bean id="userDao" class="com.mingm.spring.aop.dao.UserDao"/> 13 <bean id="employeeDao" class="com.mingm.spring.aop.dao.EmployeeDao"/> 14 <bean id="userService" class="com.mingm.spring.aop.service.UserService"> 15 <property name="userDao" ref="userDao"/> 16 </bean> 17 <bean id="employeeService" class="com.mingm.spring.aop.service.EmployeeService"> 18 <property name="employeeDao" ref="employeeDao"/> 19 </bean> 20 <!-- AOP配置 --> 21 <bean id="methodAspect" class="com.mingm.spring.aop.aspect.MethodAspect"></bean> 22 <aop:config> 23 <!-- PointCut 切点,使用execution表达式描述切面的作用范围 --> 24 <!-- execution(public * com.mingm..*.*(..)) 说明切面作用在com.mingm包下的所有类的所有方法上 --> 25 <!--<aop:pointcut id="pointcut" expression="execution(public * com.mingm..*.*(..))"></aop:pointcut>--> 26 <!--只对所有Service类生效--> 27 <aop:pointcut id="pointcut" expression="execution(* com.mingm..*Service.*(..))"></aop:pointcut> 28 <!--只对所有返回值为String类型方法生效--> 29 <!--<aop:pointcut id="pointcut" expression="execution(String com.mingm..*Service.*(..))"></aop:pointcut>--> 30 <!--对方法名进行约束 --> 31 <!--<aop:pointcut id="pointcut" expression="execution(* com.mingm..*Service.create*(..))"></aop:pointcut>--> 32 <!-- 对参数进行约束 --> 33 <!--<aop:pointcut id="pointcut" expression="execution(* com.mingm..*Service.*(String,*))"></aop:pointcut>--> 34 <!-- 定义切面类 --> 35 <aop:aspect ref="methodAspect"> 36 <!-- before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime() --> 37 <aop:before method="printExecutionTime" pointcut-ref="pointcut"/> 38 <aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/> 39 <aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/> 40 <aop:after method="doAfter" pointcut-ref="pointcut"></aop:after> 41 </aop:aspect> 42 </aop:config> 43 </beans>
SpringApplication.java
1 package com.mingm.spring.aop; 2 3 import com.mingm.spring.aop.service.UserService; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 public class SpringApplication { 8 public static void main(String[] args) { 9 ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 10 UserService userService = context.getBean("userService", UserService.class); 11 userService.createUser(); 12 userService.generateRandomPassword("MD5", 16); 13 } 14 }
MethodAspect.java
1 package com.mingm.spring.aop.aspect; 2 3 import org.aspectj.lang.JoinPoint; 4 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 8 //切面类 9 public class MethodAspect { 10 //切面方法,用于扩展额外功能 11 //JoinPoint 连接点,通过连接点可以获取目标类/方法的信息 12 public void printExecutionTime(JoinPoint joinPoint){ 13 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); 14 String now = sdf.format(new Date()); 15 String className = joinPoint.getTarget().getClass().getName();//获取目标类的名称 16 String methodName = joinPoint.getSignature().getName();//获取目标方法名称 17 System.out.println("---->" + now + ":" + className + "." + methodName); 18 Object[] args = joinPoint.getArgs(); 19 System.out.println("---->参数个数:" + args.length); 20 for(Object arg:args){ 21 System.out.println("---->参数:" + arg); 22 } 23 } 24 25 public void doAfterReturning(JoinPoint joinPoint,Object ret){ 26 System.out.println("<----返回后通知:" + ret); 27 } 28 public void doAfterThrowing(JoinPoint joinPoint,Throwable th){ 29 System.out.println("<----异常通知:" + th.getMessage()); 30 } 31 public void doAfter(JoinPoint joinPoint){ 32 System.out.println("<----触发后置通知"); 33 } 34 }
AOP配置过程
1.依赖AspectJ
2.实现切面类/方法
3.胚子Aspect Bean
4.定义PointCut
5.配置Advice通知
Spring AOP 与AspectJ的关系
Eclipse AspectJ,一种基于Java平台的面向切面编程的语言
Spring AOP使用AspectJWeavear实现类与方法匹配
Spring AOP利用代理模式实现对象运行时功能扩展
Spring AOP相关概念
| 注解 | 说明 |
| Aspect | 切面,具体的可插拔组件功能类,通常一个切面只实现一个通用功能 |
| Target Class/Method | 目标类,目标方法,指真正要执行与业务相关的方法 |
| PointCut | 切入点,使用execution表达式说明切面要作用在系统的哪些类上 |
| JoinPoint | 连接点,切面运行过程中包含了目标类/方法元数据的对象 |
| Adivice | 通知,说明具体的切面的执行时机,Spring包含五种不同类型通知 |
JoinPoint核心方法
| 注解 | 说明 |
| Object getTarget() | 获取IoC容器目标对象 |
| Signature getSignature() | 获取目标方法 |
| Object[] getArgs() | 获取目标方法参数 |
PointCut切点表达式

* - 通配符
.. - 包通配符
(..) - 参数通配符
示例:
只对所有Service类生效: execution(* com.mingm..*Service.*(..))
只对所有返回值为String类型方法生效: execution(String com.mingm..*Service.*(..))
对方法名进行约束: execution(* com.mingm..*Service.create*(..))
对参数进行约束: execution(* com.mingm..*Service.*(String,*))
AOP通知
五种通知类型
| 注解 | 说明 |
| Before | 前置通知,目标方法运行前执行 |
| After Returning Advice | 返回后通知,目标方法返回数据后执行 |
| After Throwing Advice | 异常通知,目标方法抛出异常后执行 |
| After Advice | 后置通知,目标方法运行后执行 |
| Around Advice | 最强大通知,自定义通知执行时机,可决定目标方法是否执行 |
特殊的"通知"-引介增强
引介增强是对类的增强,而非方法
本质是拦截器
引介增强允许在运行时为目标类增加新属性或方法
引介增强允许在运行时改变类的行为,让类随运行环境动态变更
后置通知和返回后通知执行顺序
由applicationContext.xml配置顺序决定
Demo:利用AOP方法进行性能筛选
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>com.mingm.spring</groupId> 8 <artifactId>aop</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 <repositories> 11 <repository> 12 <id>aliyun</id> 13 <name>aliyun</name> 14 <url>https://maven.aliyun.com/repository/public</url> 15 </repository> 16 </repositories> 17 <dependencies> 18 <dependency> 19 <groupId>org.springframework</groupId> 20 <artifactId>spring-context</artifactId> 21 <version>5.2.6.RELEASE</version> 22 </dependency> 23 <dependency> 24 <groupId>org.aspectj</groupId> 25 <artifactId>aspectjweaver</artifactId> 26 <version>1.9.5</version> 27 </dependency> 28 </dependencies> 29 </project>
MethodChecker.java
package com.mingm.spring.aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MethodChecker {
////ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
long startTime = new Date().getTime();
Object ret = pjp.proceed();//执行目标方法
long endTime = new Date().getTime();
long duration = endTime - startTime; //执行时长
if(duration >= 1000){
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
}
return ret;
} catch (Throwable throwable) {
System.out.println("Exception message:" + throwable.getMessage());
throw throwable;
}
}
}
applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:context="http://www.springframework.org/schema/context" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns="http://www.springframework.org/schema/beans" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop.xsd"> 12 <bean id="userDao" class="com.mingm.spring.aop.dao.UserDao"/> 13 <bean id="employeeDao" class="com.mingm.spring.aop.dao.EmployeeDao"/> 14 <bean id="userService" class="com.mingm.spring.aop.service.UserService"> 15 <property name="userDao" ref="userDao"/> 16 </bean> 17 <bean id="employeeService" class="com.mingm.spring.aop.service.EmployeeService"> 18 <property name="employeeDao" ref="employeeDao"/> 19 </bean> 20 21 <bean id="methodChecker" class="com.mingm.spring.aop.aspect.MethodChecker"></bean> 22 <aop:config> 23 <aop:pointcut id="pointcut" expression="execution(* com.mingm..*.*(..))"></aop:pointcut> 24 <aop:aspect ref="methodChecker"> 25 <!--环绕通知--> 26 <aop:around method="check" pointcut-ref="pointcut"/> 27 </aop:aspect> 28 </aop:config> 29 </beans>
SpringApplication.java
1 package com.mingm.spring.aop; 2 3 import com.mingm.spring.aop.service.UserService; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 public class SpringApplication { 8 public static void main(String[] args) { 9 ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 10 UserService userService = context.getBean("userService", UserService.class); 11 userService.createUser(); 12 } 13 }
利用注解开发Spring AOP
将利用AOP方法进行性能筛选demo改造为注解开发
applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:context="http://www.springframework.org/schema/context" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns="http://www.springframework.org/schema/beans" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop.xsd"> 12 <!--初始化IoC容器--> 13 <context:component-scan base-package="com.imooc"/> 14 <!--启用Spring AOP注解模式--> 15 <aop:aspectj-autoproxy/> 16 </beans>
MethodChecker.java
1 package com.imooc.spring.aop.aspect; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.*; 5 import org.springframework.stereotype.Component; 6 7 import java.text.SimpleDateFormat; 8 import java.util.Date; 9 @Component //标记当前类为组件 10 @Aspect //说明当前类是切面类 11 public class MethodChecker { 12 //环绕通知,参数为PointCut切点表达式 13 @Around("execution(* com.imooc..*Service.*(..))") 14 //ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行 15 public Object check(ProceedingJoinPoint pjp) throws Throwable { 16 try { 17 long startTime = new Date().getTime(); 18 Object ret = pjp.proceed();//执行目标方法 19 long endTime = new Date().getTime(); 20 long duration = endTime - startTime; //执行时长 21 if(duration >= 1000){ 22 String className = pjp.getTarget().getClass().getName(); 23 String methodName = pjp.getSignature().getName(); 24 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); 25 String now = sdf.format(new Date()); 26 System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======"); 27 } 28 return ret; 29 } catch (Throwable throwable) { 30 System.out.println("Exception message:" + throwable.getMessage()); 31 throw throwable; 32 } 33 } 34 }
Spring AOP实现原理
Spring基于代理模式实现功能动态扩展,包含两种形式:
目标类拥有接口,通过JDK动态代理实现功能扩展
目标类没有接口,通过CGlib组件实现功能扩展
代理模式
代理模式通过代理对象对原对象实现功能扩展

静态代理
代理类和委托类都要实现相同的接口
接口
1 public interface UserService { 2 public void createUser(); 3 }
委托类
1 public class UserServiceImpl implements UserService{ 2 public void createUser() { 3 System.out.println("执行创建用户业务逻辑"); 4 } 5 }
代理类
1 import java.text.SimpleDateFormat; 2 import java.util.Date; 3 //静态代理是指必须手动创建代理类的代理模式使用方式 4 public class UserServiceProxy implements UserService{ 5 //持有委托类的对象 6 private UserService userService ; 7 public UserServiceProxy(UserService userService){ 8 this.userService = userService; 9 } 10 11 public void createUser() { 12 System.out.println("=====" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"========="); 13 userService.createUser(); 14 } 15 }
JDK动态代理
1 /** 2 * InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强 3 * InvocationHandler实现类与切面类的环绕通知类似 4 */ 5 public class ProxyInvocationHandler implements InvocationHandler { 6 private Object target;//目标对象 7 private ProxyInvocationHandler(Object target){ 8 this.target = target; 9 } 10 /** 11 * 在invoke()方法对目标方法进行增强 12 * @param proxy 代理类对象 13 * @param method 目标方法对象 14 * @param args 目标方法实参 15 * @return 目标方法运行后返回值 16 * @throws Throwable 目标方法抛出的异常 17 */ 18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 19 System.out.println("=====" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"========="); 20 Object ret = method.invoke(target, args);//调用目标方法,ProceedingJoinPoint.proceed() 21 return ret; 22 } 23 24 public static void main(String[] args) { 25 UserService userService = new UserServiceImpl(); 26 ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService); 27 //动态创建代理类 28 UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(), 29 userService.getClass().getInterfaces(), 30 invocationHandler); 31 userServiceProxy.createUser(); 32 33 //动态代理,必须实现接口才可以运行 34 EmployeeService employeeService = new EmployeeServiceImpl(); 35 EmployeeService employeeServiceProxy = (EmployeeService)Proxy.newProxyInstance(employeeService.getClass().getClassLoader(), 36 employeeService.getClass().getInterfaces(), 37 new ProxyInvocationHandler(employeeService)); 38 employeeServiceProxy.createEmployee(); 39 } 40 }
CGlib实现代理类
CGlib是运行时字节码增强技术
Spring AOP扩展无接口类使用CGlib
AOP会运行时生成目标继承类字节码的方式进行扩展
CGlib执行过程
1.生成代理类的二进制字节码文件
2.加载二进制字节码,生成Class对象(Class.forName())
3.通过反射机制获取实例构造,并创建代理类对象

浙公网安备 33010602011771号