有Bean得治
任何一個正常程式的訪問都會在記憶體中建立非常多的物件,物件與物件之間還會出現很多依賴關係(一個處理業務邏輯的類中幾乎都會使用到別的類的例項),一般的做法都是使用new關鍵字來建立物件,對於多次重複使用的物件會採用單例模式來設計。
但在Spring中卻不是這樣,Spring框架使用了一個容器對這些物件進行管理,每一個需要被管理的物件被稱為Bean,而管理這些Bean的容器,被稱為IoC容器。
控制反轉(IoC)在是一種通過描述來生成或者獲取物件的技術,在Spring Boot中,我們經常是通過註解來建立物件,這裡只談註解,無視XML。
Spring Boot在啟動的過程中,會去掃描需要被管理的Bean,將Bean裝載到IoC容器中,根據依賴關係進行例項化物件,最後進行依賴注入。也就是說,在專案啟動完成後,所有的Bean都已經例項化完成,並已在相應的地方注入完成,訪問程式使用到的這些物件都只是在直接呼叫物件例項,並不會出現一個new的過程,因為這些物件在專案啟動的時候就已經都被new出來了。
Bean的建立方式有很多種,最常用的是@Component,@Service,@Repository等等直接放在類頭上的註解,都是一些標記,用於被收集後反射再例項化最終賦值達到注入的目的,可瞭解的應該就是其中的一些附帶規則:條件裝配,就是如果存在某個Bean時(也可以判斷不存在時)再裝配當前這個Bean,不過條件裝配我覺得應用場景並不常見。
這裡主要是要挖一挖@Bean這個註解的使用。
用@Bean建立Bean
@Bean註解是隻能用在方法上,標誌該方法需要建立一個Bean,方法返回的物件就是建立Bean的目標物件。
建一個User實體類:
public class User { private int id = 2; private String name = "我是User類的小光"; private String sex = "我是難的"; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "User{" + "id=" + id + ", name=`" + name + ``` + ", sex=`" + sex + ``` + `}`; } }
示例:
@Configuration public class Config { @Bean public String aaa(){ return "你大爺"; } @Bean public User bbb(){ return new User(); } }
這個Config類,建立了兩個Bean,建立的物件一個是String物件一個是User物件,可以是任何物件,我這裡為了更加直白的表達“Bean”是什麼東西,特意使用了String來做例子。
當然了 bean的名字是可以定的 以下三種定義方式都可以:
@Bean(name = "aaa") @Bean("aaa") @Bean
第三種方式沒有指定名字,那麼框架會將方法名作為bean的名字,注意:是方法原名,不會是小駝峰,只有應用在類上的註解建立的bean才會是小駝峰,@Bean是方法上的註解。
你的腦補絕對有用
此時,你可以腦補一下框架是怎麼建立Bean的:
HashMap map = new HashMap(); map.put("aaa",new String("你大爺")); map.put("bbb",new User());
所謂IoC容器,就是一個集合,裝了很多物件,你要用哪個物件,Spring就會從集合裡取給你。
—–>當然了,雖然你的腦補不一定完全正確,但是你的腦補絕對有用。
@Bean註解受不受@ComponentScan的影響?
@ComponentScan註解定義掃描路徑,也就是說,Spring Boot只會掃描指定路徑之內的類,遇到特殊註解就會開始進行Bean的收集工作。
在Spring Boot的啟動類main方法上有@SpringBootApplication註解,點進去之後可以看到它裡面包含的有@ComponentScan,所以儘管我們沒有手動指定掃描範圍,但是預設是以啟動類所在的包作為一個掃描範圍。
但這裡要說明的是,@ComponentScan這個註解,它是用來約束@Configuration,@Controller,@Service,@Repository,@Component等作用在類上註解,但是它管不了@Bean註解,只要專案中有@Bean註解,即使身處啟動類的外層,不在@ComponentScan的掃描範圍之內,該Bean也會存在,只是沒有被掃描裝配,也就是可以獲取,但無法注入。
是否一定要和@Configuration一起用?
我的答案是:是的!
如果你沒有加@Configuration註解,那麼Spring將不會掃描這個類,但是你後期獲取該Bean的時候,Spring可以找到它,也就是說,如果沒有加@Configuration註解,那麼專案啟動後是沒有該Bean的,只有當你獲取該Bean的時候,才會進行例項化。
獲取你的Bean
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); Object aaa = context.getBean("aaa"); System.out.println(aaa.toString()); Object bbb = context.getBean("bb"); System.out.println(bbb.toString());
該示例程式碼獲取的是Config類中的aaa和bbb兩個Bean。
注意,它並不是獲取容器中的Bean,它是根據你提供的class和Bean名稱跑去執行了一下方法,新new了一個物件給你,再有,這個Bean名稱必須用原名,大小寫不打折的。
小提示:無論類的頭上有沒有@Configuration註解,該方式都可以成功,當然,如果類的頭上有@Configuration註解,那麼該Bean的返回物件可以直接在別處進行注入。
如果你要獲取類上的Bean,可以採用下面這個工具類來獲取:
@Component public class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (null == SpringUtil.applicationContext) { SpringUtil.applicationContext = applicationContext; } } // 獲取applicationContext public static ApplicationContext getApplicationContext() { return applicationContext; } // 通過name獲取 Bean. public static Object getBean(String name) { return getApplicationContext().getBean(name); } // 通過class獲取Bean. public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } // 通過name,以及Clazz返回指定的Bean public static <T> T getBean(String name, Class<T> clazz) { return getApplicationContext().getBean(name, clazz); } }