Spring AOP 中的代理對(duì)象是怎么創(chuàng)建出來的?
1. AOP 用法
先來一個(gè)簡單的案例,小伙伴們先回顧一下 AOP,假設(shè)我有如下類:
@Service
public class UserService {
public void hello() {
System.out.println("hello javaboy");
}
}
然后我寫一個(gè)切面,攔截 UserService 中的方法:
@Component
@Aspect
@EnableAspectJAutoProxy
public class LogAspect {
@Before("execution(* org.javaboy.bean.aop.UserService.*(..))")
public void before(JoinPoint jp) {
String name = jp.getSignature().getName();
System.out.println(name+" 方法開始執(zhí)行了...");
}
}
最后,我們看一下從 Spring 容器中獲取到的 UserService 對(duì)象:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml");
UserService us = ctx.getBean(UserService.class);
System.out.println("us.getClass() = " + us.getClass());
打印結(jié)果如下:
圖片
可以看到,獲取到的 UserService 是一個(gè)代理對(duì)象。
其他各種類型的通知我這里就不說了,不熟悉的小伙伴可以在公眾號(hào)【江南一點(diǎn)雨】后臺(tái)回復(fù) ssm,有松哥錄制的免費(fèi)入門視頻。
2. 原理分析
那么注入到 Spring 容器中的 UserService,為什么在獲取的時(shí)候變成了一個(gè)代理對(duì)象,而不是原本的 UserService 了呢?
整體上來說,我們可以將 Spring Bean 的生命周期分為四個(gè)階段,分別是:
實(shí)例化。
屬性賦值。
初始化。
銷毀。
如下圖:
圖片
首先實(shí)例化就是通過反射,先把 Bean 的實(shí)例創(chuàng)建出來;接下來屬性賦值就是給創(chuàng)建出來的 Bean 的各個(gè)屬性賦值;接下來的初始化就是給 Bean 應(yīng)用上各種需要的后置處理器;最后則是銷毀。
2.1 doCreateBean
AOP 代理對(duì)象的創(chuàng)建是在初始化這個(gè)過程中完成的,所以今天我們就從初始化這里開始看起。
AbstractAutowireCapableBeanFactory#doCreateBean:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//...
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//...
return exposedObject;
}
小伙伴們看到,這里有一個(gè) initializeBean 方法,在這個(gè)方法中會(huì)對(duì) Bean 執(zhí)行各種后置處理器:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
這里一共是執(zhí)行了四個(gè)方法,也都是非常常見的 Bean 初始化方法:
- invokeAwareMethods:執(zhí)行 Aware 接口下的 Bean。
- applyBeanPostProcessorsBeforeInitialization:執(zhí)行 BeanPostProcessor 中的前置方法。
- invokeInitMethods:執(zhí)行 Bean 的初始化方法 init。
- applyBeanPostProcessorsAfterInitialization:執(zhí)行 BeanPostProcessor 中的后置方法。
1、3 這兩個(gè)方法很明顯跟 AOP 關(guān)系不大,我們自己平時(shí)創(chuàng)建的 AOP 對(duì)象基本上都是在 applyBeanPostProcessorsAfterInitialization 中進(jìn)行處理的,我們來看下這個(gè)方法:
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
小伙伴們看到,這里就是遍歷各種 BeanPostProcessor,并執(zhí)行其 postProcessAfterInitialization 方法,將執(zhí)行結(jié)果賦值給 result 并返回。
2.2 postProcessAfterInitialization
BeanPostProcessor 有一個(gè)實(shí)現(xiàn)類 AbstractAutoProxyCreator,在 AbstractAutoProxyCreator 的 postProcessAfterInitialization 方法中,進(jìn)行了 AOP 的處理:
@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;
}
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;
}
// Create proxy if we have advice.
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;
}
可以看到,首先會(huì)嘗試去緩存中獲取代理對(duì)象,如果緩存中沒有的話,則會(huì)調(diào)用 wrapIfNecessary 方法進(jìn)行 AOP 的創(chuàng)建。
正常來說,普通 AOP 的創(chuàng)建,前面三個(gè) if 的條件都是不滿足的。第一個(gè) if 是說 beanName 是否是一個(gè) targetSource,顯然我們這里不是;第二個(gè) if 是說這個(gè) Bean 是不是不需代理(結(jié)合上篇文章一起理解),我們這里顯然是需要代理的;第三個(gè) if 的作用我們也在上篇文章中和小伙伴們介紹過,這里就不再贅述了。
關(guān)于第二個(gè) if 我多說一句,如果這里進(jìn)來的是一個(gè)切面的 Bean,例如第一小節(jié)中的 LogAspect,這種 Bean 顯然是不需要代理的,所以會(huì)在第二個(gè)方法中直接返回,如果是其他普通的 Bean,則第二個(gè) if 并不會(huì)進(jìn)來。
所在在 wrapIfNecessary 中,最重要的方法實(shí)際上就是兩個(gè):getAdvicesAndAdvisorsForBean 和 createProxy,前者用來找出來所有跟當(dāng)前類匹配的切面,后者則用來創(chuàng)建代理對(duì)象。
2.3 getAdvicesAndAdvisorsForBean
這個(gè)方法,說白了,就是查找各種 Advice(通知/增強(qiáng)) 和 Advisor(切面)。來看下到底怎么找的:
AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean:
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
從這里可看到,這個(gè)方法主要就是調(diào)用 findEligibleAdvisors 去獲取到所有的切面,繼續(xù):
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
這里一共有三個(gè)主要方法:
- findCandidateAdvisors:這個(gè)方法是查詢到所有候選的 Advisor,說白了,就是把項(xiàng)目啟動(dòng)時(shí)注冊(cè)到 Spring 容器中所有切面都找到,由于一個(gè) Aspect 中可能存在多個(gè) Advice,每個(gè) Advice 最終都能封裝為一個(gè) Advisor,所以在具體查找過程中,找到 Aspect Bean 之后,還需要遍歷 Bean 中的方法。
- findAdvisorsThatCanApply:這個(gè)方法主要是從上個(gè)方法找到的所有切面中,根據(jù)切點(diǎn)過濾出來能夠應(yīng)用到當(dāng)前 Bean 的切面。
- extendAdvisors:這個(gè)是添加一個(gè) DefaultPointcutAdvisor 切面進(jìn)來,這個(gè)切面使用的 Advice 是 ExposeInvocationInterceptor,ExposeInvocationInterceptor 的作用是用于暴露 MethodInvocation 對(duì)象到 ThreadLocal 中,如果其他地方需要使用當(dāng)前的 MethodInvocation 對(duì)象,直接通過調(diào)用 currentInvocation 方法取出即可。
接下來我們就來看一下這三個(gè)方法的具體實(shí)現(xiàn)。
2.3.1 findCandidateAdvisors
AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
@Override
protected List<Advisor> findCandidateAdvisors() {
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
這個(gè)方法的關(guān)鍵在于通過 buildAspectJAdvisors 構(gòu)建出所有的切面,這個(gè)方法有點(diǎn)復(fù)雜:
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
Class<?> beanType = this.beanFactory.getType(beanName, false);
if (beanType == null) {
continue;
}
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
這個(gè)方法第一次進(jìn)來的時(shí)候,aspectNames 變量是沒有值的,所以會(huì)先進(jìn)入到 if 分支中,給 aspectNames 和 aspectBeanNames 兩個(gè)變量賦值。
具體過程就是首先調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法(不熟悉該方法的小伙伴參考 Spring 中的父子容器是咋回事?一文),去當(dāng)前容器以及當(dāng)前容器的父容器中,查找到所有的 beanName,將返回的數(shù)組賦值給 beanNames 變量,然后對(duì) beanNames 進(jìn)行遍歷。
遍歷時(shí),首先調(diào)用 isEligibleBean 方法,這個(gè)方法是檢查給定名稱的 Bean 是否符合自動(dòng)代理的條件的,這個(gè)細(xì)節(jié)我們就不看了,因?yàn)橐话闱闆r下,我們項(xiàng)目中的 AOP 都是自動(dòng)代理的。
接下來根據(jù) beanName,找到對(duì)應(yīng)的 bean 類型 beanType,然后調(diào)用 advisorFactory.isAspect 方法去判斷這個(gè) beanType 是否是一個(gè) Aspect,具體的判斷過程上篇文章講過了,小伙伴們可以參考。
如果當(dāng)前 beanName 對(duì)應(yīng)的 Bean 是一個(gè) Aspect,那么就把 beanName 添加到 aspectNames 集合中,并且把 beanName 和 beanType 封裝為一個(gè) AspectMetadata 對(duì)象。
接下來會(huì)去判斷 kind 是否為 SINGLETON,這個(gè)默認(rèn)都是 SINGLETON,所以這里會(huì)進(jìn)入到分支中,進(jìn)來之后,會(huì)調(diào)用 this.advisorFactory.getAdvisors 方法去 Aspect 中找到各種通知和切點(diǎn)并封裝成 Advisor 對(duì)象返回,由于一個(gè)切面中可能定義多個(gè)通知,所以最終返回的 Advisor 是一個(gè)集合,最后把找到的 Advisor 集合存入到 advisorsCache 緩存中。
后面方法的邏輯就很好懂了,從 advisorsCache 中找到某一個(gè) aspect 對(duì)應(yīng)的所有 Advisor,并將之存入到 advisors 集合中,然后返回集合。
這樣,我們就找到了所有的 Advisor。
2.3.2 findAdvisorsThatCanApply
接下來 findAdvisorsThatCanApply 方法主要是從眾多的 Advisor 中,找到能匹配上當(dāng)前 Bean 的 Advisor,小伙伴們知道,每一個(gè) Advisor 都包含一個(gè)切點(diǎn) Pointcut,不同的切點(diǎn)意味著不同的攔截規(guī)則,所以現(xiàn)在需要進(jìn)行匹配,檢查當(dāng)前類需要和哪個(gè) Advisor 匹配:
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
這里實(shí)際上就是調(diào)用了靜態(tài)方法 AopUtils.findAdvisorsThatCanApply 去查找匹配的 Advisor:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
這個(gè)方法中首先會(huì)去判斷 Advisor 的類型是否是 IntroductionAdvisor 類型,IntroductionAdvisor 類型的 Advisor 只能在類級(jí)別進(jìn)行攔截,靈活度不如 PointcutAdvisor,所以我們一般都不是 IntroductionAdvisor,因此這里最終會(huì)走入到最后一個(gè)分支中:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor ia) {
return ia.getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor pca) {
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
從這里小伙伴們就能看到,IntroductionAdvisor 類型的 Advisor 只需要調(diào)用 ClassFilter 過濾一下就行了,ClassFilter 松哥在前面的文章中已經(jīng)介紹過了(玩一玩編程式 AOP),小伙伴們看這里的匹配邏輯也是非常 easy!而 PointcutAdvisor 類型的 Advisor 則會(huì)繼續(xù)調(diào)用 canApply 方法進(jìn)行判斷:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher iamm) {
introductionAwareMethodMatcher = iamm;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
小伙伴們看一下,這里就是先按照類去匹配,匹配通過則繼續(xù)按照方法去匹配,方法匹配器要是設(shè)置的 true,那就直接返回 true 就行了,否則就加載當(dāng)前類,也就是 targetClass,然后遍歷 targetClass 中的所有方法,最后調(diào)用 introductionAwareMethodMatcher.matches 方法去判斷方法是否和切點(diǎn)契合。
就這樣,我們就從所有的 Advisor 中找到了所有和當(dāng)前類匹配的 Advisor 了。
2.3.3 extendAdvisors
這個(gè)是添加一個(gè) DefaultPointcutAdvisor 切面進(jìn)來,這個(gè)切面使用的 Advice 是 ExposeInvocationInterceptor,ExposeInvocationInterceptor 的作用是用于暴露 MethodInvocation 對(duì)象到 ThreadLocal 中,如果其他地方需要使用當(dāng)前的 MethodInvocation 對(duì)象,直接通過調(diào)用 currentInvocation 方法取出即可。
這個(gè)方法的邏輯比較簡單,我就不貼出來了,小伙伴們可以自行查看。
2.4 createProxy
看完了 getAdvicesAndAdvisorsForBean 方法,我們已經(jīng)找到了適合我們的 Advisor,接下來繼續(xù)看 createProxy 方法,這個(gè)方法用來創(chuàng)建一個(gè)代理對(duì)象:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
return buildProxy(beanClass, beanName, specificInterceptors, targetSource, false);
}
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {
AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
}
}
else {
// No proxyTargetClass flag enforced, let's apply our default checks...
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);
}
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = smartClassLoader.getOriginalClassLoader();
}
return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
}
這段代碼不知道小伙伴們看了是否覺得眼熟,這就是前面發(fā)的另類 AOP,編程式 AOP!一文中的內(nèi)容,所以這塊源碼大家自己看看就好了,我就不啰嗦了。