文章内容
一、什么是AOP?
AOP(面向切面)编程,是一种编程思想;是对面向对象编程OOP的一种增强,OOP是纵向对一个事物的抽象。而AOP是横向的对不同事物的抽象、抽取,而用这种思维去设计编程的方式叫做面向切面编程。
二、相关概念
- Target(目标对象)需要被增强的方法所在的对象
- Proxy(代理对象)对目标对象进行增强后的对象
- Joinpoint(连接点)目标对象中可以被增强的方法
- Pointcut(切入点)目标对象中实际被增强的方法
- Advice(通知/增强)增强部分的代码逻辑
- Aspect(切面)增强部分和切入点的组合
- Weaving(织入)将通知和切入点动态组合的过程
上图所示是一个AOP的架构,现有一个目标对象A,对象A中有三个方法可以被增强,也就是说有三个连接点;现在想要增强的方法是methodA1、methodA2,所以这两个方法就是切入点;我们要使用对象B中的两个方法做一个增强,所以methodB1、methodB2就是增强的具体代码;最后我们将切点与增强的代码组合在一起就形成了一个切面并存储到代理对象的具体方法中。现在通过调用代理对象的方法就可以执行增强后的methodA1、methodA2。
三、在Spring中通过注解实现AOP
1、导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
2、在配置类中添加@EnableAspectJAutoProxy注解
@Configuration
@ComponentScan("com.demo")
@EnableAspectJAutoProxy //开启切面自动代理(开启AOP功能)
public class SpringConfig {}
3、目标对象
@Component
public class User {
@Autowired
private Pets pets;
//切入点
void petEat(){
pets.eat();
}
}
4、定义切面、编写增强方法、配置切入点
@Component
@Aspect //定义切面
public class MyAspect {
//配置切入点 @Before:前置增强
@Before(value = "execution(* com.demo.User.petEat(..))")
public void sayHello(){ //编写增强方法
System.out.println("hello");
}
}
5、运行结果
hello
大黄开始吃东西了
四、AOP运行原理
1、导入AspectJAutoProxyRegistrar
@EnableAspectJAutoProxy注解会导入AspectJAutoProxyRegistrar
@EnableAspectJAutoProxy //开启切面自动代理(开启AOP功能)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
2、注册AnnotationAwareAspectJAutoProxyCreator
紧接着会注册一个AnnotationAwareAspectJAutoProxyCreator到容器中,继承体系中包含BeanPostProcessor后处理器,在 postProcessAfterInitialization 中筛选出需要被增强的bean,创建代理对象
- BeanPostProcessor后处理器:后处理器
- postProcessAfterInitialization方法,会在Bean创建实例并初始化之后,填充到单例池singletonObjects之前执行。在这个阶段可以对指定的对象进行扩展。
五、动态代理的两种方式
- JDK动态代理使用条件:目标类有接口,实现原理是基于接口动态生成实现类的代理对象在spring中,如果目标类有接口,默认使用JDK动态代理
- Cglib动态代理
- 使用条件:目标类没有接口并且不能使用final修饰,实现原理是基于被代理对象动态生成子对象为代理对象
- 在spring中,目标类如果没有接口,默认使用Cglib动态代理。
- 强制使用:如果目标类有接口,仍需使用Cglib,可以通过配置proxy-target-class=”true”强制使用
- @EnableAspectJAutoProxy(proxyTargetClass = true)