一、AOP
- APO即Aspect Oriented Programing的缩写,字面直译的意思就是:面向切面编程。
- AOP采用横向抽取机制,取代了传统的纵向继承体系中的重复性代码,是对OOP面向对象编程的一种补充和完善。AOP最重要或者说要达到的目的就是开发者在不修改不侵入源代码的前提下,给系统中的业务组件添加某种通用的功能。比如现在要统计系统中service层的所有方法的执行时间,如果使用面向对象的话只能在每个方法前后手动编写一段代码打印开始结束时间,来计算每个方法的执行时间;如果使用AOP就可以把计算时间的代码抽取出来,使用切面来在目标方法执行前后统计时间,不需要修改原来方法的源代码,方便了很多。
- Spring AOP底层是通过JDK动态代理或者CGlib动态代理实现的。
类型 | 原理 | 什么情况下使用 | 注意 |
---|---|---|---|
JDK动态代理 | 在运行时期,目标类加载后,为接口动态生成代理类,将切面织入到代理类中 | 目标对象实现了接口,spring使用JDK的java.lang.reflect.Proxy类代理 | 接口中的方法不能使用final修饰 |
CGlib动态代理 | 动态生成目标类的子类,将切面织入到子类中 | 目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类 | 类或方法不能使用final修饰 |
二、AOP术语
- Target:目标对象,被代理的对象;
- Join Point:连接点,目标对象中要代理的方法;
- PointCut:切入点,实际上被代理的方法;
- Advice:通知,在切入点之前或者之后执行的方法,也是切面要完成的实际功能,(通知分为:前置通知、后置通知、异常通知、最终通知、环绕通知);
- Aspect:切面,是对物体特征的抽象;
- Introduction:引入,在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段;
- Weaving:织入,将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
三、XML方式使用AOP
1、创建UserService接口和UserServiceImpl实现类,UserServiceImpl就是我们要代理的类,即Target。
package com.aop;
// User接口
public interface UserService {
String addUser(String name);
String updateUser();
}
package com.aop;
// 实现类
public class UserServiceImpl implements UserService {
@Override
public String addUser(String name) {
System.out.println("addUser...."+ name);
return "添加user成功";
}
@Override
public String updateUser() {
System.out.println("updateUser....");
return "修改user成功";
}
}
2、创建MyAspect类,MyAspect就是我们的切面
package com.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
// 切面类
public class MyAspect {
// Advice:前置通知
public void myBefore(JoinPoint joinPoint){
// 可以通过joinPoint获取代理对象和方法的相关信息
/*System.out.println("args:" + joinPoint.getArgs());
System.out.println("kind:" + joinPoint.getKind());
System.out.println("signature:" + joinPoint.getSignature());
System.out.println("sourceLocation:" + joinPoint.getSourceLocation());
System.out.println("staticPart:" + joinPoint.getStaticPart());
System.out.println("target:" + joinPoint.getTarget());
System.out.println("this:" + joinPoint.getThis());*/
System.out.println("目标方法执行前执行");
}
// Advice:后置通知
public void myAfter(JoinPoint joinPoint){
System.out.println("目标方法执行后执行");
}
// Advice:环绕通知
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("环绕之前:"+ joinPoint.getSignature().getName());
// 必须自己手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("环绕之后:"+ joinPoint.getSignature().getName());
return obj;
}
// Advice:异常抛出通知
public void myThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常抛出:"+ joinPoint.getSignature().getName() + ", 异常信息" + e.getMessage());
}
// Advice:最终通知
public void myFinal(JoinPoint joinPoint){
System.out.println("最终:"+ joinPoint.getSignature().getName());
}
}
3、在resources创建beans.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<!-- 1 配置目标 -->
<bean id="userService" class="com.aop.UserServiceImpl"></bean>
<!-- 2 配置切面类 -->
<bean id="myAspect" class="com.aop.MyAspect"></bean>
<!-- 3 aspect配置 -->
<aop:config>
<aop:aspect ref="myAspect">
<!-- 3.1 确定切入点 -->
<aop:pointcut id="myPointCut" expression="execution(* com.aop..*.*(..))" />
<!-- 3.2 通知 -->
<!-- 3.2.1 前置通知-->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!-- 3.2.2 后置, 可以获得目标方法的返回值
* 可以通过 returning属性配置 后置通知方法的第二个参数的名称,类型为Object
<aop:after-returning returning="returnValue"/>
public void myAfterReturning(JoinPoint joinPoint, Object returnValue){}
<aop:after-returning method="myAfter" pointcut-ref="myPointCut"/>-->
<!-- 3.2.3 环绕 ,必须手动执行目标方法,且需要将执行结果进行返回。
参数类型必须是:ProceedingJoinPoint
<aop:around method="myAround" pointcut-ref="myPointCut"/>
-->
<!-- 3.2.4 异常 ,当程序出现异常时,才执行。
* 通过 throwing 属性设置 抛出异常通知方法的第二个参数的参数名称,类型 throwable
<aop:after-throwing throwing="e"/>
public void myAfterThrowing(JoinPoint joinPoint,Throwable e)
<aop:after-throwing method="myThrowing" pointcut-ref="myPointCut" throwing="e"/>
-->
<!-- 3.2.5 最终通知 ,正常程序执行或出现异常,都将执行
<aop:after method="myFinal" pointcut-ref="myPointCut"/>-->
</aop:aspect>
</aop:config>
</beans>
4、测试
package com.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
String xmlPath = "beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 获得配置的目标类,实际上是拿到spring自动生成的代理类
UserService userService3 = (UserService) applicationContext.getBean("userService");
userService3.addUser("张三");
userService3.updateUser();
}
}
四、注解方式使用切面
@Before:前置通知
@AfterReturning:后置通知
@Around:环绕通知
@AfterThrowing 抛出
@After:最终通知
@PointCunt:统一管理表达式
@Aspect:表示自己是切面类
package com.aop_annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
// 切面类
@Aspect
@Component
public class MyAspect {
@Pointcut(value = "execution(* com.aop..*.*(..))")
private void myExce(){}
// Advice:前置通知
@Before("myExce()")
public void myBefore(JoinPoint joinPoint){
// 可以通过joinPoint获取代理对象和方法的相关信息
/*System.out.println("args:" + joinPoint.getArgs());
System.out.println("kind:" + joinPoint.getKind());
System.out.println("signature:" + joinPoint.getSignature());
System.out.println("sourceLocation:" + joinPoint.getSourceLocation());
System.out.println("staticPart:" + joinPoint.getStaticPart());
System.out.println("target:" + joinPoint.getTarget());
System.out.println("this:" + joinPoint.getThis());*/
System.out.println("目标方法执行前执行");
}
// Advice:后置通知
@AfterReturning("myExce()")
public void myAfter(JoinPoint joinPoint){
System.out.println("目标方法执行后执行");
}
// Advice:环绕通知
@Around("myExce()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("环绕之前:"+ joinPoint.getSignature().getName());
// 必须自己手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("环绕之后:"+ joinPoint.getSignature().getName());
return obj;
}
// Advice:异常抛出通知
@AfterThrowing(value = "myExce()", throwing = "e")
public void myThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常抛出:"+ joinPoint.getSignature().getName() + ", 异常信息" + e.getMessage());
}
// Advice:最终通知
@After("myExce()")
public void myFinal(JoinPoint joinPoint){
System.out.println("最终:"+ joinPoint.getSignature().getName());
}
}