AOP
是什么
AOP
(Aspect-Oriented Programming
,面向切面编程),可以说是 OOP
的补充和完善。OOP
引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP
则显得无能为力。OOP
允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切代码
面向切面编程,核心原理是使用动态代理模式在方法执行前后或出现异常时加入相关逻辑
AOP
的使用场景
Authentication
权限的处理Cache
缓存的处理Transactions
事务的处理Performance optimization
性能的优化等
AOP
相关概念
- 切入点
Pointcut
:指定一个通知将被引发的一系列连接点的集合。AOP
框架必须允许开发者指定切入点:例如,使用正则表达式。spring
定义了Pointcut
接口,用来组合MethodMatcher
和ClassFilter
,可以通过名字很清楚的理解,MethodMatcher
是用来检查目标类的方法是否可以被应用此通知,而ClassFilter
是用来检查Pointcut
是否应该应用到目标类上 - 连接点
Joinpoint
:程序执行过程中明确的点,如方法的调用或特定的异常被抛出 - 通知
Advice
:在特定的连接点,AOP
框架执行的动作。各种类型的通知包括around、before
和throws
通知。许多AOP
框架包括spring
都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。spring
中定义了四个advice
:BeforeAdvice, AfterAdvice, ThrowAdvice,DynamicIntroductionAdvice
- 切面
Aspect
:一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE
应用中一个很好的横切关注点例子。方面用spring
的Advisor
或拦截器实现 - 织入
Weaving
:组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ
编译器),也可以在运行时完成
Spring AOP
的简单实现
引入 maven
依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建接口及其实现类
public interface Person {
void say();
}
public class Student implements Person{
public void say(){
System.out.println("这是一个苦逼的程序员");
}
}
创建切面类
@Aspect
public class AspectJTest {
@Pointcut("execution(* *.say(..))")
public void test(){
}
@Before("test()")
public void before(){
System.out.println("before test..");
}
@After("test()")
public void after(){
System.out.println("after test..");
}
@Around("test()")
public Object around(ProceedingJoinPoint p){
System.out.println("before1");
Object o = null;
try {
o = p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("after1");
return o;
}
}
测试类
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
Person bean2 = (Person)ac.getBean("student");
bean2.say();
}
}
测试结果
before1
before test..
这是一个苦逼的程序员
after1
after test..
Spring AOP
源码解读
读源码之前
-
在使用
ApplicationContext
相关实现类加载bean
的时候,会对所有单例且非懒加载的bean
,在构造ApplicationContext
的时候就会初始化好这些bean
,而不会等到使用的时候才去初始化。这也就是单例bean
默认非懒加载的应用 -
结合以上,被代理后的
bean
,实际在ApplicationContext
构造完成之后就已经被创建完成,getBean()
的操作直接从一级缓存singletonObjects
中获取即可
beanPostProcessor
和 beanFactoryPostProcessor
-
beanPostProcessor
接口:bean
后置处理器。beanPostProcessor
能在spring
容器实例化bean
之后,bean
初始化前或后对bean
做一些修改。而aop
的功能实现正式基于此,在bean
初始化后创建针对该bean
的proxy
,然后返回给用户该proxy
-
beanFactoryPostProcessor
接口:beanFactoryPostProcessor
接口是针对bean
容器的,它的实现类可以在当前spring
容器加载bean
定义后,bean
实例化之前修改bean
的定义属性,达到影响之后实例化bean
的效果
spring
中单例 bean
的初始化主要过程
createBeanInstance
:实例化,其实也就是调用对象的构造方法实例化对象populateBean
:填充属性,这一步主要是多bean
的依赖属性进行填充initializeBean
:初始化,执行该bean
的一些初始化方法
寻找 aop
注解对应的解析器
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser(