概念
IOC和AOP作为Spring的两大核心,如果你接触过Spring框架,那就一定需要了解这两个核心部分的工作原理。IOC控制反转(或者说依赖注入)的部分已经在我的从源码解读Spring的IOC文章中有介绍,这篇文章主要来讲解AOP面向切面编程的原理
AOP,即Aspect Oriented Programming,面向切面编程,关于什么是面向切面编程,为了避免喧宾夺主,我在这里不进行过多的探讨,只需要简单地了解面向切面编程就是相当于给一个方法在运行时动态添加新的功能,如果你了解过代理模式就非常好理解AOP的知识了
同时,在开始本文之前,也希望你了解动态代理的一些知识,比如Jdk代理和Cglib代理,如果对这两种代理方式一无所知的话,那就暂时先知道Jdk代理是通过在运行时编译生成某接口对象的实现类,而Cglib代理是在运行时动态生成某对象的子类对象
AOP的使用
spring中用到aop的地方有很多,但是大部分地方都是在你不知道的情况下自动开启的,比如事务管理这样的功能,你仅仅只是添加一个注解而已,spring就自动帮你做了aop的切面,这里我们通过手动写一个小的demo,来让你快速理解一下aop是如何使用的,而如果你已经了解了AOP的使用方法,就可以直接跳过这一部分
首先在pom.xml中添加aop的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
然后在配置文件中添加以下配置(不配置也可以,但是接下来可能会用到):
# 开启注解(默认开启)
spring.aop.auto=true
# 使用cglib代理(默认不使用)
spring.aop.proxy-target-class=false
准备工作完成了,我们开始接下来的操作,现在我们有一个歌手:
@Component
public class Singer implements Singable {
public final String name = "小红";
@Override
public void sing() {
System.out.println("la la la...");
}
}
我们想在她唱歌前打开灯光,在唱歌后打扫舞台,于是我们做了下面这样的类:
public class Manager {
void turnLightOn() {
System.out.println("灯光打开了...");
}
void cleanStage() {
System.out.println("打扫舞台...");
}
}
好了,接下来我们怎么在sing方法调用前后调用这两个方法呢,难不成将对象注入到Singer中然后手动调用?这样当然可以,但是我们既然学了aop,就要使用aop的思想,不主动侵入代码,于是我们给Manger类添加以下注解:
@Aspect
@Component
public class Manager {
@Pointcut("execution(* com.akira.demo.interfaces.Singable.sing(..))")
public void singsong() {}
@Before("singsong()")
void turnLightOn() {
System.out.println("灯光打开了...");
}
@After("singsong()")
void cleanStage() {
System.out.println("打扫舞台...");
}
}
这样我们就完成aop的操作,来测试一下(一定要通过自动注入的方式):
@Autowired
Singable singer;
@Test
public void sing() {
singer.sing();
}
结果:
AOP的实现原理
从刚才的代码演示,有过一些基础的应该可以了解spring中aop的大概使用方法,接下来我们就要从刚才的方法入手,详细了解spring是怎么把切面注入方法的
首先,方法的起点是AopNamespaceHandler类,其中只有一个init方法:
@Override
public void init() {
// 在 2.0 XSD 和 2.1 XSD 中
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// 仅在 2.0 XSD 中,在2.1 XSD 被移入context命名空间
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
用过xml配置aop的应该对这些命名空间很熟悉,我们关注aspectj-autoproxy这个标签,这是用来配置自动开启aop注解的,我们进入对应的解析器AspectJAutoProxyBeanDefinitionParser中,发现parse方法中有这么一行:
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
不理解这个方法干什么没关系,看名称上有一个registerAspectJAnnotation,说明是要注册aop,我们进入这个方法
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
实际上代码只有三行,第一行我们知道BeanDefinition就是bean的实体封装对象,这里实际上转发调用了registerOrEscalateApcAsRequired方法,在其中注册AnnotationAwareAspectJAutoProxyCreator对象,实际上相当于确定切点匹配的bean,即确定被代理的bean
第二行useClassProxyingIfNecessary方法主要是用于选择代理方式,可以忽略
第三行则是给刚才的bean注册组件,用于监听器的监听
接下来好像就一头雾水了,别急,我们发现registerOrEscalateApcAsRequired传入的第一个参数就是AnnotationAwareAspectJAutoProxyCreator.class,这实际上也是aop的核心类,我们一路往上,找到它的一个父类AbstractAutoProxyCreator,定义如下:
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
我们发现里面有一个postProcessBeforeInstantiation方法,这个是加载实例化之前执行的方法,来自于BeanPostProcessor接口。既然是动态代理,那肯定与对象的实例化有关,这个方法源码如下:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
核心方法很显然就是wrapIfNecessary,源码如下:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 如果我们声明了通知,就创建代理对象
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
终于到核心方法了,我们先来看getAdvicesAndAdvisorsForBean方法,顾名思义,这个方法是用来获取bean的所有通知方法,或者可以叫增强方法,具体执行过程不再此赘述,感兴趣的可以自行查阅
获取了增强方法之后,会发现这里调用了createProxy创建了代理对象(这个方法的实现还是在AbstractAutoProxyCreator中,不愧是核心类),这个方法源码如下:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 把增强器添加到代理工厂中
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 确定增强方法,包括拦截器
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// 空方法
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 返回代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
看到这里你可能大失所望,怎么还是没有调用代理方法,别急,这里只是进行初始化而已,不过我们也能发现,这里的操作已经完全针对特定的注入的bean和增强方法了
我们进入最后的getProxy方法,来寻找创建代理类的位置:
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
进入ProxyCreatorSupport中,这是一个同步方法:
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
// 激活监听器
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
这里getAopProxyFactory默认返回DefaultAopProxyFactory,重点来了,我们进入createAopProxy方法:
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// config.isOptimize():判断是否进行代理器的主动优化
// config.isProxyTargetClass():判断是否直接代理目标类和接口
// hasNoUserSuppliedProxyInterfaces(config)):判断是否指定了代理接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 如果被代理类是接口,或动态生成指定的类作为代理类,则使用Jdk代理
return new JdkDynamicAopProxy(config);
}
// 否则使用Cglib代理
return new ObjenesisCglibAopProxy(config);
}
else {
// 默认使用Jdk代理
return new JdkDynamicAopProxy(config);
}
}
终于发现真正代理类的位置了,配合注释基本可以大致理清流程:
- 如果开发者没有进行主动配置,则选用Jdk代理
- 如果被代理类是接口,或者需要代理getProxyClass/newProxyInstance动态生成的方法时,也选用Jdk代理
- 其余情况选择Cglib代理
到这里,我们就明白Spring中一个代理对象是如何创建出来的了,接下来就是进行代理对象方法的调用而已,关于具体的代理对象的执行过程,如果有机会我会单独写一篇文章来详细讲解
总结
整个代理过程确实有些复杂,这里为大家做一个简要的总结:
- AopNamespaceHandler加载配置
- 调用AopNamespaceUtils工具类注册核心类AnnotationAwareAspectJAutoProxyCreator和其他组件
- AnnotationAwareAspectJAutoProxyCreator因为继承了BeanPostProcessor接口,会在实例化前调用postProcessBeforeInstantiation方法,在这个方法中调用了wrapIfNecessary方法
- wrapIfNecessary方法中获取声明的通知(增强方法),然后调用createProxy创建代理对象
- createProxy中创建代理工厂,确定增强方法,调用工厂方法getProxy返回代理对象
- getProxy最终是调用了DefaultAopProxyFactory的createAopProxy来根据配置和代理类的特点选用代理方式
- 最终返回代理对象