aop 面向切面编程,是对oop(面向对象编程)的补充。
应用场景:可以在业务的共性关注点插入切面,使得业务可以专注自己的功能逻辑,而不必考虑别的虽然跟它有关联边边角角的东西。考虑下面的场景:战场战士和战地记者。如果一个战士在战场作业,挖战壕、布雷区、阻击敌人、冲锋杀敌,打扫战场,这战场发生的一系列的事件,要留下影像。作为战士难道在挖战壕的时候要拿个录像机或者纸和笔记下自己的当前行为吗?如果这个可以接受,那么一边刺敌人,一边告诉敌人你等会,我要记下来我刺了你一下,这实在太过滑稽,估计活不过一个回合。那么这种事情要做,战士不能,那就交给战地记者做,记者会记录每一个战士的英勇,甚至整个残酷激烈的战场。
oop 和 aop 蕾丝这样的关系。oop只管关注深入自己的逻辑功能,aop来负责处理很多业务(战士)有相同或者具有共性的事情。这样的事情叫做横切关注点。在关系上,如果没有oop,则aop则毫无用武之地。切面就无处安放。也正是oop存在不足,aop才出现弥补了oop的短板。
可参阅Spring aop 文档,比较详尽。
https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/aop.html
aop最先是eclipse团队搞的 aspectj (2002年左右)。因为Spring被广知。
Spring aop套了aspectj的外壳,比如一些定义:aspect、jointpoint 、pointcut、advise。不过Spring在此基础上删减了一部分。比如jointpoint 只能在方法执行时。而aspectj的连接点可以是在字段上、实例构造器上、方法调用、方法执行时。也包括切面织入的实现跟aspectj都不同。
spring aop 用的代理模式的设计,代理模式有两种情况,一种是代理类和目标类共有相同的接口,代理实例持有目标类的实例,这种是接口方式。另外一种是继承方式,代理类和目标类是父子关系,代理类继承了目标类,并重写对应的方法实现代理。只不过在Spring aop中以上两种模式的代理类都不是在编译前事先编写好的,而是在运行期生成的代理类。这就是动态代理。静态代理一般是代码编写的时候就编写好了代理类和目标类,以及代理对象对目标的代理。而动态代理是运行期动态生成代理类的class文件并加载至内存,然后生成对应的代理实例来提供服务。 spring aop 在动态代理模式下对应有两种实现方案,一种是jdk自带的(接口方式),一种是cglib方式(继承方式)。
Spring aop代理实例的生命周期
代理bean初始化,依靠BeanPostProcessor实例来处理。
入口方法AbstractAutoProxyCreator#postProcessAfterInitialization(具体内部的逻辑比较复杂,可以进入wrapIfNecessary方法查看):
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.containsKey(cacheKey)) {
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
内部会做一些逻辑:比如采用jdk代理还是cglib代理,添加适配advisor,最终返回代理bean。
spring test
加入相应的maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.17.RELEASE</version>
</dependency>
<dependency><!-- JUnit单元测试框架 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
test启动入口类。
package com.lee;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 启动
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DiyAnnotationSpringContext.class})
public class RunSpring {
@Autowired
private AopDemo aopDemo;
@Test
public void demo(){
aopDemo.testAdvise();
}
}
spring context 配置类
package com.lee;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"com.lee"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DiyAnnotationSpringContext {
}
切面类
package com.lee;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AopAspect {
@Pointcut( "execution(* *(..))")
void pointCut() {
}
/**
* 在方法执行之前
*/
@Before(value = "pointCut()")
void before() {
System.out.println("before");
}
/**
* 在finally中执行的通知也就是忽视方法是否有无正常
*/
@After(value = "pointCut()")
void after(){
System.out.println("after");
}
/**
* 在抛出异常之后执行的通知
*/
@AfterThrowing("pointCut()")
void throww() {
System.out.println("throww");
}
/**
* 在方法正常调用结束后调用,也即是方法没有抛出异常到调用它的上层方法
*/
@AfterReturning("pointCut()")
void afterReturn() {
System.out.println("afterReturn");
}
}
被代理的业务逻辑类(target)
package com.lee;
import org.springframework.stereotype.Component;
@Component
public class AopDemo {
public void testAdvise () {
System.out.println("testAdvise");
};
}