AOP基础概念及Spring AOP
在软件开发中,AOP(Aspect-Oriented Programming,面向方面编程)把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似,比如权限认证、日志、事务处理。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的,但是往往会直接嵌入到应用的业务逻辑之中。把这些横切关注点与业务逻辑相分离正是面向切面编程所要解决的问题。AOP可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。
切面(Aspect):在使用面向切面编程时, 我们仍然在一个地方定义通用功能, 但是可以通过声明的方式定义这个功能要以何种方式在何处应用, 而无需修改受影响的类。 横切关注点可以被模块化为特殊的类,这些类被称为切面(aspect)。
通知(Advice): 在AOP术语中, 切面的工作被称为通知。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。
连接点(Joinpoint): 连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合,通常使用明确的类和方法名称, 或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
引入(Introduction): 引入允许我们向现有的类添加新方法或属性。例如,我们可以创建一个Auditable通知类,该类记录了对象最后一次修改时的状态。这很简单,只需一个方法,setLastModified(Date),和一个实例变量来保存这个状态。然后,这个新方法和实例变量就可以被引入到现有的类中,从而可以在无需修改这些现有的类的情况下,让它们具有新的行为和状态。
织入(Weaving): 织入是把切面应用到目标对象并创建新的代理对象的过程。 切面在指定的连接点被织入到目标对象中。 在目标对象的生命周期里有多个点可以进行织入:编译期,切面在目标类编译时被织入,这种方式需要特殊的编译器,AspectJ的织入编译器就是以这种方式织入切面的;类加载期,切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器( ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码;运行期,切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。
Spring提供了4种类型的AOP支持:基于代理的经典Spring AOP;纯POJO切面;@AspectJ注解驱动的切面;注入式AspectJ切面(适用于Spring各版本)。前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。而且它不支持构造器连接点,我们就无法在bean创建时应用通知。
Spring AOP实现原理
Spring使用动态代理或是CGLIB生成代理是有规则的,高版本的Spring会自动选择是使用动态代理还是CGLIB生成代理内容,当然我们也可以强制使用CGLIB生成代理,那就是里面有一个”proxy-target-class”属性,这个属性值如果被设置为true,那么基于类的代理将起作用,如果proxy-target-class被设置为false或者这个属性被省略,那么基于接口的代理将起作用。
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。更多关于动态代理的内容,关注后续文章。
Spring AOP使用示例
基于AspectJ的注解方式:
1,创建需要被代理的接口
public interface AopActionInf {
public void doSomething_01();
public void doSomething_02(String arg1);
public void doSomething_03(String arg1, String arg2);
public void doSomething_04(String arg1, String arg2, String arg3);
}
2,创建实现该接口的实现类
public class ActionImpl implements AopActionInf{
public void doSomething_01() {
System.out.println("this is method doSomething_01");
}
public void doSomething_02(String arg1) {
System.out.println("this is method doSomething_02");
System.out.println("arg1 is "+arg1);
}
public void doSomething_03(String arg1, String arg2) {
System.out.println("this is method doSomething_03");
System.out.println("arg1 is "+arg1);
System.out.println("arg2 is "+arg2);
}
public void doSomething_04(String arg1, String arg2, String arg3) {
System.out.println("this is method doSomething_03");
System.out.println("arg1 is "+arg1);
System.out.println("arg2 is "+arg2);
System.out.println("arg3 is "+arg3);
}
}
3,创建切面类
@Aspect
public class ActionAspect {
@Pointcut("execution(* com.maowei.learning.aop.ActionImpl.*(..))")
private void anyMethod(){}
@Before("anyMethod() && args(name)")
public void beforeMethod(String name){
System.out.println("前置通知,请求参数:"+name);
}
@AfterReturning("anyMethod()")
public void afterReturningMethod(){
System.out.println("后置通知");
}
@After("anyMethod()")
public void afterMethod(){
System.out.println("最终通知");
}
@AfterThrowing("anyMethod()")
public void afterThrowMethod(){
System.out.println("例外通知");
}
@Around("anyMethod()")
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知");
Object object = pjp.proceed();
System.out.println("退出方法");
return object;
}
}
4,配置相关Bean信息
<aop:aspectj-autoproxy/>
<bean id="actionImpl" class="com.maowei.learning.aop.ActionImpl"/>
<bean id="actionAspect" class="com.maowei.learning.aop.ActionAspect"/>
5,编写测试类
public class AopTest {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/spring-aop.xml");
AopActionInf a = (AopActionInf)ctx.getBean("actionImpl");
a.doSomething_01();
}
}
基于XML配置文件
1,如上文所述,创建代理接口和相应实现类
2,创建切面类
public class ActionAspectXML {
public void beforeMethod(String name){
System.out.println("前置通知,请求参数:"+name);
}
public void beforeMethod(){
System.out.println("前置通知");
}
public void afterReturningMethod(){
System.out.println("后置通知");
}
public void afterMethod(){
System.out.println("最终通知");
}
public void afterThrowMethod(){
System.out.println("例外通知");
}
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知");
Object object = pjp.proceed();//执行该方法
System.out.println("退出方法");
return object;
}
}
3,配置XML文件
<bean id="actionImpl" class="com.maowei.learning.aop.ActionImpl"/>
<bean id="actionAspectXML" class="com.maowei.learning.aop.ActionAspectXML"/>
<aop:config>
<aop:aspect id = "aspectXML" ref="actionAspectXML">
<aop:pointcut id="anyMethod" expression="execution(* com.maowei.learning.aop.ActionImpl.*(..))"/>
<aop:before method="beforeMethod" pointcut-ref="anyMethod"/>
<aop:after method="afterMethod" pointcut-ref="anyMethod"/>
<aop:after-returning method="afterReturningMethod" pointcut-ref="anyMethod"/>
<aop:after-throwing method="afterThrowMethod" pointcut-ref="anyMethod"/>
<aop:around method="aroundMethod" pointcut-ref="anyMethod"/>
</aop:aspect>
</aop:config>
4,如上文所述,编写测试类
Spring AOP使用场景
- 1,事务处理;
- 2,权限认证;
- 3,日志跟踪;
- ……
参考文献
- 《Spring 实战》 第四版;