spring boot基于Java的容器配置講解
spring容器是負(fù)責(zé)實(shí)例化、配置、組裝組件的容器。
容器的配置有很多,常用的是xml、Java注解和Java代碼。
在spring中Ioc容器相關(guān)部分是context和beans中。其中context-support保存著許多線程的容器實(shí)現(xiàn)。比如AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext。兩者只有接收的目標(biāo)不同,前者接收J(rèn)ava類后者接收Xml文件。但作為spring容器的不同實(shí)現(xiàn)殊途同歸。
下面我通過(guò)spring文檔中的介紹來(lái)自己過(guò)一遍容器配置,來(lái)加深印象。這里我使用的是springboot。所以有些基于web.xml的配置不會(huì)涉及到。
@Bean和@Configuration
@Configuration注解的類,表示這個(gè)類是一個(gè)配置類,類似于<beans></beans>或者.xml文件。
@Bean注解用來(lái)說(shuō)明使用springIoc容器管理一個(gè)新對(duì)象的實(shí)例化、配置和初始化。類似于<bean></bean>,默認(rèn)情況下,bean名稱就是方法名稱.
例子:
@Configuration public class Conf { @Bean public HelloService helloService() { return new HelloServiceImpl(); } }
這種配置方式就類似于xml配置中的
<beans> <bean id="helloService" class="com.dust.service.impl.HelloServiceImpl" /> </beans>
等價(jià)于注解配置中的
@Service public class HelloServiceIMpl implements HelloService { @Override public String hello() { return "hello world"; } }
使用AnnotationConfigApplicationContext實(shí)例化Spring容器
這是在spring3.0加入的功能,除了接收@Configuration注解的類作為輸入類之外還可以接受使用JSR-330元數(shù)據(jù)注解的簡(jiǎn)單類和@Component類。
當(dāng)@Configuration注解的類作為輸入時(shí),@Configuration類本身會(huì)被注冊(cè)為一個(gè)bean,在這個(gè)類中所有用@Bean注解的方法都會(huì)被定義為一個(gè)bean。
具體有哪些類型的bean可以方法遍歷打印容器中的bean。
public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(Conf.class); HelloService helloService = context.getBean(HelloService.class); String hello = helloService.hello(); System.out.println(hello); }
該實(shí)例的步驟為:
1. 創(chuàng)建AnnotationConfigApplicationContext容器對(duì)象,同時(shí)將@Configuration注解的Conf.class作為參數(shù)傳入。
2. 容器回根據(jù)傳入的Conf類來(lái)構(gòu)建bean。其中就有helloService
3. 通過(guò)bean的對(duì)象類型獲取到容器中保管的對(duì)象。
4. 執(zhí)行對(duì)象方法
但是AnnotationConfigApplicationContext并不僅使用@Configuration類。任何@Component或JSR-330注解的類都可以作為輸入提供給構(gòu)造函數(shù)。例如:
public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(HelloServiceImpl.class, A.class, B.class); HelloService helloService = context.getBean(HelloService.class); String hello = helloService.hello(); System.out.println(hello); }
上面假設(shè)MyServiceImpl、A和B都用了Spring的依賴注入的注解,例如@Autowired。
使用register(Class<?>…)的方式構(gòu)建容器
也可以使用無(wú)參構(gòu)造函數(shù)實(shí)例化AnnotationConfigApplicationContext,然后使用register()方法配置。當(dāng)使用編程方式構(gòu)建AnnotationConfigApplicationContext時(shí),這種方法特別有用。
例子:
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); HelloService helloService = context.getBean(HelloService.class); String hello = helloService.hello(); System.out.println(hello); }
其中的refresh方法是一個(gè)初始化工作。否則注冊(cè)的類并不會(huì)被生成bean。
使用scan(String …)組件掃描
組件掃描,只需要設(shè)置好對(duì)應(yīng)包路徑,spring容器回自動(dòng)掃描包下面所有能夠被容器初始化的Java類。
使用注解:
@Configuration @ComponentScan("com.example.springdemo.beans") public class Conf { @Bean public HelloService helloService() { //用這種方法創(chuàng)建的service相當(dāng)于用@Service注解標(biāo)注 return new HelloServiceImpl(); } }
在該路徑下還有一個(gè)配置文件:
@Configuration public class Conf2 { @Bean public ByeService byeService() { //用這種方法創(chuàng)建的service相當(dāng)于用@Service注解標(biāo)注 return new ByeServiceImpl(); } }
然后是初始化容器:
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); ByeService byeService = context.getBean(ByeService.class); String hello = byeService.bye(); System.out.println(hello); }
可以看到,雖然傳入的是Conf類,但是由于包掃描機(jī)制,該容器同時(shí)創(chuàng)建了Conf2類中的bean。
這就類似xml配置中的:
<beans> <context:component-scan base-package="com.example.springdemo.beans"/> </beans>
還可以直接調(diào)用容器的掃描方法
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // context.register(Conf.class); context.scan("com.example.springdemo.beans"); context.refresh(); ByeService byeService = context.getBean(ByeService.class); String hello = byeService.bye(); System.out.println(hello); }
springboot中的包掃描
springboot通過(guò)main方法啟動(dòng),其中的注解為@SpringBootApplication。通過(guò)查看該注解的代碼可以發(fā)現(xiàn)一下代碼段:
@AliasFor( annotation = ComponentScan.class, attribute = "basePackages" )
由此可以知道@SpringBootApplication注解包括了包掃描注解,同時(shí)掃描的是該類的目錄以及子目錄的所有可以被spring容器初始化的類
AnnotationConfigWebApplicationContext對(duì)于web應(yīng)用的支持
AnnotationConfigApplicationContext在WebApplicationContext中的變體為 AnnotationConfigWebApplicationContext。當(dāng)配置Spring ContextLoaderListener servlet 監(jiān)聽器、Spring MVC DispatcherServlet的時(shí)候,可以用此實(shí)現(xiàn)。
Bean依賴
@Bean注解方法可以具有描述構(gòu)建該bean所需依賴關(guān)系的任意數(shù)量的參數(shù)。依賴的必須也是Ioc容器中注冊(cè)的bean。
將上面的代碼修改后如下:
@Configuration public class Conf { @Bean public HelloService helloService(ByeService byeService) { return new HelloServiceImpl(byeService); } @Bean public ByeService byeService() { return new ByeServiceImpl(); } }
public class HelloServiceImpl implements HelloService { private ByeService byeService; public HelloServiceImpl(ByeService byeService) { this.byeService = byeService; } @Override public String hello() { return "hello world" + byeService.bye(); } }
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); HelloService helloService = context.getBean(HelloService.class); String hello = helloService.hello(); System.out.println(hello); ByeService byeService = context.getBean(ByeService.class); String bye = byeService.bye(); System.out.println(bye); }
輸出結(jié)果:
hello worldGoodbye!
Goodbye!
這種解決原理和基于構(gòu)造函數(shù)的依賴注入幾乎相同。
生命周期回調(diào)
@Bean注解支持任意的初始化和銷毀回調(diào)方法,這與Spring XML 中bean元素上的init方法和destroy-method屬性非常相似:
@Bean(initMethod = "init") public HelloService helloService(ByeService byeService) { return new HelloServiceImpl(byeService); } @Bean(destroyMethod = "destroy") public ByeService byeService() { return new ByeServiceImpl(); } public interface ByeService { String bye(); void destroy(); } public interface HelloService { String hello(); void init(); } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); context.close(); }
輸出如下:
init helloService!!
destroy byeService!
默認(rèn)情況下,Ioc容器關(guān)閉后所有bean都會(huì)被銷毀,但是如果要引入一個(gè)生命周期在應(yīng)用程序之外進(jìn)行管理的組件,例如:DataSource。那么只需要將@Bean(destroyMethod =””)添加到你的bean定義中即可禁用默認(rèn)(推測(cè))模式。
@Bean(destroyMethod="") public DataSource dataSource() throws NamingException { return (DataSource) jndiTemplate.lookup("MyDS"); }
當(dāng)然,初始化的時(shí)候也可以先執(zhí)行對(duì)應(yīng)方法,而不用交給Ioc容器
@Bean public HelloService helloService(ByeService byeService) { HelloService helloService = new HelloServiceImpl(byeService); helloService.init(); return helloService; }
@Scope和scope 代理
Scope描述的是Spring容器如何新建Bean實(shí)例的。
- Singleton:一個(gè)Spring容器中只有一個(gè)Bean的實(shí)例,此為Spring的默認(rèn)配置,全容器共享一個(gè)實(shí)例。
- Prototype:每次調(diào)用新建一個(gè)Bean實(shí)例。
- Request:Web項(xiàng)目中,給每一個(gè) http request 新建一個(gè)Bean實(shí)例。
- Session:Web項(xiàng)目中,給每一個(gè) http session 新建一個(gè)Bean實(shí)例。
- GlobalSession:這個(gè)只在portal應(yīng)用中有用,給每一個(gè) global http session 新建一個(gè)Bean實(shí)例。
@Bean //每次調(diào)用就創(chuàng)建一個(gè)新的bean @Scope("prototype") public UserInfo userInfo() { return new UserInfo(); } @Bean public UserService userService() { UserService userService = new UserServiceImpl(); userService.init(userInfo()); return userService; }
測(cè)試代碼:
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); UserService userService = context.getBean(UserService.class); UserService userService2 = context.getBean(UserService.class); UserInfo userInfo = context.getBean(UserInfo.class); UserInfo userInfo2 = context.getBean(UserInfo.class); System.out.println(userService == userService2); System.out.println(userInfo == userInfo2); }
輸出:
true
false
自定義Bean命名
通常,bean的名稱是bean的方法名,但是可以通過(guò)name屬性重命名。有時(shí)一個(gè)單一的bean需要給出多個(gè)名稱,稱為bean別名。為了實(shí)現(xiàn)這個(gè)目標(biāo),@Bean注解的name屬性接受一個(gè)String數(shù)組。
@Bean(name = {"user", "userService", "User"}) public UserService userService() { UserService userService = new UserServiceImpl(); userService.init(userInfo()); return userService; }
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); Object user = context.getBean("user"); Object userService = context.getBean("userService"); Object User = context.getBean("User"); System.out.println(user == userService); System.out.println(user == User); System.out.println(userService == User); }
輸出:
true
true
true
Bean描述
有時(shí)候需要提供一個(gè)詳細(xì)的bean描述文本是非常有用的。當(dāng)對(duì)bean暴露(可能通過(guò)JMX)進(jìn)行監(jiān)控使,特別有用??梢允褂聾Description注解對(duì)Bean添加描述:
@Bean(name = {"user", "userService", "User"}) @Description("這是用戶服務(wù)對(duì)象") public UserService userService() { UserService userService = new UserServiceImpl(); userService.init(userInfo()); return userService; }
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); String description = context.getBeanDefinition("user").getDescription(); System.out.println(description); }
輸出:
這是用戶服務(wù)對(duì)象
基于Java組合配置
使用@Import注解
和Spring XML文件中使用元素來(lái)幫助模塊化配置類似,@Import注解允許從另一個(gè)配置類加載@Bean定義:
@Configuration @Import(UserConf.class) public class Conf { @Bean(initMethod = "init") public HelloService helloService(ByeService byeService) { //用這種方法創(chuàng)建的service相當(dāng)于用@Service注解標(biāo)注 return new HelloServiceImpl(byeService); } @Bean(destroyMethod = "destroy") public ByeService byeService() { return new ByeServiceImpl(); } }
@Configuration public class UserConf { @Bean //每次調(diào)用就創(chuàng)建一個(gè)新的bean @Scope("prototype") public UserInfo userInfo() { return new UserInfo(); } @Bean(name = {"user", "userService", "User"}) @Description("這是用戶服務(wù)對(duì)象") public UserService userService() { UserService userService = new UserServiceImpl(); userService.init(userInfo()); return userService; } }
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Conf.class); context.refresh(); String description = context.getBeanDefinition("user").getDescription(); System.out.println(description); }
這種方法簡(jiǎn)化了容器實(shí)例化,因?yàn)橹恍枰幚硪粋€(gè)類,而不是需要開發(fā)人員在構(gòu)建期間記住大量的@Configuration注解類。
Java and XML 混合配置
Java配置并不能100%替代xml配置,因此Ioc容器支持兩者混合配置。不過(guò)這里有個(gè)區(qū)別就是以xml為中心還是以Java配置為中心。
以XML為中心
@Configuration public class DataSourceConf { @Autowired private DataSource dataSource; @Bean public DataSourceService dataSource() { return new DataSourceerviceImpl(dataSource); } }
jdbc.url=jdbc:mysql://39.108.119.174:3306/dust jdbc.username=root jdbc.password=123456
<beans> <context:annotation-config/> <context:property-placeholder location="classpath:jdbc.properties"/> <bean class="com.example.springdemo.beans.DataSourceConf"/> <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml"); DataSourceService dataSourceService = context.getBean(DataSourceService.class); System.out.println(dataSourceService.toString()); }
以Java類為中心
<beans> <context:property-placeholder location="classpath:jdbc.properties"/> </beans>
@Configuration @ImportResource("classpath:spring/datasource.xml") public class DataSourceConf { @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSourceService dataSource() { return new DataSourceerviceImpl(url, username, password); } }
public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.scan("com.example.springdemo.beans"); context.refresh(); DataSourceService dataSourceService = context.getBean(DataSourceService.class); System.out.println(dataSourceService.toString()); // ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml"); // DataSourceService dataSourceService = context.getBean(DataSourceService.class); // System.out.println(dataSourceService.toString()); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot?Webflux創(chuàng)建TCP/UDP?server并使用handler解析數(shù)據(jù)
這篇文章主要介紹了SpringBoot?Webflux創(chuàng)建TCP/UDP?server并使用handler解析數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Java?GenericObjectPool?對(duì)象池化技術(shù)之SpringBoot?sftp?連接池工具類詳解
這篇文章主要介紹了Java?GenericObjectPool?對(duì)象池化技術(shù)之SpringBoot?sftp?連接池工具類詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04IDEA啟動(dòng)Tomcat報(bào)Unrecognized option: --add-opens=java
這篇文章主要為大家介紹了解決IDEA啟動(dòng)Tomcat報(bào)Unrecognized option: --add-opens=java.base/java.lang=ALL-UNNAMED的方法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn)
這篇文章主要介紹了SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn),MethodArgumentResolver采用一種策略模式,在Handler的方法被調(diào)用前,Spring MVC會(huì)自動(dòng)將HTTP請(qǐng)求中的參數(shù)轉(zhuǎn)換成方法參數(shù)2023-04-04IntellJ idea使用FileWatch實(shí)時(shí)編譯less文件的方法
這篇文章主要介紹了IntellJ idea使用FileWatch實(shí)時(shí)編譯less文件的相關(guān)資料,需要的朋友可以參考下2018-02-02ibatis結(jié)合oracle批量插入三種方法的測(cè)評(píng)
今天小編就為大家分享一篇關(guān)于ibatis結(jié)合oracle批量插入三種方法的測(cè)評(píng),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12springboot整合RabbitMQ 中的 TTL實(shí)例代碼
TTL 是 RabbitMQ 中一個(gè)消息或者隊(duì)列的屬性,表明一條消息或者該隊(duì)列中的所有消息的最大存活時(shí)間,單位是毫秒,這篇文章主要介紹了springboot整合RabbitMQ 中的 TTL,需要的朋友可以參考下2022-09-09JAXB命名空間及前綴_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要給大家介紹了關(guān)于JAXB命名空間及前綴的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08Java?Spring的核心與設(shè)計(jì)思想你知道嗎
這篇文章主要為大家詳細(xì)介紹了Java?Spring的核心與設(shè)計(jì)思想,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03