背景
為了實現(xiàn)基于注解的AOP緩存方案,需要在Service層的方法上進行AspectJ的切面注解來實現(xiàn)緩存。試驗后發(fā)現(xiàn)在其他Service類中可以正常的被切面管理起來,但是在內(nèi)部(相當(dāng)于this)時,這種橫切沒有生效,因此引入了在BeanPostProcessor的postProcessAfterInitialization方法中將spring調(diào)用CGlib代理之后的內(nèi)賦值給了自身的一個引用,從而使得調(diào)用內(nèi)部this時也可以使用到切面來管理。
但是有一個匪夷所思的問題就出現(xiàn)了,使用了4個ServiceImpl來進行操作,但是其中3個是正常生效的(即返回的是CGlib增強過的代理類),只有1個始終返回了其自身的ServiceImpl類。
postProcessAfterInitialization處理代碼如下:
public Object postProcessAfterInitialization(Object springInitedBean, String beanName) throws BeansException {
if (springInitedBean instanceof MochaBeanSelfAware) {
MochaBeanSelfAware proxyBean = (MochaBeanSelfAware)springInitedBean;
proxyBean.setSelfSpringProxy(proxyBean);
// 用于在環(huán)境中打印, setSelf的初始過程; 便于查看log, 是否注入? 注入的是什么類?
System.out.println("springInitedBean is: " + springInitedBean.getClass() +" ,beanName: "+ beanName);
return proxyBean;
}
return springInitedBean;
}
得到的結(jié)果如下(可以看到wantServiceImpl并沒有返回被增強后的類):
springInitedBean is: class com.xxx.service.TopNewsServiceImpl$$EnhancerByCGLIB$$d2e9d029 ,beanName: topNewsServiceImpl
springInitedBean is: class com.xxx.service.WantServiceImpl ,beanName: wantServiceImpl
進一步分析
- 首先懷疑可能是沒有被配置文件中CGlib的處理未生效,但是經(jīng)過排查后發(fā)現(xiàn)修改application.xml中的配置以及強制使用annotation去強制讓wantservice用CGlib來處理,打印結(jié)果還是一致。而且是沒有辦法解釋為什么在其他類中使用wantservice是正常的(因為其他類中正常說明了應(yīng)該是使用了經(jīng)過增強的wantservice$$EnhancerByCGLIB),相關(guān)配置xml如下:
<aop:aspectj-autoproxy proxy-target-class="true" />
- 因此進一步通過debug看了spring內(nèi)部的處理流程,發(fā)現(xiàn)了有很意思的現(xiàn)象,在加載bean的過程中,其加載順序是很有意思的。關(guān)鍵點在于由于wantservice在其它的service中被引用倒了,因此會直接在屬性填充的過程中,先進行其它依賴項的加載過程。而在加載完成后,在beanFactory的SingletonObjects中已經(jīng)有了wantservice$$EnhancerByCGLIB的實例,其后續(xù)的實例化過程會有一定的差別,從而使得wantService在調(diào)用BeanPostProcessor的postProcessAfterInitialization方法先返回的是wantServiceImpl的bean,但是最后返回時,會比較SingletonObjects和Bean對象,最后將SingletonObjects中的對象進行返回。這也就是為什么postProcessAfterInitialization獲取到的bean和最后返回的bean不一致的原因。
關(guān)于返回bean不一致以及postProcessAfterInitialization方法的執(zhí)行時機的解釋
首先看一下這篇博客的解釋Spring Bean的生命周期,其中關(guān)于生命周期的圖特別清晰易懂,特此引用。
如果存在侵權(quán)問題,請通知刪除,謝謝
生命周期如圖:
我們需要特別看一下這一步:
就是在這個方法中,會根據(jù)依賴去先加載其它相關(guān)的Service類,造成最后在緩存中已經(jīng)存在了wantServiceImpl的增強對象了。
具體在spring代碼中體現(xiàn)為populateBean()這一方法的調(diào)用:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = (BeanWrapper) this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet(dependentBeans.length);
for (int i = 0; i < dependentBeans.length; i++) {
String dependentBean = dependentBeans[i];
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
}
因此在填充之后,運行到以下這段代碼時,發(fā)現(xiàn)雖然經(jīng)過了實例化(實例化過程中會調(diào)用postProcessAfterInitialization方法),但是所產(chǎn)生的exposedObject還是和一開始所賦值的Bean是相同的,因此會將從getSingleton()中得到的對象最終傳遞給exposedObject并作為函數(shù)結(jié)果返回。也就是之前所說的為什么在方法中所得到的Bean和最后spring所用的Bean是不同的原因
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
...
同時,根據(jù)時序圖可以分析發(fā)現(xiàn),BeanPost中的方法之所以沒有在Bean一開始加載時就打印(反而時部分依賴類先打印了BeanPost中的方法)的原因,就在于該接口只有在實例化的前后才調(diào)用。在本例中就是為什么TopNews本來是后加載的類,卻先打印了相關(guān)信息的原因
befor initial: class com.xxx.service.TopNewsServiceImpl
springInitedBean is: class com.xxx.service.TopNewsServiceImpl$$EnhancerByCGLIB$$d2e9d029 ,beanName: topNewsServiceImpl
befor initial: class com.xxx.service.WantServiceImpl
springInitedBean is: class com.xxx.service.WantServiceImpl ,beanName: wantServiceImpl
具體解決方案
那么根據(jù)以上的分析,方案就很簡單了,我們在postProcessAfterInitialization方法中不應(yīng)該直接使用spring從參數(shù)中傳來的bean,而是直接從context里面去通過singletonObjects這個Map里拿到實際需要的實例對象即可。而要獲取到當(dāng)前spring的上下文,也只需簡單的實現(xiàn)ApplicationContextAware接口,并在Application.xml中將具體類配置為Bean即可。
具體實現(xiàn)如下,首選是xml文件
<bean id="XXBeanPostProcessor" class="com.cache.XXBeanPostProcessor" />
然后是具體類的代碼
public class MochaBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
public Object postProcessAfterInitialization(Object springInitedBean, String beanName) throws BeansException {
if (springInitedBean instanceof MochaBeanSelfAware) {
MochaBeanSelfAware originBean = (MochaBeanSelfAware) springInitedBean;
MochaBeanSelfAware proxyBean = (MochaBeanSelfAware)getTheSingletonObject(beanName);
originBean.setSelfSpringProxy(proxyBean);
// 用于在環(huán)境中打印, setSelf的初始過程; 便于查看log, 是否注入? 注入的是什么類?
System.out.println("springInitedBean is: " + springInitedBean.getClass() +" ,beanName: "+ beanName);
System.out.println("getTheSingletonObject is: " + getTheSingletonObject(beanName).getClass().getName());
if(beanName!=null && beanName.contains("want")){
System.out.println("*****origin: "+originBean.toString());
System.out.println("*****proxy: "+proxyBean.toString());
}
return originBean;
}
return springInitedBean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof MochaBeanSelfAware){
System.out.println("befor initial: "+bean.getClass());
}
return bean;
}
private Object getTheSingletonObject(String beanName){
return currentCtx.getAutowireCapableBeanFactory().getBean(beanName);
}
private ApplicationContext currentCtx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
currentCtx = applicationContext;
}
}
最后是最終的運行結(jié)果,可以發(fā)現(xiàn)我們獲取倒了正確的實例
befor initial: class com.xxx.service.TopNewsServiceImpl
springInitedBean is: class com.xxx.service.TopNewsServiceImpl$$EnhancerByCGLIB$$d2e9d029 ,beanName: topNewsServiceImpl
getTheSingletonObject is: com.xxx.service.TopNewsServiceImpl$$EnhancerByCGLIB$$d2e9d029
befor initial: class com.xxx.service.product.WantServiceImpl
springInitedBean is: class com.xxx.service.product.WantServiceImpl ,beanName: wantServiceImpl
getTheSingletonObject is: com.xxx.service.WantServiceImpl$$EnhancerByCGLIB$$925dead
*****origin: in want: count is 0
*****proxy: in want: count is 0
當(dāng)然對于更加深入的spring加載過程還存在理解的不夠準(zhǔn)確的地方,也感謝大家批評指正,一起進步。
僅僅記錄下遇到的這個問題,以供遇到類似問題的小伙伴能更快的解決問題~