項目+測試+測試配置工程結構:
本測試類中沒有用到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秒。有時間看下源碼有哪些設計。