本文將從純xml模式、xml和註解結合、純註解的方式講解Spring IOC容器的配置和相關應用。
純XML模式
例項化Bean的三種方式:
- 使用無參建構函式
預設情況下,會使用反射呼叫無參建構函式來建立物件。
<bean id="connectionUtils" class="com.mmc.ioc.utils.ConnectionUtils"></bean>
- 使用靜態方法建立
在實際開發中,我們使用的方法有時候不是通過建構函式建立出來的,他可能在建立的時候會做很多額外的操作。此時會提供一個建立物件的方法,如果這個方法是static修飾的,就是用這種配置方式。
<bean id="druidUtils" class="com.mmc.ioc.utils.DruidUtils" factory-method="getInstance"></bean>
- 使用例項化方法建立
當方法不是靜態的時候,用這種方式
<bean id="connectionUtils" class="com.mmc.ioc.utils.ConnectionUtils"></bean>
<bean id="account" factory-bean="connectionUtils" factory-method="createAccount"></bean>
bean的作用範圍和宣告週期
常用的是singleton【預設】(單例模式)和prototype(原型模式或多例模式)。通過scope屬性可以進行配置
<bean id="account" factory-bean="connectionUtils" factory-method="createAccount" scope="singleton"></bean>
不同作用範圍的生命週期
單例模式:singleton
物件建立:當建立容器時,物件就被建立
物件活著:只要容器在,物件一直活著
物件死亡:當容器銷燬,物件就被銷燬
總結:單例模式的bean物件生命週期與容器相同
多例模式:prototype
物件建立:當使用物件時,建立新的物件例項
物件活著:只要物件在使用中,就一直活著
物件死亡:當物件長時間不用時,被垃圾回收器回收
總結:多例模式的bean物件,spring框架只負責建立,不負責銷燬。
Bean的標籤屬性
- id屬性: ⽤於給bean提供⼀個唯⼀標識。在⼀個標籤內部,標識必須唯⼀。
- class屬性:⽤於指定建立Bean物件的全限定類名。
- name屬性:⽤於給bean提供⼀個或多個名稱。多個名稱⽤空格分隔。
- factory-bean屬性:⽤於指定建立當前bean物件的⼯⼚bean的唯⼀標識。當指定了此屬性之後,
class屬性失效。 - factory-method屬性:⽤於指定建立當前bean物件的⼯⼚⽅法,如配合factory-bean屬性使⽤,
則class屬性失效。如配合class屬性使⽤,則⽅法必須是static的。 - scope屬性:⽤於指定bean物件的作⽤範圍。通常情況下就是singleton。當要⽤到多例模式時,
可以配置為prototype。 - init-method屬性:⽤於指定bean物件的初始化⽅法,此⽅法會在bean物件裝配後調⽤。必須是
⼀個⽆參⽅法。 - destory-method屬性:⽤於指定bean物件的銷燬⽅法,此⽅法會在bean物件銷燬前執⾏。它只
能為scope是singleton時起作⽤。
DI依賴注入
- 按照注入的方式分類
- 建構函式注入:就是利用帶參建構函式實現對類成員的屬性賦值
<bean id="account" class="com.mmc.ioc.bean.Account">
<constructor-arg name="cardNo" value="123"></constructor-arg>
<constructor-arg name="money" value="23"></constructor-arg>
<constructor-arg name="name" value="aa"></constructor-arg>
</bean>
- set方法注入:通過類成員的set方法實現資料注入
<bean id="account" class="com.mmc.ioc.bean.Account">
<property name="name" value="mmc"></property>
<property name="cardNo" value="abc"></property>
<property name="money" value="22"></property>
</bean>
- 按照注入的資料型別分類
- 基本資料型別和String
- 其他Bean型別
- 複雜型別(集合型別)
基本型別使用value,其他bean型別使用ref,複雜型別使用對應的array、map、set標籤
<bean id="user" class="com.mmc.ioc.bean.User">
<property name="id" value="1"></property>
<property name="account" ref="account"></property>
<property name="list">
<array>
<value>aa</value>
<value>bb</value>
</array>
</property>
<property name="map">
<map>
<entry key="a" value="1"></entry>
<entry key="b" value="2"></entry>
</map>
</property>
</bean>
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置spring ioc容器的配置檔案-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--使用監聽器啟動Spring的IOC容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
xml與註解結合的方式
注意:實際開發中,純xml模式使用已經很少了,引入註解功能,不需要引入額外的jar包。xml+註解結合模式中,xml檔案依然存在,所以Spring IOC容器的啟動仍然從載入xml開始。
一般來說第三方jar包裡面的bean定義在xml裡面,自己開發的bean使用註解。
將第三方jar包的bean放入容器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!--包掃描-->
<context:component-scan base-package="com.mmc.ioc"></context:component-scan>
<!--引入配置檔案-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
xml中標籤與註解的對應
xml形式 | 註解 |
---|---|
標籤 | @Component,註解加在類上。預設情況下bean的id為類名(首字母小寫)。另外,針對分層程式碼開發提供了@Componenet的三種別名@Controller、 |
@Service、@Repository分別⽤於控制層類、服務層類、dao層類的bean定義,這
四個註解的⽤法完全⼀樣,只是為了更清晰的區分⽽已
標籤的scope屬性 | @Scope("prototype")
DI依賴注入的註解實現方式
- @Autowired(推薦使用)
@Autowired為Spring提供的註解。策略是按型別注入
public class TransferServiceImpl implements TransferService {
@Autowired
private AccountDao accountDao;
}
如上程式碼所示,這樣裝配會去spring容器中找到型別為AccountDao的bean,然後將其中如。但如果一個型別有多個bean怎麼辦呢?可以配合@Qualifier("bean的id")使用。
public class TransferServiceImpl implements TransferService {
@Autowired
@Qualifier("jdbcAccountDao")
private AccountDao accountDao;
}
- @Resource
@Resource註解由j2EE提,如果指定了name或type就會根據指定的來,如果都沒有指定就自動按照ByName方式裝配。
注意:@Resource在Jdk11中已經移除,如果要使用,需要單獨引入jar包。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
在Servlet類裡面獲取applicationContext
public class TransferServlet extends HttpServlet {
@Override
public void init() throws ServletException {
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
ProxyFactory proxyFactory = (ProxyFactory) webApplicationContext.getBean("proxyFactory");
transferService= (TransferService) proxyFactory.getJdkProxy(webApplicationContext.getBean("transferService"));
}
}
純註解模式
將xml配置改為java程式碼:
在配置類上宣告@Configuration,表明是配置類。
@Configuration
@ComponentScan("com.mmc.ioc")
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
@Value("${jdbc.driver}")
private String driverClass;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource=new DruidDataSource();
druidDataSource.setDriverClassName(driverClass);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
如果還有其他配置類,可以通過@Import引入進來。
web.xml配置如下:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--告訴ContextLoaderListener是註解方式啟動ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!--配置spring ioc容器的配置檔案-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.mmc.ioc.SpringConfig</param-value>
</context-param>
<!--使用監聽器啟動Spring的IOC容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
高階特性
延遲載入
xml方式:
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="true" />
也可以在容器層次配置預設釋放延遲載入,如:
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>
註解方式:
@Lazy註解
@Bean
@Lazy
public DataSource dataSource(){
}
FactoryBean
Spring中的Bean有兩種,一種是普通bean,一種是工廠bean(FactoryBean),FactoryBean可以生產某一個型別的Bean例項,也就是說我們可以藉助它自定義Bean的建立過程。
@Component("user")
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
User user=new User();
Account account=new Account();
account.setName("mmc");
user.setAccount(account);
List<String> list=new ArrayList<>();
list.add("a");
user.setList(list);
user.setId(2);
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
執行測試程式碼,取出beanname為user的物件。
@Test
public void testAno(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
Object user = applicationContext.getBean("user");
System.out.println(user);
}
執行結果如下:
可以看出雖然是UserFactoryBean放入了容器,但是取出來的卻是User物件。這就是FactoryBean的作用。
Spring擴充套件介面和方法
- BeanNameAware
- BeanFactoryAware
- ApplicationContextAware
- InitializingBean
- DisposableBean
- @PostConstruct
- @PreDestroy
- init-method
- destroy-method
- BeanPostProcessor
- BeanFactoryPostProcessor
從獲取Spring裡的東西來分有:
- BeanNameAware
- BeanFactoryAware
- ApplicationContextAware
初始化:
- @PostConstruct
- InitializingBean
- init-method
銷燬:
- @PreDestroy
- DisposableBean
- destroy-method
初始化和銷燬的執行先後順序都是註解->介面->xml
使用示例:
<bean id="account" class="com.mmc.ioc.bean.Account" init-method="initMethod" destroy-method="destroyMethod"></bean>
public class Account implements BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware:"+beanFactory);
}
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware:"+name);
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContextAware:"+applicationContext);
}
@PostConstruct
public void postConstruct(){
System.out.println("postConstruct");
}
@PreDestroy
public void preDestroy(){
System.out.println("preDestroy");
}
public void initMethod(){
System.out.println("init-method");
}
public void destroyMethod(){
System.out.println("destroy-method");
}
}
全域性的:
BeanFactoryPostProcessor是在BeanFactory初始化之後可以處理一些事情,是針對Bean的工廠進行處理,典型應用:PropertyPlaceholderConfigurer
BeanPostProcessor是針對所有的bean進行攔截進行處理,使用如下:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("account")){
System.out.println("BeanPostProcessor before"+bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("account")){
System.out.println("BeanPostProcessor after"+bean);
}
return bean;
}
}
全部配置好後,列印檢視執行先後順序:
可以得到下面的執行流程圖:
高頻面試題:
BeanFactory、FactoryBean、ApplicationContext的區別
- BeanFactory是Spring框架中IOC容器的頂層介面,它只是用來定義一些基礎功能
- ApplicationContext是它的一個子介面,它擁有更多的功能,如國際化支援和資源訪問等等。
- FactoryBean:一般情況下,Spring通過反射機制例項化Bean,在某些情況下,例項化bean過程比較複雜,這時配置起來就比較麻煩。如果採用編碼的方式會簡單一些。於是Spring給我們提供了FactoryBean的介面,使用者就可以通過實現這個介面來自定義例項化Bean的邏輯。
總結:BeanFactory是負責生產和管理Bean的一個工廠介面,提供一個Spring Ioc容器規範。FactoryBean是一種Bean建立的方法,對Bean的一種擴充套件。
類圖如下: