本文將從純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的一種擴展。
類圖如下: