并发多线程环境下, 触发spring3的getBean的一个bug, bean的scope为prototype(在spring-framework-3.0.0.RELEASE和spring-framework-3.0.3.RELEASE测试过)。
bug见:https://jira.springsource.org/browse/SPR-7423
svn源码提交号为:3603;revised constructor argument caching for highly concurrent creation scenarios (follow-up to SPR-7423)
bug在3.0.4版本被修正。
bug修正(具体修改请看spring svn源码):
1。把volatile方式调整为同步方式来保证代码正确性与健壮性:
去除RootBeanDefinition中resolvedConstructorOrFactoryMethod、constructorArgumentsResolved、resolvedConstructorArguments、preparedConstructorArguments的四个属性修饰符volatile,增加constructorArgumentLock对象在访问上述四个属性相关代码对constructorArgumentLock加synchronized来保证同步。
2。涉及org/springframework/beans/factory中的以下类:
RootBeanDefinition.java
DefaultListableBeanFactory.java
AbstractAutowireCapableBeanFactory.java
ConstructorResolver.java
SimpleInstantiationStrategy.java
concurrent get bean test will be java.lang.NullPointerException:
frist/Person.java文件:
/**
*
*/
package first;
/**
* person bean
*
* @author yangwm Aug 5, 2010 11:20:16 AM
*/
public class Person {
private String name;
private int age;
private String hobby;
public Person(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Person{name=");
sb.append(name);
sb.append(", age=");
sb.append(age);
sb.append(", hobby=");
sb.append(hobby);
sb.append("}");
return sb.toString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
frist/first.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="person" name="person"
class="first.Person" scope="prototype">
<constructor-arg value="yangwm" type="String" />
<constructor-arg value="25" type="int" />
<constructor-arg value="basketball" type="String" />
</bean>
</beans>
first/GetPersonTest.java文件:
/**
*
*/
package first;
import java.io.IOException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* concurrent get bean test will be java.lang.NullPointerException
*
* @author yangwm Jun 12, 2010 1:31:50 PM
*/
public class GetPersonTest {
/** get spring bean container applicationContext **/
private static final ApplicationContext context = new ClassPathXmlApplicationContext("first/first.xml");
public static void main(String[] args) throws IOException {
for (int i = 0; i < 1000; i++) {
new Thread(new PersonRun(i)).start();
}
}
/**
* concurrent get bean test runnable
*
* @author yangwm Jun 15, 2010 3:40:18 PM
*/
static class PersonRun implements Runnable {
int id;
public PersonRun(int id) {
this.id = id;
}
public void run() {
System.out.println("Thread " + id + " start..." + System.currentTimeMillis());
try {
Person people = (Person) context.getBean("person");
System.out.println(Thread.currentThread().getName() + " people.toString()=" + people.toString());
} catch (Exception e) {
System.err.println(Thread.currentThread().getName() + "-----------------------------------------");
e.printStackTrace();
System.exit(0);
}
System.out.println("Thread " + id + " end..." + System.currentTimeMillis());
}
}
}
运行抛出异常:
。。。。。
Thread 75 end...1280993191703
Thread-100 people.toString()=Person{name=yangwm, age=25, hobby=basketball}
Thread 100 end...1280993191703
Thread-71 people.toString()=Person{name=yangwm, age=25, hobby=basketball}
Thread 71 end...1280993191703Thread-80-----------------------------------------
java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:669)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:194)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:993)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:897)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1075)
at first.GetPersonTest$PersonRun.run(GetPersonTest.java:43)
at java.lang.Thread.run(Thread.java:717)
Thread-143-----------------------------------------
java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:669)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:194)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:993)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:897)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1075)
at first.GetPersonTest$PersonRun.run(GetPersonTest.java:43)
at java.lang.Thread.run(Thread.java:717)
Thread-141-----------------------------------------
java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:669)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:194)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:993)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:897)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1075)
at first.GetPersonTest$PersonRun.run(GetPersonTest.java:43)
at java.lang.Thread.run(Thread.java:717)
Thread-130-----------------------------------------
java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:669)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:194)