SpringIOC基礎知識總結

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="//www.springframework.org/schema/beans"
       xmlns:context="//www.springframework.org/schema/context"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
            //www.springframework.org/schema/beans
            //www.springframework.org/schema/beans/spring-beans.xsd
            //www.springframework.org/schema/context
            //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="//www.springframework.org/schema/beans"
       xmlns:context="//www.springframework.org/schema/context"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
            //www.springframework.org/schema/beans
            //www.springframework.org/schema/beans/spring-beans.xsd
            //www.springframework.org/schema/context
            //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"
     "//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>
    
    
Tags: