一、背景
Spring使用BeanFactory來產(chǎn)生和管理Bean,它是工廠模式的實現(xiàn)忆畅。BeanFactory使用控制反轉(zhuǎn)模式將應用的配置和依賴性規(guī)范與實際的應用程序代碼分開六剥。BeanFactory使用依賴注入的方式給組件提供依賴 。
二庞呕、準備工作
準備工作:運行一個Springboot工程新建一個Springboot工程,由于我平時使用的版本是2.1.3.RELEASE新翎,所以我選擇了自己的常用版本。使用maven項目住练,引入spring-boot-starter依賴包.OK一個最簡單的環(huán)境已經(jīng)準備好了地啰。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.3.RELEASE</version>
</dependency
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
}
}
三、定義一個Bean有哪些方式讲逛?
3.1 @Component注解方式
啟動類默認掃描路徑為當前所在目錄及子目錄亏吝,只要確保加了@Component,@Service,@Configuration等注解,Spring就會將該Bean注入到容器,這也應該是我們最常用到的一種方式盏混。
@Component
public class MyCompBean {
private String name="myCompBean";
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//@Component
Object myCompBean = context.getBean("myCompBean");
System.out.println(myCompBean);
}
}
3.2 @Bean方式
這個注解我們用的應該也挺多的蔚鸥,大家都很熟悉,就不過多介紹了许赃。
@Data
public class MyAnnoBean {
private String name = "myAnnoBean";
}
@Configuration
public class MyConfig {
@Bean
public MyAnnoBean myAnnoBean(){
return new MyAnnoBean();
}
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//anno
Object myAnnoBean = context.getBean("myAnnoBean");
System.out.println(myAnnoBean);
}
}
3.3 @Named
Jsr330的規(guī)范止喷,了解不多,不過多描述混聊,就把它當作@Component使用就好了
需要在pom中引入javax.inject依賴
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Data
@Named
public class MyNamedBean {
private String name="myNamedBean";
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//@Named
Object myNamedBean = context.getBean("myNamedBean");
System.out.println(myNamedBean);
}
}
3.4 XML方式
我相信大部分人對于Spring的XML配置文件不會陌生(實際上自從Springboot問世以來弹谁,后續(xù)使用Spring的人還真就不會再去接觸XML了),但是作為一個使用Spring的“老人”,我們肯定是不會忘本的僵闯。
在resources目錄下新建beans.xml
<bean class="org.example.springbean.xml.MyXmlBean" id="myXmlBean"></bean>
由于我們使用Springboot啟動卧抗,因此xml還需要通過@ImportResource注解來引入一下
@Configuration
@ImportResource(locations={"beans.xml"})
public class XmlConfig {
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//xml
Object myXmlBean= context.getBean("myXmlBean");
System.out.println(myXmlBean);
}
}
3.5 Properties方式
在resources目錄下新建beans.properties
myPropertiesBean.(class)=org.example.springbean.props.MyPropertiesBean
這個方式是在使用@ImportResource注解引入XML文件的時候發(fā)現(xiàn)注解里面可以指定reader,默認的實現(xiàn)是XmlBeanDefinitionReader鳖粟,這里需要指定reader = PropertiesBeanDefinitionReader.class
@Configuration
@ImportResource(locations={"beans.properties"},reader = PropertiesBeanDefinitionReader.class)
public class PropsConfig {
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//properties
Object myPropertiesBean = context.getBean("myPropertiesBean");
System.out.println(myPropertiesBean);
}
}
3.6 Groovy方式
這個姑且也算是一種方式吧社裆,跟xml差不多除了需要制定reader = GroovyBeanDefinitionReader.class之外,還需要額外引入一個groovy-xml的依賴
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-xml</artifactId>
<version>3.0.8</version>
</dependency>
在resources目錄下新建一個beans.groovy
import org.example.springbean.groovy.MyGroovyBean;
beans{
myGroovyBean(MyGroovyBean){}
}
@Data
public class MyGroovyBean {
private String name="myGroovyBean";
}
@Configuration
@ImportResource(locations = {"beans.groovy"},reader = GroovyBeanDefinitionReader.class)
public class GroovyConfig {
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//groovy
Object myGroovyBean = context.getBean("myGroovyBean");
System.out.println(myGroovyBean);
}
}
從加載的方式上來看向图,properties泳秀,xml,groovy這三種方式可以歸為一類榄攀,我們完全可以依葫蘆畫瓢自定義一個自己的文件格式嗜傅,然后實現(xiàn)一個BeanDefinitionReader。只是這3種是Spring已經(jīng)給我們實現(xiàn)好了的可以拿來直接用檩赢。有一句說一句吕嘀,工作了這么久我也是只用過Xml方式。贞瞒。偶房。
3.7 FactoryBean方式
@Data
public class MyFacBean {
private String name = "myFacBean";
}
@Component
public class MyFacBeanFactory implements FactoryBean<MyFacBean> {
@Override
public MyFacBean getObject() throws Exception {
return new MyFacBean();
}
@Override
public Class<?> getObjectType() {
return MyFacBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//factoryBean
//1.找不到這個bean
//myFacBean = (MyFacBean)context.getBean("myFacBean");
//2.通過工廠的名稱獲取bean獲取的是實際的bean
Object myFacBean = context.getBean("myFacBeanFactory");
System.out.println(myFacBean);
//3.獲取工廠bean必須在name前加上一個 &
Object myFacBeanFactory = context.getBean("&myFacBeanFactory");
System.out.println(myFacBeanFactory);
}
}
}
3.8 @Import
public class MyImportBean {
private String name="myImportBean";
}
@SpringBootApplication
@Import(value = {MyImportBean.class})
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//必須使用全路徑
//Object myImportBean = context.getBean("myImportBean");
Object myImportBean = context.getBean("org.example.springbean.ipt.MyImportBean");
System.out.println(myImportBean);
}
}
}
3.9 ImportSelector
@Data
public class MyImportSelectorBean {
private String name="myImportSelectorBean";
}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{MyImportSelectorBean.class.getName()};
}
}
@SpringBootApplication
@Import(value = {MyImportSelector.class})
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//ImportSelector
Object myImportSelectorBean = context.getBean("org.example.springbean.ipt.MyImportSelectorBean");
System.out.println(myImportSelectorBean);
}
}
}
3.10 ImportBeanDefinitionRegistrar
@Data
public class MyImportBeanDefinitionRegistrarBean {
private String name="importBeanDefinitionRegistrar";
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(MyImportBeanDefinitionRegistrarBean.class);
registry.registerBeanDefinition("myImportBeanDefinitionRegistrarBean", beanDefinition);
}
}
@SpringBootApplication
@Import(value = {MyImportBeanDefinitionRegistrar.class})
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//ImportBeanDefinitionRegistrar
Object myImportBeanDefinitionRegistrarBean = context.getBean("myImportBeanDefinitionRegistrarBean");
System.out.println(myImportBeanDefinitionRegistrarBean);
}
}
}
3.11 BeanFactoryPostProcessor
@Data
public class MyBeanFactoryPostProcessorBean {
private String name="myBeanFactoryPostProcessorBean";
}
@Configuration
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerSingleton("myBeanFactoryPostProcessorBean",new MyBeanFactoryPostProcessorBean());
}
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//BeanFactoryPostProcessor
Object myBeanFactoryPostProcessorBean = context.getBean("myBeanFactoryPostProcessorBean");
System.out.println(myBeanFactoryPostProcessorBean);
}
}
}
3.12 BeanDefinitionRegistryPostProcessor
@Data
public class MyBeanDefinitionRegistryPostProcessorBean {
private String name = "myBeanDefinitionRegistryPostProcessorBean";
}
@Configuration
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RootBeanDefinition rbd = new RootBeanDefinition(MyBeanDefinitionRegistryPostProcessorBean.class);
registry.registerBeanDefinition("myBeanDefinitionRegistryPostProcessorBean", rbd);
//如果構(gòu)造參數(shù)特別復雜,可以借助BeanDefinitionBuilder
// AbstractBeanDefinition rbd2 = BeanDefinitionBuilder.rootBeanDefinition(MyBeanDefinitionRegistryPostProcessorBean.class)
// .addPropertyValue("name","myBeanDefinitionRegistryPostProcessorBean2")
// .getBeanDefinition();
// registry.registerBeanDefinition("myBeanDefinitionRegistryPostProcessorBean2", rbd2);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//同BeanFactoryPostProcessor军浆,優(yōu)先加載
}
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//BeanDefinitionRegistryPostProcessor
Object myBeanDefinitionRegistryPostProcessorBean = context.getBean("myBeanDefinitionRegistryPostProcessorBean");
System.out.println(myBeanDefinitionRegistryPostProcessorBean);
}
}
}
3.13 借助ApplicationContext
@Data
public class CustomBean {
private String name="customBean";
}
@Component
public class CustomContext {
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void init(){
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) this.applicationContext;
RootBeanDefinition rbd = new RootBeanDefinition(CustomBean.class);
beanDefinitionRegistry.registerBeanDefinition("customBean",rbd);
}
}
@SpringBootApplication
public class BootStarter {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BootStarter.class);
//custom
Object customBean = context.getBean("customBean");
System.out.println(customBean);
}
}
}
四棕洋、條條大路通羅馬
數(shù)了一下,上述大概列出了13種向Spring容器中注入Bean的方式乒融,不知道各位小伙伴們都用過哪些掰盘?有些方式咱們只需要稍微了解一下就可以了,但是如果想要學好Spring赞季,那么我們一定要了解其中比較重要的幾個比如ImportSelector愧捕、FactoryBean、BeanFactoryPostProcessor碟摆,因為日后的源碼中會經(jīng)常遇到他們晃财。
五、總結(jié)
Spring對于Bean的配置有兩種方式:
- 基于資源文件解析的方式典蜕,其中最常用的是XML配置
優(yōu)點:可以在后期維護的時候適當?shù)卣{(diào)整Bean管理模式断盛,并且只要遵循一定的命名規(guī)范,可以讓程序員不必關心Bean之間的依賴關系 愉舔。缺點 :系統(tǒng)越龐大钢猛,XML配置文件就越大,關系錯綜復雜轩缤,容易導致錯誤 命迈。
- 基于 JavaConfig 的注解配置方式
優(yōu)點:配置比較方便贩绕,我們只要在 service 層代碼設置即可實現(xiàn),不需要知道系統(tǒng)需要多少個 Bean壶愤,交給容器來注入就好了 淑倾。缺點:當你要修改或刪除一個 Bean 的時候,你無法確定到底有多少個其他的 Bean依賴于這個 Bean征椒。(解決方法 : 需要有嚴格的開發(fā)文檔娇哆,在修改實現(xiàn)時盡可能繼續(xù)遵守相應的 接口規(guī)則,避免使其他依賴于此的 Bean不可用勃救。)
聊完了Bean的配置方式碍讨,下次我們一起探討一下這些Bean是如何被Spring容器發(fā)現(xiàn)并組裝提供我們使用。
程序員的核心競爭力其實還是技術(shù)蒙秒,因此對技術(shù)還是要不斷的學習勃黍,關注 “IT 巔峰技術(shù)” 公眾號 ,該公眾號內(nèi)容定位:中高級開發(fā)晕讲、架構(gòu)師覆获、中層管理人員等中高端崗位服務的,除了技術(shù)交流外還有很多架構(gòu)思想和實戰(zhàn)案例益兄,作者是 《 消息中間件 RocketMQ 技術(shù)內(nèi)幕》 一書作者锻梳,同時也是 “RocketMQ 上海社區(qū)”聯(lián)合創(chuàng)始人箭券,曾就職于拼多多净捅、德邦等公司,現(xiàn)任上市快遞公司架構(gòu)負責人辩块,主要負責開發(fā)框架的搭建蛔六、中間件相關技術(shù)的二次開發(fā)和運維管理、混合云及基礎服務平臺的建設废亭。