Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

step-9-auto-create-aop-proxy的JDK切面无效,非常困惑!!! #10

Open
dynastqin opened this issue Dec 21, 2017 · 6 comments
Open

Comments

@dynastqin
Copy link

dynastqin commented Dec 21, 2017

us.codecraft.tinyioc.context.ApplicationContextTest#test 该方法无切面效果,运行直接为:
Hello World!

而step-10-invite-cglib-and-aopproxy-factory例子就可以,反复对比源码,发现就是因为AbstractBeanFactory类中:

@OverRide
public Object getBean(String name) throws Exception {
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
throw new IllegalArgumentException("No bean named " + name + " is defined");
}
Object bean = beanDefinition.getBean();
if (bean == null) {
bean = doCreateBean(beanDefinition);
bean = initializeBean(bean, name);
beanDefinition.setBean(bean); //本行在起作用,必须有,否则切面无效果但到底为何?
}
return bean;
}

@dynastqin
Copy link
Author

后面我找到了一个更搞笑的解决方案,上面去掉,如下的也去掉就可以了:
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
Object bean = createBeanInstance(beanDefinition);
// beanDefinition.setBean(bean);
applyPropertyValues(bean, beanDefinition);
return bean;
}

@dachengxu
Copy link

@dynastqin 你那搞笑的解决方案不行的, 这样每次getBean的时候都会走doCreateBean, inititalzeBean,所以才会生效

@JinhaoPlus
Copy link

@dynastqin

@Override
public Object getBean(String name) throws Exception {
	BeanDefinition beanDefinition = beanDefinitionMap.get(name);
	if (beanDefinition == null) {
		throw new IllegalArgumentException("No bean named " + name + " is defined");
	}
	Object bean = beanDefinition.getBean();
	if (bean == null) {
		bean = doCreateBean(beanDefinition);
                 bean = initializeBean(bean, name);
		beanDefinition.setBean(bean);
	}
	return bean;
}

这段代码我反复调试了一下发现了这句话的意思:
beanDefinition.setBean(bean);
虽然在doCreateBean方法里面已经进行过beanDefinition.setBean(bean),但是和这里的beanDefinition.setBean(bean)的用途是不一样的。
在doCreateBean方法里设置beanDefinition里面bean的引用是为了将bean的引用在反射newInstance之后立即设置好,这样做为了避免在循环引用的时候出现死循环。
但是这里的设置beanDefinition里面bean的引用是为了在 initializeBean(bean, name)方法过后再进行一次引用设置,这样做的目的是为了处理BeanPostProcessor对bean的影响,在这里就是AspectJAwareAdvisorAutoProxyCreator创建代理的时候会把以前的bean转换为Proxy创建的新bean,这个问题可以debug的时候去观察一下,代理创建的bean和传入的bean实际上是不一样的。(在这里推荐单步调试的方法去查看,我尝试过在代理前后打印这个bean却发现打印出的bean是一样的)

另外, @dynastqin 说的第二个方法其实会破坏循环引用的问题。

@jackfromcn
Copy link

jackfromcn commented Jun 1, 2018

@dynastqin 经过调试,发现是因为doCreateBean方法中创建的是通过反射创建的类,没有作代理,在initializeBean方法中会通过aspectj对bean进行动态代理,但是代理类并没有put到Map中,因此,在@test方法中,使用applicationContext.getBean()方法获取到的还是通过反射出来,但是没有经过动态代理的对象。所以,切片失效。

@HuangtianyuCN
Copy link

image

@HuangtianyuCN
Copy link

可以看到,第一次setBean实现了将beanDefinition.bean指向内存空间a。此时bean和beanDefinition.bean指向了同一块内存区域,因此对bean的操作本质上是对内存空间a的操作,而beanDefinition.bean也指向这块内存区域,因此对这块区域propertyvalue的赋值不影响beanDefinition.bean的引用关系。
**但是!**当return new之后,bean已经指向了不同的内存空间b,beanDefinition.bean仍然指向内存空间a,因此需要重新set。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants