一、什么是AOP
AOP全称为Aspec Oriented Programming,译为面向切面编程。通过预编译方式和运作期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程衍生规型。利用AOP可以对业务逻辑的各个部分进行隔离,使业务各部分的逻辑耦合度降低,大大提高程序的可重用性,同时提高程序的开发效率
二、AOP的实现原理
AOP底层采用代理机制实现,接口+实现类的代理spring采用JDK的动态代理,实现类的代理spring采用CGlib字节码增强代理。(详情见Spring基础学习五、六)
三、AOP术语:
targe:目标类,指需要代理的类
Joinpoint:所谓的连接点是指那些可能被拦截到的方法,根据具体业务实现,理论上所有方法都可以是连接点
PointCut:切入点,指已经被增强的链接点
advice:通知/增强,增强代码,指在执行目标类方法前后环绕等增加的事物逻辑
Weaving(织入):指把增强advice应用到目标对象target来创建新的代理对象proxy的过程
proxy:代理类
Aspect:切面,是切入点pointCut和通知advice的结合
切入点表达式:execution()用于描述方法
语法:execution(修饰符 返回值 包.类.方法名(..)throws异常)
修饰符——一般省略 public表示公共方法,*表示任意
返回值——不能省略 void表示 没有返回值,String表示返回字符串,*表示任意
包——不能省略 例如:com.seehope.cnm 表示指定固定包 com.seehope.cnm.*.service 表示cnm包下任意子包的service子包 com.seehope.cnm.*.service.. (注意有两个点)表示cnm包下任意子包,固定目录service,service目录下任意包(包括自己)
类——不能省略 例如:UserServiceImpl 表示指定类 *Impl 表示以Impl结尾的类 User* 表示以User开头的类
方法名——不能省略 例如:addUser 表示指定方法 *Do 表示以Do结尾的方法 add* 表示以add开头的方法
参数——不能省略 ()表示无参 (int)表示一个整形 (int,int)表示两个整形 (..)表示参数任意
throws异常,可省略,一般不写
例如:execution(* com.seehope.cnm.*.service..*.*(..)) 表示返回值任意,cnm任意子包下的service包的任意子包的所有类的所有方法,参数任意
AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
• 前置通知 org.springframework.aop.MethodBeforeAdvice 在目标方法执行前实施增强
• 后置通知 org.springframework.aop.AfterReturningAdvice 在目标方法执行后实施增强
• 环绕通知 org.aopalliance.intercept.MethodInterceptor 在目标方法执行前后实施增强,功能强大,基本能实现所有功能,特别标记
• 异常抛出通知 org.springframework.aop.ThrowsAdvice 在方法抛出异常后实施增强
• 引介通知 org.springframework.aop.IntroductionInterceptor 在目标类中添加一些新的方法和属性
用环绕通知实现其他通知的方法:
环绕通知,必须手动执行目标方法
try{
//前置通知
//执行目标方法
//后置通知
} catch(){
//抛出异常通知
}本文只讲述SpringAOP全自动的配置方式,即 从spring容器获得目标类,如果配置aop,spring将自动生成代理。
在下一篇博文中将介绍AspectJ配置AOP的两种方式,AspectJ是基于Java语言的AOP框架,更为常用
目标类:
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("添加用户");
}
@Override
public User queryUserByXX() {
// TODO Auto-generated method stub
System.out.println("查找用户");
return null;
}
@Override
public void update() {
System.out.println("更新用户");
// TODO Auto-generated method stub
}
@Override
public void delete() {
System.out.println("删除用户");
// TODO Auto-generated method stub
}
}
切面类:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/*
* 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
* 具体的接口看上文的通知类型后面
* 采用环绕通知
*/
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
Object obj = null;
try{
System.out.println("执行方法前");
//手动执行目标方法
obj = invocation.proceed();
System.out.println("执行方法后");
return obj;
}catch(Exception e){
e.printStackTrace();
}
return obj;
}
}
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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 创建目标类 -->
<bean id="userServiceImpl" class="net.seehope.springAOP.UserServiceImpl"></bean>
<!-- 创建切面类 -->
<bean id="myAspect" class="net.seehope.springAOP.MyAspect"></bean>
<!-- 3 aop编程
3.1 导入命名空间
3.2 使用 <aop:config>进行配置
proxy-target-class="true" 声明时使用cglib代理
<aop:pointcut> 切入点 ,从目标对象获得具体方法
<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
advice-ref 通知引用
pointcut-ref 切入点引用
3.3 切入点表达式
execution(* com.seehope.springAOP.*.*(..))
返回值任意 包 类名任意 方法名任意 参数任意
-->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* net.seehope.springAOP..*.*(..))" id="myPointCut"/>
<aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/>
</aop:config>
</beans>
测试类:注意导入的包
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class PostTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
UserServiceImpl userService = (UserServiceImpl) context.getBean("userServiceImpl");
userService.queryUserByXX();
userService.addUser();
userService.delete();
userService.update();
}
}
测试结果:
由结果可见,担我们需要在方法执行前后进行逻辑处理时,可以利用springAOP对目标类方法进行代理增强,常见的操作有添加日志之类的,AOP切面类易管理更改,这样就无需对每个方法进行添加逻辑处理,不仅减少了我们开发的工作量,更加重要的是易于代码的维护