spring容器是負責實例化、配置、組裝組件的容器。java
容器的配置有不少,經常使用的是xml、Java註解和Java代碼。mysql
在spring中Ioc容器相關部分是context和beans中。其中context-support保存着許多線程的容器實現。好比AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext。二者只有接收的目標不一樣,前者接收Java類後者接收Xml文件。但做爲spring容器的不一樣實現異曲同工。git
下面我經過spring文檔中的介紹來本身過一遍容器配置,來加深印象。這裏我使用的是springboot。因此有些基於web.xml的配置不會涉及到。web
歡迎捉蟲spring
@Configuration註解的類,表示這個類是一個配置類,相似於<beans></beans>或者.xml文件。sql
@Bean註解用來講明使用springIoc容器管理一個新對象的實例化、配置和初始化。相似於<bean></bean>,默認狀況下,bean名稱就是方法名稱.編程
例子:數組
@Configuration
public class Conf {
@Bean
public HelloService helloService() {
return new HelloServiceImpl();
}
}
複製代碼
這種配置方式就相似於xml配置中的springboot
<beans>
<bean id="helloService" class="com.dust.service.impl.HelloServiceImpl" />
</beans>
複製代碼
等價於註解配置中的bash
@Service
public class HelloServiceIMpl implements HelloService {
@Override
public String hello() {
return "hello world";
}
}
複製代碼
這是在spring3.0加入的功能,除了接收@Configuration註解的類做爲輸入類以外還能夠接受使用JSR-330元數據註解的簡單類和@Component類。
當@Configuration註解的類做爲輸入時,@Configuration類自己會被註冊爲一個bean,在這個類中全部用@Bean註解的方法都會被定義爲一個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);
}
複製代碼
該實例的步驟爲:
1. 建立AnnotationConfigApplicationContext容器對象,同時將@Configuration註解的Conf.class做爲參數傳入。
2. 容器回根據傳入的Conf類來構建bean。其中就有helloService
3. 經過bean的對象類型獲取到容器中保管的對象。
4. 執行對象方法
複製代碼
可是AnnotationConfigApplicationContext並不只使用@Configuration類。任何@Component或JSR-330註解的類均可以做爲輸入提供給構造函數。例如:
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);
}
複製代碼
上面假設MyServiceImpl、A和B都用了Spring的依賴注入的註解,例如@Autowired。
也可使用無參構造函數實例化AnnotationConfigApplicationContext,而後使用register()方法配置。當使用編程方式構建AnnotationConfigApplicationContext時,這種方法特別有用。
例子:
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方法是一個初始化工做。不然註冊的類並不會被生成bean。
組件掃描,只須要設置好對應包路徑,spring容器回自動掃描包下面全部可以被容器初始化的Java類。
使用註解:
@Configuration
@ComponentScan("com.example.springdemo.beans")
public class Conf {
@Bean
public HelloService helloService() {
//用這種方法建立的service至關於用@Service註解標註
return new HelloServiceImpl();
}
}
複製代碼
在該路徑下還有一個配置文件:
@Configuration
public class Conf2 {
@Bean
public ByeService byeService() {
//用這種方法建立的service至關於用@Service註解標註
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類,可是因爲包掃描機制,該容器同時建立了Conf2類中的bean。
這就相似xml配置中的:
<beans>
<context:component-scan base-package="com.example.springdemo.beans"/>
</beans>
複製代碼
還能夠直接調用容器的掃描方法
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經過main方法啓動,其中的註解爲@SpringBootApplication。經過查看該註解的代碼能夠發現一下代碼段:
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
複製代碼
由此能夠知道@SpringBootApplication註解包括了包掃描註解,同時掃描的是該類的目錄以及子目錄的全部能夠被spring容器初始化的類
AnnotationConfigApplicationContext在WebApplicationContext中的變體爲 AnnotationConfigWebApplicationContext。當配置Spring ContextLoaderListener servlet 監聽器、Spring MVC DispatcherServlet的時候,能夠用此實現。
@Bean註解方法能夠具備描述構建該bean所需依賴關係的任意數量的參數。依賴的必須也是Ioc容器中註冊的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);
}
複製代碼
輸出結果:
hello worldGoodbye!
Goodbye!
這種解決原理和基於構造函數的依賴注入幾乎相同。
@Bean註解支持任意的初始化和銷燬回調方法,這與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!
默認狀況下,Ioc容器關閉後全部bean都會被銷燬,可是若是要引入一個生命週期在應用程序以外進行管理的組件,例如:DataSource。那麼只須要將@Bean(destroyMethod =」」)添加到你的bean定義中便可禁用默認(推測)模式。
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS");
}
複製代碼
固然,初始化的時候也能夠先執行對應方法,而不用交給Ioc容器
@Bean
public HelloService helloService(ByeService byeService) {
HelloService helloService = new HelloServiceImpl(byeService);
helloService.init();
return helloService;
}
複製代碼
Scope描述的是Spring容器如何新建Bean實例的。
@Bean
//每次調用就建立一個新的bean
@Scope("prototype")
public UserInfo userInfo() {
return new UserInfo();
}
@Bean
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();
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的方法名,可是能夠經過name屬性重命名。有時一個單一的bean須要給出多個名稱,稱爲bean別名。爲了實現這個目標,@Bean註解的name屬性接受一個String數組。
@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描述文本是很是有用的。當對bean暴露(可能經過JMX)進行監控使,特別有用。可使用@Description註解對Bean添加描述:
@Bean(name = {"user", "userService", "User"})
@Description("這是用戶服務對象")
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);
}
複製代碼
輸出:
這是用戶服務對象
和Spring XML文件中使用元素來幫助模塊化配置相似,@Import註解容許從另外一個配置類加載@Bean定義:
@Configuration
@Import(UserConf.class)
public class Conf {
@Bean(initMethod = "init")
public HelloService helloService(ByeService byeService) {
//用這種方法建立的service至關於用@Service註解標註
return new HelloServiceImpl(byeService);
}
@Bean(destroyMethod = "destroy")
public ByeService byeService() {
return new ByeServiceImpl();
}
}
複製代碼
@Configuration
public class UserConf {
@Bean
//每次調用就建立一個新的bean
@Scope("prototype")
public UserInfo userInfo() {
return new UserInfo();
}
@Bean(name = {"user", "userService", "User"})
@Description("這是用戶服務對象")
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);
}
複製代碼
這種方法簡化了容器實例化,由於只須要處理一個類,而不是須要開發人員在構建期間記住大量的@Configuration註解類。
Java配置並不能100%替代xml配置,所以Ioc容器支持二者混合配置。不過這裏有個區別就是以xml爲中心仍是以Java配置爲中心。
@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());
}
複製代碼
<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());
}
複製代碼