Spring IOC

Spring IOC

主要內容

Spring 框架

Spring 框架概念

​ Spring 是衆多開源java項目中的一員,基於分層的javaEE應用一站式輕量級開源框架,主要核心是 IOC(控制反轉/依賴注入)與 AOP(面向切面)兩大技術,實現項目在開發過程當中的輕鬆解耦,提升項目的開發效率。html

​ 在項目中引入 Spring 當即能夠帶來下面的好處 下降組件之間的耦合度,實現軟件各層之間的解耦。可使用容器提供的衆多服務,如:事務管理服務、消息服務等等。當咱們使用容器管理事務時,開發人員就再也不須要手工控制事務.也不需處理複雜的事務傳播。 容器提供單例模式支持,開發人員再也不須要本身編寫實現代碼。 容器提供了AOP技術,利用它很容易實現如權限攔截、運行期監控等功能。java

Spring 源碼架構

​ Spring 總共大約有20個模塊,由1300多個不一樣的文件構成。而這些組件被分別整合在覈心容器(Core Container)、Aop(Aspect Oriented Programming)和設備支持(Instrmentation)、數據訪問及集成(Data Access/Integeration)、Web、報文發送(Messaging)、測試6個模塊集合中。web

  1. 核心容器:Spring-beans 和 Spring-core 模塊是 Spring 框架的核心模塊,包含控制反轉(Inversion of Control, IoC)和依賴注入(Dependency Injection, DI),核心容器提供 Spring 框架的基本功能。核心容器的主要組件是 BeanFactory,工廠模式的實現。BeanFactory 使用控制反轉(IOC) 思想將應用程序的配置和依賴性規範與實際的應用程序代碼分開。

    Spring 上下文Spring Context:Spring 上下文是一個配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和調度功能。spring

    Spring-Expression 模塊是統一表達式語言(unified EL)的擴展模塊,能夠查詢、管理運行中的對象,同時也方便的能夠調用對象方法、操做數組、集合等。它的語法相似於傳統EL,但提供了額外的功能,最出色的要數函數調用和簡單字符串的模板函數。編程

  2. Spring-AOP:Spring-aop是Spring的另外一個核心模塊, 在Spring中,他是以JVM的動態代理技術爲基礎,而後設計出了一系列的Aop橫切實現,好比前置通知、返回通知、異常通知等。經過其配置管理特性,Spring AOP 模塊直接將面向切面的編程功能集成到了 Spring 框架中。因此,能夠很容易地使 Spring 框架管理的任何對象支持 AOP。
  3. Spring Data Access(數據訪問):由Spring-jdbc、Spring-tx、Spring-orm、Spring-jms和Spring-oxm 5個模塊組成 Spring-jdbc 模塊是 Spring 提供的JDBC抽象框架的主要實現模塊,用於簡化 Spring JDBC。

    Spring-tx 模塊是SpringJDBC事務控制實現模塊。使用Spring框架,它對事務作了很好的封裝,經過它的Aop配置,能夠靈活的配置在任何一層。數組

    Spring-Orm 模塊是ORM框架支持模塊,主要集成 hibernate, Java Persistence API (JPA) 和 Java Data Objects (JDO) 用於資源管理、數據訪問對象(DAO)的實現和事務策略。緩存

    Spring-Jms 模塊(Java Messaging Service)可以發送和接受信息。安全

    Spring-Oxm 模塊主要提供一個抽象層以支撐OXM(OXM 是 Object-to-XML-Mapping 的縮寫,它是一個O/M-mapper,將java對象映射成 XML 數據,或者將 XML 數據映射成 java 對象),例如:JAXB, Castor, XMLBeans, JiBX 和 XStream 等。websocket

  4. Web 模塊:由Spring-web、Spring-webmvc、Spring-websocket和Spring-webmvc-portlet 4個模塊組成,Web 上下文模塊創建在應用程序上下文模塊之上,爲基於 Web 的應用程序提供了上下文。Web 模塊還簡化了處理多部分請求以及將請求參數綁定到域對象的工做。
  5. 報文發送:即Spring-messaging模塊。

    Spring-messaging是Spring4 新加入的一個模塊,主要職責是爲Spring 框架集成一些基礎的報文傳送應用。session

  6. 單元測試:即Spring-test模塊。Spring-test模塊主要爲測試提供支持

Spring 框架環境搭建

環境要求

​ JDK 版本:

​ JDK 1.7 及以上版本

​ Spring版本:

​ Spring 5.x版本

新建 Maven 項目
  1. 建立 Maven 的普通 Java 項目
  2. 設置項目的座標
  3. 設置項目的 Maven 環境
  4. 設置項目的名稱和存放的工做空間
調整項目環境
  1. 修改 JDK 版本

    <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
  2. 修改單元測試 JUnit 版本

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  3. build標籤中的pluginManagement標籤

    <!--刪除build標籤中的pluginManagement標籤-->
    <build>
    </build>
添加 Spring 框架的依賴座標

Maven倉庫:https://mvnrepository.com/

<!-- 添加Spring框架的核心依賴 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.4.RELEASE</version>
</dependency>
編寫 Bean 對象
package com.xxxx.service;

public class UserService {

    public void test(){
        System.out.println("Hello Spring!");
    }

}
添加Spring 配置文件
  1. 在項目的src下建立文件夾 resources(Alt+insert)
  2. 將 resources 標記爲資源目錄
  3. 在 srcmainresources 目錄下新建 spring.xml 文件,並拷貝官網文檔提供的模板內容到 xml 中。

    配置 bean 到 xml 中,把對應 bean 歸入到 Spring 容器來管理

    spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        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">
    
       <!-- 
            xmlns 即 xml namespace  xml使用的命名空間
            xmlns:xsi 即xml schema instance xml 遵照的具體規範
            xsi:schemaLocation 本文檔xml遵照的規範 官方指定
       -->
       <bean id="userService" class="com.xxxx.service.UserService"></bean>
    
    </beans>
  4. 在 spring.xml 中配置 Bean 對象

    <!--
            id:bean對象的id,惟一標識。通常是Bean對象的名稱的首字母小寫
            class:bean對象的類路徑
    -->
    <bean id="userService" class="com.xxxx.service.UserService"></bean>
加載配置文件,獲取實例化對象
package com.xxxx;

import com.xxxx.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        // 獲取Spring上下文環境 (加載配置文件)
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
        // 經過getBean方法獲得Spring容器中實例化好的Bean對象 (實例化Bean對象)
        // userService表明的是配置文件中bean標籤的id屬性值
        UserService userService = (UserService) ac.getBean("userService");
        // 調用方法 (使用實例化對象)
        userService.test();
    }
}

Spring IOC 容器 Bean 對象實例化模擬

思路:

  1. 定義Bean 工廠接口,提供獲取bean方法
  2. 定義Bean工廠接口實現類,解析配置文件,實例化Bean對象
  3. 實現獲取Bean方法

定義 Bean 屬性對象

package com.xxxx.spring;

/**
 * bean對象
 *      用來接收配置文件中bean標籤的id與class屬性值
 */
public class MyBean {

    private String id; // bean對象的id屬性值
    private String clazz; // bean對象的類路徑

    public MyBean() {
    }

    public MyBean(String id, String clazz) {
        this.id = id;
        this.clazz = clazz;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClazz() {
        return clazz;
    }

    public void setClazz(String clazz) {
        this.clazz = clazz;
    }
}

添加 dom4j 座標依賴

<!-- dom4j -->
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>
<!-- XPath -->
<dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.1.6</version>
</dependency>

準備自定義配置文件

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="userService" class="com.xxxx.service.UserService"></bean>
    <bean id="accountService" class="com.xxxx.service.AccountService"></bean>
</beans>

定義 Bean 工廠接口

package com.xxxx.spring;

/**
 * Bean 工廠接口定義
 */
public interface MyFactory {
    // 經過id值獲取對象
    public Object getBean(String id);
}

定義 Bean 接口的實現類

package com.xxxx.spring;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 模擬Spring的實現
 *  一、經過構造器獲得相關配置文件
 *  二、經過dom4j解析xml文件,獲得List   存放id和class
 *  三、經過反射實例化獲得對象   Class.forName(類的全路徑).newInstance(); 經過Map<id,Class>存儲
 *  四、獲得指定的實例化對象
 */
public class MyClassPathXmlApplicationContext implements BeanFactory {

    private Map beans = new HashMap(); // 實例化後的對象放入map
    private List<MyBean> myBeans; // 存放已讀取bean 配置信息

    /* 一、經過構造器獲得相關配置文件 */
    public MyClassPathXmlApplicationContext(String fileName) {

        /* 二、經過dom4j解析xml文件,獲得List (存放id和class) */
        this.parseXml(fileName);

        /* 三、經過反射實例化獲得對象Class.forName(類路徑).newInstance();  經過Map存儲 */
        this.instanceBean();

    }

    /**
     * 經過dom4j解析xml文件,獲得List   存放id和class
     *  一、獲取解析器
     *  二、獲得配置文件的URL
     *  三、經過解析器解析xml文件(spring.xml)
     *  四、經過xpath語法,獲取beans標籤下的全部bean標籤
     *  五、經過指定語法解析文檔對象,返回集合
     *  六、判斷集合是否爲空,遍歷集合
     *  七、獲取標籤元素中的屬性
     *  八、獲得Bean對象,將Bean對象設置到集合中
     * @param fileName
     */
    private void parseXml(String fileName) {
        // 一、獲取解析器
        SAXReader reader = new SAXReader();
        // 二、獲得配置文件的URL
        URL url = this.getClass().getClassLoader().getResource(fileName);
        try {
            // 三、經過解析器解析xml文件(spring.xml)
            Document document = reader.read(url);
            // 四、經過xpath語法,獲取beans標籤下的全部bean標籤
            XPath xPath = document.createXPath("beans/bean");
            // 經過指定語法解析文檔對象,返回集合
            List<Element> list = xPath.selectNodes(document);
            // 判斷集合是否爲空,遍歷集合
            if (list != null && list.size() > 0) {
                myBeans = new ArrayList<>();
                for(Element el : list) {
                    // 獲取標籤元素中的屬性
                    String id = el.attributeValue("id"); // id 屬性值
                    String clazz = el.attributeValue("class"); // class 屬性值
                    System.out.println(el.attributeValue("id"));
                    System.out.println(el.attributeValue("class"));
                    // 獲得Bean對象
                    MyBean bean = new MyBean(id, clazz);
                    // 將Bean對象設置到集合中
                    myBeans.add(bean);
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 經過反射實例化獲得對象  
     *     Class.forName(類的全路徑).newInstance();  
     *    經過Map<id,Class>存儲
     */
    private void instanceBean() {
        // 判斷bean集合是否爲空,不爲空遍歷獲得對應Bean對象
        if (myBeans != null && myBeans.size() > 0) {
            for (MyBean bean : myBeans){                                      
                try {
                    // 經過類的全路徑實例化對象
                    Object object = Class.forName(bean.getClazz()).newInstance();
                    // 將id與實例化對象設置到map對象中
                    beans.put(bean.getId(), object);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 經過key獲取map中的指定value
     * @param id
     * @return
     */
    @Override
    public Object getBean(String id) {
        Object object = beans.get(id);
        return object;
    }
}

測試自定義 IOC 容器

  1. 建立與配置文件中對應的Bean對象

    UserService.java

    package com.xxxx.service;
     
    public class UserService {
     
        public void test(){
            System.out.println("UserService Test...");
        }
    }

    AccountService.java

    package com.xxxx.service;
    
    public class AccountService {
    
        public void test(){
            System.out.println("AccountService Test...");
        }
    }
  2. 測試是否能夠獲取實例化的Bean對象

    package com.xxxx;
    
    import com.xxxx.spring.MyFactory;
    import com.xxxx.spring.MyClassPathXmlApplicationContext;
    import com.xxxx.service.AccountService;
    import com.xxxx.service.UserService;
    
    public class App {
        
        public static void main(String[] args) {
            MyFactory factory = new MyClassPathXmlApplicationContext("spring.xml");
            // 獲得實例化對象
            UserService userService = (UserService) factory.getBean("userService");
            userService.test();
    
            UserService userService2 = (UserService) factory.getBean("userService");
            System.out.println(userService+"=====" + userService2);
    
            AccountService accountService = 
            (AccountService)factory.getBean("accountService");
            accountService.test();
    
        }
    }

    ​ Spring 容器在啓動的時候 讀取xml配置信息,並對配置的 bean 進行實例化(這裏模擬的比較簡單,僅用於幫助你們理解),同時經過上下文對象提供的 getBean() 方法拿到咱們配置的 bean 對象,從而實現外部容器自動化維護並建立 bean 的效果。

Spring IOC 配置文件加載

Spring 配置文件加載

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    
    <bean id="userService" class="com.xxxx.service.UserService"></bean>
</beans>
根據相對路徑加載資源
ApplicationContext ac  = new ClassPathXmlApplicationContext("spring.xml");
根據絕對路徑加載資源(瞭解)
ApplicationContext ac = new FileSystemXmlApplicationContext("C:/IdeaWorkspace/spring01/src/main/resources/spring.xml");

Spring 多配置文件加載

​ Spring 框架啓動時能夠加載多個配置文件到環境中。對於比較複雜的項目,可能對應的配置文件有多個,項目在啓動部署時會將多個配置文件同時加載進來。

service.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    
    <bean id="userService" class="com.xxxx.service.UserService"></bean>
</beans>

dao.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    
    <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
</beans>
可變參數,傳入多個文件名
// 同時加載多個資源文件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml","dao.xml");
經過總的配置文件import其餘配置文件

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    
    <!--導入須要包含的資源文件-->
    <import resource="service.xml"/>
    <import resource="dao.xml"/>
</beans>

加載時只需加載總的配置文件便可

// 加載總的資源文件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");

Spring IOC 容器 Bean 對象實例化

構造器實例化

注:經過默認構造器建立 空構造方法必須存在 不然建立失敗

  1. 設置配置文件 spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
    
        <bean id="userService" class="com.xxxx.service.UserService"></bean>
    
    </beans>
  2. 獲取實例化對象

    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = (UserService) ac.getBean("userService");  
    userService.test();

靜態工廠實例化(瞭解)

注:

  • 要有該工廠類及工廠方法
  • 工廠方法爲靜態的
  1. 定義靜態工廠類

    package com.xxxx.factory;
    
    import com.xxxx.service.UserService;
    
    /**
     * 定義靜態工廠類
     */
    public class StaticFactory {
        /**
         * 定義對應的靜態方法,返回實例化對象
         * @return
         */
        public static UserService createUserService() {
            return new UserService();
        }
    }
  2. 設置配置文件 spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
    
        <!--靜態工廠-->
        <bean id="userService" class="com.xxxx.factory.StaticFactory" factory-method="createUserService"></bean>
    
    </beans>
  3. 獲取實例化對象

    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = (UserService) ac.getBean("userService");  
    userService.test();

    ​ 當咱們指定Spring使用靜態工廠方法來建立Bean實例時,Spring將先解析配置文件,並根據配置文件指定的信息,經過反射調用靜態工廠類的靜態工廠方法,並將該靜態工廠方法的返回值做爲Bean實例,在這個過程當中,Spring再也不負責建立Bean實例,Bean實例是由用戶提供的靜態工廠方法提供的。

實例化工廠實例化(瞭解)

注:

  • 工廠方法爲非靜態方法
  • 須要配置工廠bean,並在業務bean中配置factory-bean,factory-method屬性
  1. 定義工廠類

    package com.xxxx.factory;
    
    import com.xxxx.service.UserService;
    
    /**
     * 定義工廠類
     */
    public class InstanceFactory {
        /**
         * 定義方法,返回實例化對象
         * @return
         */
        public UserService createUserService() {
            return new UserService();
        }
    }
  2. 設置配置文件 spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
    
        <!--
            實例化工廠
                1.定義實例化工廠bean
                2.引用工廠bean 指定工廠建立方法(方法爲非靜態)
        -->
        <bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"></bean>
        <bean id="userService" factory-bean="instanceFactory" factory-method="createUserService"></bean>
    
    </beans>
  3. 獲取實例化對象

    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = (UserService) ac.getBean("userService");  
    userService.test();

Spring三種實例化Bean的方式比較

  • 方式一:經過bean的缺省構造函數建立,當各個bean的業務邏輯相互比較獨立的時候或者和外界關聯較少的時候可使用。
  • 方式二:利用靜態factory方法建立,能夠統一管理各個bean的建立,如各個bean在建立以前須要相同的初始化處理,則可用這個factory方法險進行統一的處理等等。
  • 方式三:利用實例化factory方法建立,即將factory方法也做爲了業務bean來控制,1可用於集成其餘框架的bean建立管理方法,2可以使bean和factory的角色互換。

    開發中項目通常使用一種方式實例化bean,項目開發基本採用第一種方式,交給Spring託管,使用時直接拿來使用便可。另外兩種瞭解

Spring IOC 注入

手動實例化與外部引入

圖一:

圖二:

​ 對比發現:圖二中對於 UserDao 對象的建立並無像圖一那樣主動的去實例化,而是經過帶參方法形式將UserDao 傳入過來,從而實現 UserService 對UserDao類 的依賴。

而實際建立對象的幕後對象便是交給了外部來建立。

Spring IOC 手動裝配(注入)

​ Spring 支持的注入方式共有四種:set 注入、構造器注入、靜態工廠注入、實例化工廠注入。

set方法注入

注:

  • 屬性字段須要提供set方法
  • 四種方式,推薦使用set方法注入
業務對象 JavaBean
  1. 屬性字段提供set方法

    public class UserService {
    
        // 業務對象UserDao set注入(提供set方法)
        private UserDao userDao;
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    }
  2. 配置文件的bean標籤設置property標籤

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
        
       <!--
            IOC經過property標籤手動裝配(注入):
                Set方法注入
                    name:bean對象中屬性字段的名稱
                    ref:指定bean標籤的id屬性值
        --> 
        <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
        <bean id="userService" class="com.xxxx.service.UserService">
            <!--業務對象 注入-->
            <property name="userDao" ref="userDao"/>
        </bean>
    </beans>
經常使用對象和基本類型
  1. 屬性字段提供set方法

    public class UserService {
    
        // 經常使用對象String  set注入(提供set方法)
        private String host;
        public void setHost(String host) {
            this.host = host;
        }
    
        // 基本類型Integer   set注入(提供set方法)
        private Integer port;
        public void setPort(Integer port) {
            this.port = port;
        }
    }
  2. 配置文件的bean標籤設置property標籤

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
        
       <!--
            IOC經過property標籤手動裝配(注入):
                Set方法注入
                    name:bean對象中屬性字段的名稱
                    value:具體的值(基本類型 經常使用對象|日期  集合)
        --> 
        <bean id="userService" class="com.xxxx.service.UserService">
            <!--經常使用對象String 注入-->
            <property name="host" value="127.0.0.1"/>
            <!--基本類型注入-->
            <property name="port" value="8080"/>
        </bean>
    
    </beans>
集合類型和屬性對象
  1. 屬性字段提供set方法

    public class UserService {
    
        // List集合  set注入(提供set方法)
        public List<String> list;
        public void setList(List<String> list) {
            this.list = list;
        }
       
    
        // Set集合  set注入(提供set方法)
        private Set<String> set;
        public void setSet(Set<String> set) {
            this.set = set;
        }
    
        // Map set注入(提供set方法)
        private Map<String,Object> map;
        public void setMap(Map<String, Object> map) {
            this.map = map;
        }
        
    
        // Properties set注入(提供set方法)
        private Properties properties;
        public void setProperties(Properties properties) {
            this.properties = properties;
        }
       
    }
  2. 配置文件的bean標籤設置property標籤

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
        
       <!--
            IOC經過property標籤手動裝配(注入):
                Set方法注入
                    name:bean對象中屬性字段的名稱
                    value:具體的值(基本類型 經常使用對象|日期  集合)
        --> 
        <!--List集合 注入-->
        <property name="list">
            <list>
                <value>上海</value>
                <value>北京</value>
                <value>杭州</value>
            </list>
        </property>
    
        <!--Set集合注入-->
        <property name="set">
            <set>
                <value>上海SH</value>
                <value>北京BJ</value>
                <value>杭州HZ</value>
            </set>
        </property>
    
        <!--Map注入-->
        <property name="map">
            <map>
                <entry>
                    <key><value>周杰倫</value></key>
                    <value>我是如此相信</value>
                </entry>
                <entry>
                    <key><value>林俊杰</value></key>
                    <value>惋惜沒若是</value>
                </entry>
                <entry>
                    <key><value>陳奕迅</value></key>
                    <value>十年</value>
                </entry>
            </map>
        </property>
    
        <!--Properties注入-->
        <property name="properties">
            <props>
                <prop key="上海">東方明珠</prop>
                <prop key="北京">天安門</prop>
                <prop key="杭州">西湖</prop>
            </props>
        </property>
    
    </beans>
測試代碼

UserService.java

public class UserService {

    // 業務對象UserDao set注入(提供set方法)
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    // 經常使用對象String  set注入(提供set方法)
    private String host;
    public void setHost(String host) {
        this.host = host;
    }

    // 基本類型Integer   set注入(提供set方法)
    private Integer port;
    public void setPort(Integer port) {
        this.port = port;
    }

    // List集合  set注入(提供set方法)
    public List<String> list;
    public void setList(List<String> list) {
        this.list = list;
    }
    // List集合輸出
    public void printList() {
        list.forEach(s -> System.out.println(s));
    }

    // Set集合  set注入(提供set方法)
    private Set<String> set;
    public void setSet(Set<String> set) {
        this.set = set;
    }
    // Set集合輸出
    public void printSet() {
        set.forEach(s -> System.out.println(s));
    }

    // Map set注入(提供set方法)
    private Map<String,Object> map;
    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
    // Map輸出
    public void printMap() {
        map.forEach((k,v) -> System.out.println(k + "," + v));
    }

    // Properties set注入(提供set方法)
    private Properties properties;
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    // Properties輸出
    public  void printProperties(){
        properties.forEach((k,v) -> System.out.println(k + ","+ v ));
    }

    public  void  test(){
        System.out.println("UserService Test...");

        userDao.test();

        studentDao.test();

        System.out.println("Host:" + host  + ",port:" + port);

        // List集合
        printList();

        // Set集合
        printSet();

        // Map
        printMap();

        // Properties
        printProperties();

    }
}

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    
    <!--
        IOC經過property標籤手動裝配(注入):
            Set方法注入
                name:bean對象中屬性字段的名稱
                ref:指定bean標籤的id屬性值
                value:具體的值(基本類型 經常使用對象|日期  集合)

    -->
    <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
    <bean id="userService" class="com.xxxx.service.UserService">
        <!--業務對象 注入-->
        <property name="userDao" ref="userDao"/>
        <property name="studentDao" ref="studentDao"/>

        <!--經常使用對象String 注入-->
        <property name="host" value="192.168.1.109"/>
        <!--基本類型注入-->
        <property name="port" value="8080"/>

        <!--List集合 注入-->
        <property name="list">
            <list>
                <value>上海</value>
                <value>北京</value>
                <value>杭州</value>
            </list>
        </property>

        <!--Set集合注入-->
        <property name="set">
            <set>
                <value>上海SH</value>
                <value>北京BJ</value>
                <value>杭州HZ</value>
            </set>
        </property>

        <!--Map注入-->
        <property name="map">
            <map>
                <entry>
                    <key><value>周杰倫</value></key>
                    <value>我是如此相信</value>
                </entry>
                <entry>
                    <key><value>林俊杰</value></key>
                    <value>惋惜沒若是</value>
                </entry>
                <entry>
                    <key><value>陳奕迅</value></key>
                    <value>十年</value>
                </entry>
            </map>
        </property>

        <!--Properties注入-->
        <property name="properties">
            <props>
                <prop key="上海">東方明珠</prop>
                <prop key="北京">天安門</prop>
                <prop key="杭州">西湖</prop>
            </props>
        </property>

    </bean>
    
</beans>
構造器注入

注:

  • 提供帶參構造器
單個Bean對象做爲參數

Java 代碼

public class UserService {

    private UserDao userDao; // JavaBean 對象
    
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public  void  test(){
        System.out.println("UserService Test...");

        userDao.test();
    }

}

XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    <!--
        IOC經過構造器注入:
            經過constructor-arg標籤進行注入
                name:屬性名稱
                ref:指定bean標籤的id屬性值
    -->
    <bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
    
    <bean id="userService" class="com.xxxx.service.UserService">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg> 
    </bean>

</beans>
多個Bean對象做爲參數

Java 代碼

public class UserService {

    private UserDao userDao;  // JavaBean 對象
    private AccountDao accountDao  // JavaBean 對象
        
    public UserService(UserDao userDao, AccountDao accountDao) {
        this.userDao = userDao;
        this.accountDao = accountDao;
    }

    public  void  test(){
        System.out.println("UserService Test...");

        userDao.test();
        accountDao.test();
    }

}

XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
     <!--
        IOC經過構造器注入:
            經過constructor-arg標籤進行注入
                name:屬性名稱
                ref:指定bean標籤的id屬性值
    -->
    <bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
    <bean id="accountDao" class="com.xxxx.dao.AccountDao" ></bean>
    
    <bean id="userService" class="com.xxxx.service.UserService">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg> 
        <constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
    </bean>

</beans>
Bean對象和經常使用對象做爲參數

Java 代碼

public class UserService {

    private UserDao userDao;  // JavaBean 對象
    private AccountDao accountDao;  // JavaBean 對象
    private String uname;  // 字符串類型
        
    public UserService(UserDao userDao, AccountDao accountDao, String uname) {
        this.userDao = userDao;
        this.accountDao = accountDao;
        this.uname = uname;
    }

    public  void  test(){
        System.out.println("UserService Test...");

        userDao.test();
        accountDao.test();
        System.out.println("uname:" + uname);
    }

}

XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    <!--
        IOC經過構造器注入:
            經過constructor-arg標籤進行注入
                name:屬性名稱
                ref:指定bean標籤的id屬性值
                 value:基本類型 經常使用對象的值
                index:構造器中參數的下標,從0開始
    -->
    <bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
    <bean id="accountDao" class="com.xxxx.dao.AccountDao" ></bean>
    <bean id="userService" class="com.xxxx.service.UserService">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg> 
        <constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
        <constructor-arg name="uname" value="admin"></constructor-arg>
    </bean>

</beans>
循環依賴問題

循環問題產生的緣由:

​ Bean經過構造器注入,之間彼此相互依賴對方致使bean沒法實例化。

問題展現:

  1. Java 代碼

    public class AccountService {
    
        private RoleService roleService;
    
       public AccountService(RoleService roleService) {
            this.roleService = roleService;
        }
    
        public void  test() {
            System.out.println("AccountService Test...");
        }
    }
    
    public class RoleService {
    
        private AccountService accountService;
    
       public RoleService(AccountService accountService) {
            this.accountService = accountService;
        }
    
        public void  test() {
            System.out.println("RoleService Test...");
        }
    }
  2. XML配置

    <!--
         若是多個bean對象中互相注入,則會出現循環依賴的問題
         能夠經過set方法注入解決
    -->
    <bean id="accountService" class="com.xxxx.service.AccountService">
        <constructor-arg name="roleService" ref="roleService"/>
    </bean>
    
    <bean id="roleService" class="com.xxxx.service.RoleService">
        <constructor-arg name="accountService" ref="accountService"/>
    </bean>

如何解決:將構造器注入改成set方法注入

  1. Java代碼

    public class AccountService {
    
        private RoleService roleService;
    
       /* public AccountService(RoleService roleService) {
            this.roleService = roleService;
        }*/
    
        public void setRoleService(RoleService roleService) {
            this.roleService = roleService;
        }
    
        public void  test() {
            System.out.println("AccountService Test...");
        }
    }
    
    public class RoleService {
    
        private AccountService accountService;
    
       /* public RoleService(AccountService accountService) {
            this.accountService = accountService;
        }*/
    
        public void setAccountService(AccountService accountService) {
            this.accountService = accountService;
        }
    
        public void  test() {
            System.out.println("RoleService Test...");
        }
    }
  2. XML配置

    <!--
        <bean id="accountService" class="com.xxxx.service.AccountService">
        <constructor-arg name="roleService" ref="roleService"/>
        </bean>
    
        <bean id="roleService" class="com.xxxx.service.RoleService">
            <constructor-arg name="accountService" ref="accountService"/>
        </bean>
    -->
    <!--修改成set方法注入-->
    <bean id="accountService" class="com.xxxx.service.AccountService">
        <property name="roleService" ref="roleService"/>
    </bean>
    
    <bean id="roleService" class="com.xxxx.service.RoleService">
        <property name="accountService" ref="accountService"/>
    </bean>
靜態工廠注入
  1. 定義靜態工廠類

    public class StaticFactory {
    
        // 定義靜態方法
        public static TypeDao createTypeDao() {
            return new TypeDao();
        }
    }
  2. Java代碼

    public class TypeService {
    
        private TypeDao typeDao;
        
        public void setTypeDao(TypeDao typeDao) {
            this.typeDao = typeDao;
        }
    
        public void  test() {
            System.out.println("TypeService Test...");
        }
    }
  3. XML配置

    在配置文件中設置bean標籤,指定工廠對象並設置對應的方法

    <bean id="typeService" class="com.xxxx.service.TypeService">
        <property name="typeDao" ref="typeDao"/>
    </bean>
    <!--
        靜態工廠注入:
            靜態工廠注入也是藉助set方法注入,只是被注入的bean對象的實例化是經過靜態工廠實例化的
    -->
    <bean id="typeDao" class="com.xxxx.factory.StaticFactory" factory-method="createTypeDao"></bean>
實例化工廠注入
  1. 定義工廠類

    public class InstanceFactory {
         public TypeDao createTypeDao() {
            return new TypeDao();
        }
    }
  2. Java代碼

    public class TypeService {
    
        private TypeDao typeDao;
        
        public void setTypeDao(TypeDao typeDao) {
            this.typeDao = typeDao;
        }
    
        public void  test() {
            System.out.println("TypeService Test...");
        }
    }
  3. XML配置

    聲明工廠bean標籤,聲明bean對象,指明工廠對象和工廠方法

    <bean id="typeService" class="com.xxxx.service.TypeService">
        <property name="typeDao" ref="typeDao"/>
    </bean>
    <!--
        實例化工廠注入:
            實例化工廠注入也是藉助set方法注入,只是被注入的bean對象的實例化是經過實例化工廠實例化的
    -->
    <bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"></bean>
    <bean id="typeDao" factory-bean="instanceFactory" factory-method="createTypeDao"></bean>

    重點掌握set注入和構造器注入,工廠方式瞭解便可。實際開發中基本使用set方式注入bean。

注入方式的選擇

開發項目中set方式注入首選

​ 使用構造注入能夠在構建對象的同時一併完成依賴關係的創建,對象一創建則全部的一切也就準備好了,但若是要創建的對象關係不少,使用構造器注入會在構建函數上留下一長串的參數,且不易記憶,這時使用Set注入會是個不錯的選擇。
  使用Set注入能夠有明確的名稱,能夠了解注入的對象會是什麼,像setXXX()這樣的名稱會比記憶Constructor上某個參數的位置表明某個對象更好。

p名稱空間的使用

​ spring2.5之後,爲了簡化setter方法屬性注入,引用p名稱空間的概念,能夠將 子元素,簡化爲元素屬性配置。

  1. 屬性字段提供 set 方法

    public class UserService {
    
        // 業務對象UserDao set注入(提供set方法)
        private UserDao userDao;
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        
        // 經常使用對象String  set注入(提供set方法)
        private String host;
        public void setHost(String host) {
            this.host = host;
        }
    }
  2. 在配置文件 spring.xml 引入 p 名稱空間

    xmlns:p="http://www.springframework.org/schema/p"
    <?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:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
        <!--
            p:屬性名:="xxx"        引入常量值
            p:屬性名-ref:="xxx"    引入其餘Bean對象的id屬性值
        -->
        <bean id="userService" class="com.xxxx.service.UserService" 
            p:userDao-ref="userDao" 
            p:host="127.0.0.1" />
    
    </beans>

Spring IOC 自動裝配(注入)

註解方式注入 Bean

​ 對於 bean 的注入,除了使用 xml 配置之外,可使用註解配置。註解的配置,能夠簡化配置文件,提升開發的速度,使程序看上去更簡潔。對於註解的解釋,Spring對於註解有專門的解釋器,對定義的註解進行解析,實現對應bean對象的注入。經過反射技術實現

準備環境
  1. 修改配置文件

    <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
           http://www.springframework.org/schema/context/spring-context.xsd">
  2. 開啓自動化注入

    <!--開啓自動化裝配(注入)-->
    <context:annotation-config/>
    
    <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
    <bean id="userService" class="com.xxxx.service.UserService"></bean>
  3. 給注入的bean對象添加註解
@Resource註解

@Resource註解實現自動注入(反射)

  • 默認根據屬性字段名稱查找對應的bean對象 (屬性字段的名稱與bean標籤的id屬性值相等)
  • 若是屬性字段名稱未找到,則會經過類型(Class類型)查找
  • 屬性能夠提供set方法,也能夠不提供set方法
  • 註解能夠聲明在屬性級別 或 set方法級別
  • 能夠設置name屬性,name屬性值必須與bean標籤的id屬性值一致;若是設置了name屬性值,就只會按照name屬性值查找bean對象
  • 當注入接口時,若是接口只有一個實現則正常實例化;若是接口存在多個實現,則須要使用name屬性指定須要被實例化的bean對象

代碼示例

  1. 默認根據屬性字段名稱查找對應的bean對象 (屬性字段的名稱與bean標籤的id屬性值相等)

    /**
     * @Resource註解實現自動注入(反射)
     *  默認根據屬性字段名稱查找對應的bean對象 (屬性字段的名稱與bean標籤的id屬性值相等)
     */
    public class UserService {
    
        @Resource
        private UserDao userDao; // 屬性字段的名稱與bean標籤的id屬性值相等
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void test() {
            // 調用UserDao的方法
            userDao.test();
        }
    }
  2. 若是屬性字段名稱未找到,則會經過類型(Class類型)查找

    /**
     * @Resource註解實現自動注入(反射)
     *   若是屬性字段名稱未找到,則會經過類型(Class類型)查找
     */
    public class UserService {
    
        @Resource
        private UserDao ud; // 當在配置文件中屬性字段名(ud)未找到,則會查找對應的class(UserDao類型)
    
        public void setUd(UserDao ud) {
            this.ud = ud;
        }
    
        public void test() {
            // 調用UserDao的方法
            ud.test();
        }
    }
  3. 屬性能夠提供set方法,也能夠不提供set方法

    /**
     * @Resource註解實現自動注入(反射)
     *   屬性能夠提供set方法,也能夠不提供set方法
     */
    public class UserService {
    
        @Resource
        private UserDao userDao; // 不提供set方法
    
        public void test() {
            // 調用UserDao的方法
            userDao.test();
        }
    }
  4. 註解能夠聲明在屬性級別 或 set方法級別

    /**
     * @Resource註解實現自動注入(反射)
     *   註解能夠聲明在屬性級別 或 set方法級別
     */
    public class UserService {
    
        private UserDao userDao;
    
        @Resource // 註解也可設置在set方法上
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void test() {
            // 調用UserDao的方法
            userDao.test();
        }
    }
  5. 能夠設置name屬性,name屬性值必須與bean標籤的id屬性值一致;若是設置了name屬性值,就只會按照name屬性值查找bean對象

    /**
     * @Resource註解實現自動注入(反射)
     *   能夠設置name屬性,name屬性值必須與bean的id屬性值一致;
     *   若是設置了name屬性值,就只會按照name屬性值查找bean對象
     */
    public class UserService {
    
        @Resource(name = "userDao") // name屬性值與配置文件中bean標籤的id屬性值一致
        private UserDao ud;
    
        public void test() {
            // 調用UserDao的方法
            ud.test();
        }
    }
  6. 當注入接口時,若是接口只有一個實現則正常實例化;若是接口存在多個實現,則須要使用name屬性指定須要被實例化的bean對象

    定義接口類 IUserDao.java

    package com.xxxx.dao;
    
    /**
     * 定義接口類
     */
    public interface IUserDao {
        public void test();
    }

    定義接口實現類 UserDao01.java

    package com.xxxx.dao;
    
    /**
     * 接口實現類
     */
    public class UserDao01 implements IUserDao {
    
        @Override
        public void test(){
            System.out.println("UserDao01...");
        }
    }

    定義接口實現類 UserDao02.java

    package com.xxxx.dao;
    
    /**
     * 接口實現類
     */
    public class UserDao02 implements IUserDao {
    
        @Override
        public void test(){
            System.out.println("UserDao02...");
        }
    }

    XML配置文件

    <!--開啓自動化裝配(注入)-->
    <context:annotation-config/>
    
    <bean id="userService" class="com.xxxx.service.UserService"></bean>
    
    <bean id="userDao01" class="com.xxxx.dao.UserDao01"></bean>
    <bean id="userDao02" class="com.xxxx.dao.UserDao01"></bean>

    使用註解 UserService.java

    /**
     * @Resource註解實現自動注入(反射)
     *   當注入接口時,若是接口只有一個實現則正常實例化;若是接口存在多個實現,則須要使用name屬性指定須要被實例化的bean對象
     */
    public class UserService {
    
        @Resource(name = "userDao01") // name屬性值與其中一個實現類的bean標籤的id屬性值一致
        private IUserDao iUserDao; // 注入接口(接口存在多個實現)
    
        public void test() {
            iUserDao.test();
        }
    }
@Autowired註解

@Autowired註解實現自動化注入:

  • 默認經過類型(Class類型)查找bean對象 與屬性字段的名稱無關
  • 屬性能夠提供set方法,也能夠不提供set方法
  • 註解能夠聲明在屬性級別 或 set方法級別
  • 能夠添加@Qualifier結合使用,經過value屬性值查找bean對象(value屬性值必需要設置,且值要與bean標籤的id屬性值對應)
  1. 默認經過類型(Class類型)查找bean對象 與屬性字段的名稱無關

    /**
     * @Autowired註解實現自動化注入
     *  默認經過類型(Class類型)查找bean對象   與屬性字段的名稱無關
     */
    public class UserService {
    
        @Autowired
        private UserDao userDao; // 默認經過類型(Class類型)查找bean對象  與屬性字段的名稱無關
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void test() {
            // 調用UserDao的方法
            userDao.test();
        }
    }
  2. 屬性能夠提供set方法,也能夠不提供set方法

    /**
     * @Autowired註解實現自動化注入
     *  屬性能夠提供set方法,也能夠不提供set方法
     */
    public class UserService {
    
        @Autowired
        private UserDao userDao; // 不提供set方法
    
        public void test() {
            // 調用UserDao的方法
            userDao.test();
        }
    }
  3. 註解能夠聲明在屬性級別 或 set方法級別

    /**
     * @Autowired註解實現自動化注入
     *  註解能夠聲明在屬性級別 或 set方法級別
     */
    public class UserService {
    
        private UserDao userDao; 
    
        @Autowired// 註解能夠聲明在set方法級別
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void test() {
            // 調用UserDao的方法
            userDao.test();
        }
    }
  4. 能夠添加@Qualifier結合使用,經過value屬性值查找bean對象(value屬性值必需要設置,且值要與bean標籤的id屬性值對應)

    /**
     * @Autowired註解實現自動化注入
     *  能夠添加@Qualifier結合使用,經過value屬性值查找bean對象
             value屬性值必需要設置,且值要與bean標籤的id屬性值對應
     */
    public class UserService {
    
        @Autowired
        @Qualifier(value="userDao") // value屬性值必需要設置,且值要與bean標籤的id屬性值對應
        private UserDao userDao;
    
        public void test() {
            userDao.test();
        }
    }

    推薦使用@Resource 註解是屬於J2EE的,減小了與Spring的耦合。

Spring IOC 掃描器

​ 實際的開發中,bean的數量很是多,採用手動配置bean的方式已沒法知足生產須要,Spring這時候一樣提供了掃描的方式,對掃描到的bean對象統一進行管理,簡化開發配置,提升開發效率。

Spring IOC 掃描器的配置

Spring IOC 掃描器
   做用:bean對象統一進行管理,簡化開發配置,提升開發效率

   一、設置自動化掃描的範圍
            若是bean對象未在指定包範圍,即便聲明瞭註解,也沒法實例化
   二、使用指定的註解(聲明在類級別)    bean對象的id屬性默認是 類的首字母小寫
         Dao層:
            @Repository
         Service層:
            @Service
         Controller層:
            @Controller
         任意類:
            @Component
    注:開發過程當中建議按照指定規則聲明註解
  1. 設置自動化掃描範圍

    <?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
           http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 設置自動化掃描的範圍 -->
        <context:component-scan base-package="com.xxxx"/>
    
    </beans>
  2. 使用特定的註解

    @Repository (Dao層)

    @Repository
    public class ResourceDao {
    
        public void  test() {
            System.out.println("ResourceDao...");
        }
    }

    @Service(Service層 )

    @Service
    public class ResourceService {
    
        @Resource
        private ResourceDao resourceDao; // service層注入dao層的bean對象
    
        public  void  test() {
            System.out.println("ResourceService...");
            resourceDao.test();
        }
    }

    @Controller (Controller 層 )

    @Controller
    public class ResourceController {
    
        @Autowired
        private ResourceService resourceService; // Controller層注入service層的bean對象
    
        public  void  test() {
            System.out.println("ResourceController...");
            resourceService.test();
        }
    }

    @Component (任意層)

    @Component
    public class PropertyUtils {
        public void test(){
            System.out.println("PropertyUtils...");
        }
    }

Spring 模擬用戶登陸流程

Dao層 (查詢用戶記錄)
  1. 定義JavaBean User.java

    package com.xxxx.po;
    
    /**
     * User 用戶實體類
     */
    public class User {
    
        private String userName; // 用戶名稱
        private String userPwd; // 用戶密碼
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getUserPwd() {
            return userPwd;
        }
    
        public void setUserPwd(String userPwd) {
            this.userPwd = userPwd;
        }
    }
  2. 編寫Dao層 UserDao.java

    package com.xxxx.dao;
    
    import com.xxxx.po.User;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDao {
        
        private final String USERNAME = "admin";
        private final String USERPWD = "admin";
        
        /**
         * 經過用戶名稱查詢用戶對象
         * @param userName
         * @return
         */
        public User queryUserByUserName(String userName){
            User user = null;
            // 判斷用戶名稱是否正確
            if(!USERNAME.equals(userName)){
                // 若是不正確,返回null
                return null;
            }
            // 若是正確,將用戶名稱和密碼設置到user對象中
            user = new User();
            user.setUserName(USERNAME);
            user.setUserPwd(USERPWD);
    
            return user;
        }
    }
Service層 (業務邏輯處理)
  1. 定義業務處理返回消息模型 MessageModel.java

    package com.xxxx.po.vo;
    
    /**
     * 定義業務處理返回消息模型
     *     封裝返回結果
     */
    public class MessageModel {
    
        private Integer resultCode = 1; // 結果狀態碼  1=成功,0=失敗
        private String resultMsg = "操做成功!"; // 結果提示信息
    
        public Integer getResultCode() {
            return resultCode;
        }
    
        public void setResultCode(Integer resultCode) {
            this.resultCode = resultCode;
        }
    
        public String getResultMsg() {
            return resultMsg;
        }
    
        public void setResultMsg(String resultMsg) {
            this.resultMsg = resultMsg;
        }
    }
  2. 編寫Service層 UserService.java

    package com.xxxx.service;
    
    import com.xxxx.dao.UserDao1;
    import com.xxxx.po.User;
    import com.xxxx.po.vo.MessageModel;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    @Service
    public class UserService {
        @Resource
        private UserDao userDao;
    
        /**
         * 驗證用戶登陸
         * @param userName
         * @param userPwd
         * @return
         */
        public MessageModel userLoginCheck(String userName, String userPwd){
            // 定義業務處理返回消息模型
            MessageModel messageModel = new MessageModel();
            // 判斷用戶名稱是否非空
            if(null == userName || "".equals(userName.trim())){
                messageModel.setResultCode(0);
                messageModel.setResultMsg("用戶名不能爲空!");
                return messageModel;
            }
            // 判斷用戶密碼是否爲空
            if(null == userPwd || "".equals(userPwd.trim())){
                messageModel.setResultCode(0);
                messageModel.setResultMsg("密碼不能爲空!");
                return messageModel;
            }
            // 經過用戶名稱查詢用戶對象
            User user = userDao.queryUserByUserName(userName);
            // 判斷用戶對象是否爲空
            if(null == user){
                messageModel.setResultCode(0);
                messageModel.setResultMsg("該用戶不存在!");
                return messageModel;
            }
            // 若是用戶對象不爲空,判斷密碼是否正確
            if(!user.getUserPwd().equals(userPwd)){
                messageModel.setResultCode(0);
                messageModel.setResultMsg("用戶密碼不正確!");
                return messageModel;
            }
            // 登陸成功
            messageModel.setResultMsg("登陸成功!");
            
            return messageModel;
        }
    }
Controller層 (接收請求)
  1. 編寫Controller層 UserController.java

    package com.xxxx.controller;
    
    import com.xxxx.po.vo.MessageModel;
    import com.xxxx.service.UserService1;
    import org.springframework.stereotype.Controller;
    
    import javax.annotation.Resource;
    
    @Controller
    public class UserController {
        @Resource
        private UserService userService;
    
        /**
         * 用戶登陸
         * @param userName
         * @param userPwd
         * @return
         */
        public MessageModel login(String userName, String userPwd){
            // 調用Dao層判斷用戶登陸操做,返回結果
            MessageModel messageModel = userService.userLoginCheck(userName, userPwd);
            return messageModel;
        }
    }
經過 JUnit 進行測試
package com.xxxx;

import com.xxxx.controller.UserController;
import com.xxxx.po.vo.MessageModel;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestLogin {

    @Test
    public void test() {
        // 獲得Spring容器上下文環境
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
        // 獲得UserController實例化對象
        UserController userController = (UserController) ac.getBean("userController");
        // 傳入參數調用UserController的方法,返回封裝類
        MessageModel messageModel= userController.login("admin", "admin");

        System.out.println("狀態碼:" + messageModel.getResultCode() + ",提示信息:" + messageModel.getResultMsg());
    }
}

Bean的做用域與生命週期

Bean的做用域

​ 默認狀況下,咱們從Spring容器中拿到的對象均是單例的,對於bean的做用域類型以下:

singleton 做用域

注意: lazy-init是懶加載, 若是等於true時做用是指Spring容器啓動的時候不會去實例化這個bean, 而是在程序調用時纔去實例化. 默認是false即Spring容器啓動時實例化.

​ 默認狀況下,被管理的bean只會IOC容器中存在一個實例,對於全部獲取該Bean的操做Spring容器將只返回同一個Bean。

容器在啓動的狀況下就實例化全部singleton 的 bean對象,並緩存與容器中

lazy-init屬性(懶加載)
若是爲false,則在IOC容器啓動時會實例化bean對象,默認false
​ 若是爲true,則IOC容器啓動時不會實例化Bean對象,在使用bean對象時纔會實例化

lazy-init設置爲false有什麼好處?
​ 1)能夠提早發現潛在的配置問題
​ 2)Bean 對象存在於緩存中,使用時不用再去實例化bean,加快程序運行效率

什麼對象適合做爲單例對象?
​ 通常來講對於無狀態或狀態不可改變的對象適合使用單例模式。(不存在會改變對象狀態的成員變量)
​ 好比:controller層、service層、dao層

什麼是無狀態或狀態不可改變的對象?

​ 實際上對象狀態的變化每每均是因爲屬性值得變化而引發的,好比user類 姓名屬性會有變化,屬性姓名的變化通常會引發user對象狀態的變化。對於咱們的程序來講,無狀態對象沒有實例變量的存在,保證了線程的安全性,service 層業務對象便是無狀態對象。線程安全的。

prototype 做用域

​ 經過scope=「prototype」 設置bean的類型 ,每次向Spring容器請求獲取Bean都返回一個全新的Bean,相對於"singleton"來講就是不緩存Bean,每次都是一個根據Bean定義建立的全新Bean。

Web應用中的做用域
  1. request做用域

    表示每一個請求須要容器建立一個全新Bean。好比提交表單的數據必須是對每次請求新建一個Bean來保持這些表單數據,請求結束釋放這些數據。

  2. session做用域

    表示每一個會話須要容器建立一個全新Bean。好比對於每一個用戶通常會有一個會話,該用戶的用戶信息須要存儲到會話中,此時能夠將該Bean做用域配置爲session級別。

  3. globalSession做用域

    相似於session做用域,其用於portlet(Portlet是基於Java的Web組件,由Portlet容器管理,並由容器處理請求,生產動態內容)環境的web應用。若是在非portlet環境將視爲session做用域。

​ 配置方式和基本的做用域相同,只是必需要有web環境支持,並配置相應的容器監聽器或攔截器從而能應用這些做用域,目前先熟悉概念,後續集成web時講解具體使用,你們只須要知道有這些做用域就能夠了。

Bean的生命週期

​ 對比已經學過的servlet 生命週期(容器啓動裝載並實例化servlet類,初始化servlet,調用service方法,銷燬servlet)。

​ 一樣對於Spring容器管理的bean也存在生命週期的概念

在Spring中,Bean的生命週期包括Bean的定義、初始化、使用和銷燬4個階段

Bean的定義

​ 在Spring中,一般是經過配置文檔的方式來定義Bean的。

​ 在一個配置文檔中,能夠定義多個Bean。

Bean 的初始化

​ 默認在IOC容器加載時,實例化對象。

Spring bean 初始化有兩種方式:

方式一:在配置文檔中經過指定 init-method 屬性來完成。

public class RoleService {
    // 定義初始化時須要被調用的方法
    public void init() {
        System.out.println("RoleService init...");
    }
}
<!-- 經過init-method屬性指定方法 -->
<bean id="roleService" class="com.xxxx.service.RoleService" init-method="init"></bean>

方式二: 實現 org.springframework.beans.factory.InitializingBean 接口。

public class RoleService implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("RoleService init...");
    }
}
<bean id="roleService" class="com.xxxx.service.RoleService" ></bean>

​ Bean對象實例化過程是在Spring容器初始化時被實例化的,但也不是不可改變的,能夠經過 lazy-init=「true」 屬性延遲bean對象的初始化操做,此時再調用getBean 方法時纔會進行bean的初始化操做

Bean 的使用

方式一:使用 BeanFactory

// 獲得Spring的上下文環境
BeanFactory factory = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) factory.getBean("roleService");

方式二:使用 ApplicationContext

// 獲得Spring的上下文環境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) ac.getBean("roleService");
Bean的銷燬

​ 實現銷燬方式(Spring容器會維護bean對象的管理,能夠指定bean對象的銷燬所要執行的方法)。

步驟一:實現銷燬方式(Spring容器會維護bean對象的管理,能夠指定bean對象的銷燬所要執行的方法)

<bean id="roleService" class="com.xxxx.service.RoleService" destroy-method="destroy"></bean>
**步驟二:**經過 AbstractApplicationContext 對象,調用其close方法實現bean的銷燬過程
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
ctx.close();
IOC/DI-控制反轉和依賴注入
      將對象實例化的建立過程轉交給外部容器(IOC容器 充當工廠角色)去負
相關文章
相關標籤/搜索