项目+测试+测试配置工程结构:
本测试类中没有用到src/test/resources的资源文件
myconfigurations.java @Configuration public class myconfigurations { @Autowired private Designgraph dg; @Bean(name = "car") @Scope("prototype") public Car car() { Car car = new Car(); car.setDg(dg); return car; } } Designgraph.java //一个设计图纸可以造很多车 @Component public class Designgraph { private String graph = "car graph"; public String Designcar(){ return "benz"+graph; } } Car.java public class Car { private String name; private Designgraph dg; public String getName() { return name; } public void setName(String name) { this.name = name; } public Designgraph getDg() { return dg; } public void setDg(Designgraph dg) { this.dg = dg; } } SpringContextUtil.java @Component public class SpringContextUtil implements ApplicationContextAware { private Logger LOGGER = LoggerFactory.getLogger(SpringContextUtil.class); // Spring应用上下文环境 @Autowired private static ApplicationContext applicationContext; /** * 实现ApplicationContextAware接口的回调方法,设置上下文环境 * * @param applicationContext */ @Override public void setApplicationContext(ApplicationContext c) { applicationContext = c; LOGGER.info("get appcontext:{}",applicationContext); } /** * 获取对象 这里重写了bean方法,起主要作用 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws BeansException */ public static Object getBean(String name) throws BeansException { return applicationContext.getBean(name); } } testscope.java @RunWith(SpringRunner.class) @SpringBootTest public class testscope { Logger log = LoggerFactory.getLogger(testscope.class); @Test public void teststaticctx(){ Car car1 = (Car)SpringContextUtil.getBean("car"); System.out.println(car1); Car car2 = (Car)SpringContextUtil.getBean("car"); System.out.println(car2); } }
如果在myconfigurations.java的car()方法上没有添加 @Scope("prototype")这个注解,那么在testscope.java中调用SpringContextUtil.getBean("car")每次得到的都是同一个Bean
所以为了保证car多例, @Scope("prototype")这个注解是必须的;
怎样可以将SpringContextUtil.java的捕获ApplicationContext的功能结合到myconfigurations.java中?
怎样控制bean创建顺序,逻辑顺序?时间顺序?
代码地址:
https://github.com/KouReal/springbootstudy_buycar
myconfigurations实现了ApplicationContextAware接口,按照接口要求定义了setApplicationContext(Applicationcontext appcontext)方法;
然后自己为myconfigurations添加了static applicationcontext字段,static getBean(String name)方法;
现在要测试,在springboot启动后,主线程,其他线程都访问myconfigurations的applicationcontext字段,然后访问getbean方法,看看是否会有空指针异常,就是考虑是否会在myconfigurations完成实例化之前就执行到测试线程和其他线程的getbean方法,从而导致nullpointerexeception。
故意在myconfigurations的setApplicationContext(Applicationcontext appcontext)方法中设置了8秒的sleep
@Configuration public class myconfigurations implements ApplicationContextAware{ Logger log = LoggerFactory.getLogger(myconfigurations.class); public static ApplicationContext applicationcontext; ... @Bean(name="test222") public Test222 test222(){ return new Test222(); } @Override public void setApplicationContext(ApplicationContext actx) throws BeansException { try { log.info("setapplicationcontext sleep 8000:thread:{}",Thread.currentThread()); Thread.currentThread().sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub applicationcontext=actx; } public static Object getBean(String name) throws BeansException { return applicationcontext.getBean(name); } } class Test222{ private int i; }
@Test public void teststaticctx(){ //test context ApplicationContext act = myconfigurations.applicationcontext; System.out.println("act is : "+act); try{ Test222 test222 = (Test222) myconfigurations.getBean("test222"); System.out.println("test222:"+test222); }catch(Exception e){ System.out.println("getBean遭遇异常:"+e.getLocalizedMessage()); } Thread th = new Thread(new Runnable() { @Override public void run() { //test context ApplicationContext act = myconfigurations.applicationcontext; System.out.println("act is : "+act); try{ Test222 test222 = (Test222) myconfigurations.getBean("test222"); System.out.println("test222:"+test222); }catch(Exception e){ System.out.println("getBean遭遇异常:"+e.getLocalizedMessage()); } //boss买车 log.info("testscope:thread:{}",Thread.currentThread()); System.out.println(boss.seegrapth()); Car car1 = (Car)myconfigurations.getBean("car"); Car car2 = (Car)myconfigurations.getBean("car"); System.out.println("boss buy:"+car1.getcontent()); System.out.println("boss buy:"+car2.getcontent()); try { log.info("boss sleep...:thread:{}",Thread.currentThread()); Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("boss wake up"); Car car3 = (Car)myconfigurations.getBean("car"); System.out.println("boss buy:"+car3.getcontent()); } }); th.start(); try { th.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
myconfigurations实现了ApplicationContextAware接口,按照接口要求定义了setApplicationContext(Applicationcontext appcontext)方法;
然后自己为myconfigurations添加了static applicationcontext字段,static getBean(String name)方法;
现在要测试,在springboot启动后,主线程,其他线程都访问myconfigurations的applicationcontext字段,然后访问getbean方法,看看是否会有空指针异常,就是考虑是否会在myconfigurations完成实例化之前就执行到测试线程和其他线程的getbean方法,从而导致nullpointerexeception。
故意在myconfigurations的setApplicationContext(Applicationcontext appcontext)方法中设置了8秒的sleep
经过测试,发现没有发生上述现象,无论主线程还是其他线程都在阻塞等待8秒。有时间看下源码有哪些设计。