SpringIOC基礎知識總結

鄧曉暉發表於2020-11-23


image-20201122163930311

1.BeanFactory和ApplicationContext的區別:

BeanFactory是Spring框架中IOC容器的頂層介面,它只是用來定義一些基礎功能,定義一些基礎規範,而ApplicationContext是它的一個子介面,所以ApplicationContext是具備BeanFactory提供的全部功能。

通常我們稱BeanFactory為SpringIOC的基礎容器,ApplicationContext是容器的高階介面,比如BeanFactory擁有更多的功能,比如說國際化支援和資源訪問(xml、java配置類)等等。

2.例項化bean的三種方式:

⽅式⼀:使⽤⽆參建構函式

在預設情況下,它會通過反射調⽤⽆參建構函式來建立物件。如果類中沒有⽆參建構函式,將建立失敗。

<bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"></bean>

⽅式⼆:使⽤靜態⽅法建立

在實際開發中,我們使⽤的物件有些時候並不是直接通過建構函式就可以建立出來的,它可能在建立的過程中會做很多額外的操作。此時會提供⼀個建立物件的⽅法,恰好這個⽅法是static修飾的⽅法,即是此種情況。

例如,我們在做Jdbc操作時,會⽤到java.sql.Connection接⼝的實現類,如果是mysql資料庫,那麼⽤的就是JDBC4Connection,但是我們不會去寫 JDBC4Connection connection = newJDBC4Connection() ,因為我們要註冊驅動,還要提供URL和憑證資訊,⽤ DriverManager.getConnection ⽅法來獲取連線。

那麼在實際開發中,尤其早期的項⽬沒有使⽤Spring框架來管理物件的建立,但是在設計時使⽤了⼯⼚模式解耦,那麼當接⼊spring之後,⼯⼚類建立物件就具有和上述例⼦相同特徵,即可採⽤此種⽅式配置。

<!--使⽤靜態⽅法建立物件的配置⽅式-->
<bean id="userService" class="com.lagou.factory.BeanFactory"
factory-method="getTransferService"></bean>

⽅式三:使⽤例項化⽅法建立

此種⽅式和上⾯靜態⽅法建立其實類似,區別是⽤於獲取物件的⽅法不再是static修飾的了,⽽是類中的⼀ 個普通⽅法。此種⽅式⽐靜態⽅法建立的使⽤⼏率要⾼⼀些。

在早期開發的項⽬中,⼯⼚類中的⽅法有可能是靜態的,也有可能是⾮靜態⽅法,當是⾮靜態⽅法時,即可採⽤下⾯的配置⽅式:

<!--使⽤例項⽅法建立物件的配置⽅式-->
<bean id="beanFactory"
class="com.lagou.factory.instancemethod.BeanFactory"></bean> <bean id="transferService" factory-bean="beanFactory" factory-method="getTransferService"></bean>

3.Bean的作用範圍和生命週期

3.1作用範圍——scope

image-20201122175934570

經常使用的有singletonprototype

    <!-- scope: 定義bean的作用範圍
                singleton(預設)單例:IOC容器只有一個該類物件
                prototype 原型:每次使用該類的物件(getBean),都返回一個新的物件
    -->
   <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" >
       <property name="ConnectionUtils" ref="connectionUtils"></property>
   </bean>

測試:

   <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" >
       <property name="ConnectionUtils" ref="connectionUtils"></property>
   </bean>
       //===
	@org.junit.Test
    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountDao accountDao = (AccountDao) applicationContext.getBean("accountDao");
        System.out.println("accountDao:"+accountDao);
        AccountDao accountDao1 = (AccountDao) applicationContext.getBean("accountDao");
        System.out.println("accountDao1:"+accountDao1);
    }
//得出結果為:
accountDao:com.lagou.edu.dao.impl.JdbcAccountDaoImpl@58cbafc2
accountDao1:com.lagou.edu.dao.impl.JdbcAccountDaoImpl@58cbafc2   
//同一個物件
   <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" scope="prototype">
       <property name="ConnectionUtils" ref="connectionUtils"></property>
   </bean>
       //===
	@org.junit.Test
    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountDao accountDao = (AccountDao) applicationContext.getBean("accountDao");
        System.out.println("accountDao:"+accountDao);
        AccountDao accountDao1 = (AccountDao) applicationContext.getBean("accountDao");
        System.out.println("accountDao1:"+accountDao1);
    }
//得出結果為:
accountDao:com.lagou.edu.dao.impl.JdbcAccountDaoImpl@75d3a5e0
accountDao1:com.lagou.edu.dao.impl.JdbcAccountDaoImpl@74d1dc36  
//不是同一個物件

3.2生命週期

  • singleton(預設)單例:IOC容器只有一個該類物件

    • 物件出生:當容器建立時,物件就被建立了
    • 物件活著:只要容器在,物件就一直活著
    • 物件死亡:當容器銷燬時,物件就被銷燬了

    一句話總結:單例模式的bean物件生命週期與容器相同

  • prototype (多例)原型:每次使用該類的物件(getBean),都返回一個新的物件

    • 物件出生:當使用物件時建立新的物件例項
    • 物件活著:只要物件在使用中,就一直活著
    • 物件死亡:當物件長時間不用時,被java的垃圾回收器回收了

    一句話總結:多例模式的bean物件,spring框架只負責建立,不負責銷燬

init-method屬性:⽤於指定bean物件的初始化⽅法,此⽅法會在bean物件裝配後調⽤。必須是⼀個⽆參⽅法。

destory-method屬性:⽤於指定bean物件的銷燬⽅法,此⽅法會在bean物件銷燬前執⾏它只能為scope是singleton時起作⽤。

image-20201122181648307

4.DI依賴注入的xml配置

4.1 set方法注入

set注入使用property標籤,如果注入的是另外一個bean使用ref屬性,如果注入的是普通值使用value屬性

   <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" scope="prototype" >
       <property name="name" value="zhangsan"></property>
       <property name="age" value="1"></property>
   </bean>

4.2 建構函式注入

  • 根據有參建構函式引數索引

       <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" scope="prototype" >
           <constructor-arg index="0" ref="connectionUtils"></constructor-arg>
           <constructor-arg index="1" value="zhangsan"></constructor-arg>
           <constructor-arg index="2" value="1"></constructor-arg>
       </bean>
    
  • 根據有參建構函式引數名稱:

       <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" scope="prototype" >
           <constructor-arg name="connectionUtils" ref="connectionUtils"></constructor-arg>
           <constructor-arg name="name" value="zhangsan"></constructor-arg>
            <constructor-arg name="age" value="1"></constructor-arg>
       </bean>
    

4.3 複雜型別

   <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" scope="prototype" >
       <property name="ConnectionUtils" ref="connectionUtils"></property>
        <!--注入複雜資料型別 -->
<!--       private String[] myArray;-->
<!--       private Map<String,String> myMap;-->
<!--       private Set<String> myset;-->
<!--       private Properties myProperties;-->
       <property name="myArray">
           <array>
               <!-- 基本資料型別 就用value  引用型別就用 ref-->
               <value>array1</value>
               <value>array2</value>
           </array>
       </property>

       <property name="myMap">
           <map>
               <entry key="key1" value="value1" />
               <entry key="key2" value="value2"/>
           </map>
       </property>

       <property name="mySet">
           <set>
               <value>set1</value>
               <value>set2</value>
           </set>
       </property>

       <property name="myProperties">
           <props>
               <prop key="prop1">value1</prop>
               <prop key="prop2">value2</prop>
           </props>
       </property>
   </bean>

5.DI依賴注入的註解和xml相結合實現方式

哪些bean定義在xml中,哪些bean的定義使用註解?

第三方jar中的bean定義在xml

自己開發的bean使用註解

我是用Druid連線池,那麼它屬於第三方jar,所以我就在配置檔案中配置

    <!-- 第三方jar中的bean定義在jar包中-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="xxxx"></property>
        <property name="username" value="xxxx"></property>
        <property name="password" value="xxxx"></property>
    </bean>

然後在使用它的地方使用註解:
image-20201122230057797

5.1 @Autowired——按照型別注入

按照型別注入:如下帶程式碼,它會在容器中找到一個型別為AccoutDao的物件注入進來

@Service("transferService")//不指定也可以,變成了類名的首字母小寫
public class TransferServiceImpl implements TransferService {
    //@Autowired 按照型別注入
    @Autowired
    private AccountDao accountDao;
    //可以加在屬性和set方法上,如果加在屬性上,set方法就不需要了
//    public void setAccountDao(AccountDao accountDao) {
//        this.accountDao = accountDao;
//    }

如果AccountDao有多個實現類 ,且多個實現類都配置在了IOC容器中,怎麼辦?

5.2 @Qualifier("具體id")——指定具體的id

@Service("transferService")
public class TransferServiceImpl implements TransferService {
    //@Autowired 按照型別注入,如果按照型別無法唯一鎖定物件,可以結合@Qualifier("具體的id")
    @Autowired
    @Qualifier("accountDao")
    private AccountDao accountDao;

5.3 @Resource

@Resource 註解由J2EE 提供,需要導⼊包 javax.annotation.Resource。 (JDK11預設移除,jdk8可以直接使用)

@Resource 預設按照 ByName ⾃動注⼊。

public class TransferService {
 @Resource 
 private AccountDao accountDao;
 @Resource(name="studentDao") 
 private StudentDao studentDao;
 @Resource(type="TeacherDao") 
 private TeacherDao teacherDao;
 @Resource(name="manDao",type="ManDao") 
 private ManDao manDao;
}

如果同時指定了 nametype,則從Spring上下⽂中找到唯⼀匹配的bean進⾏裝配,找不到則丟擲異常。

如果指定了 name,則從上下⽂中查詢名稱(id)匹配的bean進⾏裝配,找不到則丟擲異常。

如果指定了 type,則從上下⽂中找到類似匹配的唯⼀bean進⾏裝配,找不到或是找到多個,都會丟擲異常。

如果既沒有指定name,⼜沒有指定type,則⾃動按照byName⽅式進⾏裝配;

注意:

@Resource 在 Jdk 11中已經移除,如果要使⽤,需要單獨引⼊jar包

<dependency>
     <groupId>javax.annotation</groupId>
     <artifactId>javax.annotation-api</artifactId>
     <version>1.3.2</version>
</dependency>

5.4 註解掃描

5.4.1 引入名稱空間——context
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
">
5.4.2 開啟註解掃描
<!--開啟註解掃描,base-package指定掃描的包路徑-->
<context:component-scan base-package="com.lagou.edu"></context:component-scan>

6.其他

引入外部資原始檔:

<!--引入外部資原始檔-->
<context:property-placeholder location="classpath:jdbc.properties" />

最終的配置檔案只留下了一個第三方jar

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
">
    <!--開啟註解掃描,base-package指定掃描的包路徑-->
    <context:component-scan base-package="com.lagou.edu"></context:component-scan>
    <!--引入外部資原始檔-->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!-- 第三方jar中的bean定義在jar包中-->
    <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>

6.DI依賴注入,純註解模式

不要xml配置檔案,從java配置類啟動

  1. 新建SpringCofig類
  2. 使用註解@Configuration 標識當前類是一個配置類
  3. 使用註解@ComponentScan({"com.lagou.edu"}) 代替<context:component-scan base-package="com.lagou.edu"></context:component-scan>進行註解掃描
  4. 使用註解@PropertySource({"classpath:jdbc.properties"}) 代替<context:property-placeholder location="classpath:jdbc.properties" />引入外部資原始檔
  5. 使用@Bean 將⽅法返回物件加⼊SpringIOC 容器
  6. @Value 對變數賦值,可以直接賦值,也可以使⽤ ${} 讀取資源配置⽂件中的資訊
  7. 還可以使用@Import 引⼊其他配置類
//@Configuration 標識當前類是一個配置類
@Configuration
@ComponentScan({"com.lagou.edu"})
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")
    public DataSource creatDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
}

6.1啟動

6.1.1 JavaSE:
    @org.junit.Test
    public void test(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        AccountDao accountDao = (AccountDao) applicationContext.getBean("accountDao");
        System.out.println(accountDao);
    }
6.1.2 JavaWeb:
  1. 配置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>
    
      <!-- 配置啟動類的全限定類名-->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.lagou.edu.SpringConfig</param-value>
      </context-param>
      
      <!-- 使用監聽器啟動Spring的IOC容器-->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    </web-app>
    
    

相關文章