Spring框架入門

Spring 框架概述 
1.1. 什麼是Spring  
Spring是分層的JavaSE/EE full-stack(一站式) 輕量級開源框架 
分層: 來自JavaEE體系結構 (客戶端層、 web層、業務層、持久層 ) 
服務器端三層結構 (web層、業務層、持久層)  
Servlet + JSP —- web層技術 —- Struts2框架  
JDBC 接口 —- 持久化技術 —- Hibernate框架  
EJB 框架(複雜) —- 業務層技術 —- Spring 框架 (取代EJB出現 )html

Spring 出現,就是爲了解決常見JavaEE 企業開發問題 ! 
一站式: Spring框架提供了 web層(表現層) SpringMVC、 業務層 IoC、AOP和事務管理、 持久層JdbcTemplate 各層javaEE軟件開發解決方案  
輕量級:相對於EJB框架而言 java

Spring核心 
IoC: 控制反轉 
AOP: 面向切面編程 
官網:http://www.springsource.org/web

Spring 給軟件開發帶來了什麼 
方便解耦,簡化開發 (Spring IoC特性) 
AOP編程的支持 
聲明式事務的支持 
方便程序的測試 
方便集成各類優秀框架 (整合Struts2 、 Hibernate 、MyBatis 、Struts1 ) 
下降JavaEE API的使用難度 (Spring 提供大量工具類, 簡化代碼編寫 )spring

Spring 體系結構編程

體系結構

Spring框架IoC和DI數組

下載開發包 
http://projects.spring.io/spring-framework/ 網址下載開發包  
最新版本4.1 課程 3.2 tomcat

開發包3.2 
spring-framework-3.2.0.RELEASE-dist.zip 
依賴吧 3.0 (常見開源技術jar包) 
spring-framework-3.0.2.RELEASE-dependencies.zip服務器

Spring目錄結構session

Docs 文檔目錄(API 和 規範)
Libs 開發須要jar包(包含javadoc和source)
Schema 全部約束文檔
  • 1
  • 2
  • 3
  • 4

導入jar包到項目 
進行Ioc和DI的開發,只須要導入最核心spring的jar包 
spring核心jar包app

Spring運行,使用commons-logging日誌技術  
(commons-logging 和 slf4j 都屬於統一日誌接口, 整合log4j日誌實現 ) 
導入log4j jar 

理解IoC和DI的概念 
什麼是IoC ? 什麼是DI ? 區別是什麼?  
IoC: 控制反轉, 解決程序對象緊密耦合問題(工廠+反射+ 配置文件), 將程序中原來構造對象的權限,交給IoC容器來構造,當程序須要對象,找IoC容器獲取。

IOC

DI : 依賴注入 , IoC容器須要爲程序提供依賴對象,返回對象所依賴對象一同能夠提供(Servlet須要Service, 找Ioc容器獲取Service, Service由容器提供,Service依賴DAO ,IoC容器提供Service對象同時, 將Service依賴DAO 注入到Service中)

編寫IoC和DI入門案例 
IOC&DI

tra

將全部對象,交給IoC容器(Spring)來管理

(spring配置文件 一般能夠在src 或者 WEB-INF目錄下, 一般名稱 applicationContext.xml ) 
參考文檔 : xsd-config.html 
經過applicationContext.xml 配置Spring管理對象 

在程序中經過ApplicationContext接口 獲取spring工廠對象  
1.ClassPathXmlApplicationContext 讀取 src下配置文件 
2.FileSystemXmlApplicationContext 讀取WEB-INF下配置文件  
IoC 經過工廠,從Spring容器獲取建立好對象 

@Test
    public void testRegist2() {
        // 從Ioc容器得到對象
        // 一、 獲取Ioc容器工廠對象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 二、 從Ioc容器工廠 獲取須要對象 (根據bean的id 獲取)
        UserServlet userServlet = (UserServlet) applicationContext.getBean("userServlet");
        userServlet.regist();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

DI 在經過工廠獲取,spring對象時,spring同時提供對象所依賴的對象 

// 依賴注入 (Spring 在構造 UserServlet對象時,同時將構造好 UserService對象,注入到UserServlet對象中 )
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

配置

<!-- 將對象的建立權,交給容器管理 -->
    <bean id="userServlet" class="cn.itcast.spring.a_quickstart.UserServlet" >
        <!-- 返回UserServlet時,同時提供 依賴的UserService對象 -->
        <!-- 
            name 屬性 對應 對象中 setXXX 方法, 表明對象中含有某個屬性
            ref 引用了另外一個Bean對象id 
         -->
        <property name="userService" ref="userService" />
    </bean>
    <bean id="userService" class="cn.itcast.spring.a_quickstart.UserServiceImpl"/>
    <bean id="userDAO" class="cn.itcast.spring.a_quickstart.UserDAOImpl" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

BeanFactory 和 ApplicationContext 接口 
ApplicationContext 是 BeanFactory 子接口,BeanFactory 纔是Spring框架最核心工廠接口。  
ApplicationContext 是對BeanFactory 接口擴展, 企業開發不多直接使用BeanFactory  
ApplicationContext 會在容器初始化時,對其中管理Bean對象進行建立, BeanFactory 會在對象獲取時才進行初始化 。

IoC容器裝配Bean(xml配置) 
3.1.三種實例化Bean的方式  
方式一: 使用類構造器實例化對象 

<!-- 方式一  使用構造器(無參數)實例化對象  -->
    <bean id="bean1" 
class="cn.itcast.spring.b_instance.Bean1" />
  • 1
  • 2
  • 3
  • 4

方式二: 使用靜態工廠 靜態方法,對對象實例化

<!-- 方式二 使用靜態工廠實例化對象 -->
    <!-- 調用 工廠的 factory-method 返回對象 -->
    <bean id="bean2" 
class="cn.itcast.spring.b_instance.Bean2Factory" factory-method="getBean2" />
  • 1
  • 2
  • 3
  • 4
  • 5

方式三: 使用實例工廠 實例方法 對對象實例化 

<!-- 方式三 使用實例工廠實例化對象 -->
    <!-- 先實例化工廠 -->
    <bean id="bean3Factory" class="cn.itcast.spring.b_instance.Bean3Factory" />
    <!-- 再經過工廠對象的實例方法,構造目標對象 -->
    <bean id="bean3" factory-bean="bean3Factory" 
factory-method="getBean3" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

應用場景: 大多數狀況,能夠經過構造器直接實例化, 只有在對象構造過程很是複雜的狀況下,纔會採用工廠實例化的方式 

Bean的做用域 
最經常使用 singleton 和 prototype 兩種  
Singleton (單例): 在一個BeanFactory對象中,引用惟一的一個目標實例  
Prototype (多例): 每次經過工廠執行getBean時,返回不一樣實例對象  
Request (請求範圍) : 建立對象保存在request範圍,若是request銷燬,對象銷燬 
Session (會話範圍): 建立對象保存Session中, 若是session銷燬,對象銷燬 
* globalSession (全局會話 ) :分佈式系統,全局會話的概念, 一次登陸,應用多個系統 

<!-- 第三部分 Bean的做用域 -->
    <!-- 經過scope屬性,指定bean做用域 (默認做用域 singleton) -->
    <bean id="singletonBean" class="cn.itcast.spring.c_scope.SingletonBean" /><!-- 單例 -->
    <bean id="prototypeBean" class="cn.itcast.spring.c_scope.PrototypeBean" scope="prototype"/> <!-- 多例 -->
  • 1
  • 2
  • 3
  • 4

單例Bean 在容器初始化時,實例化 (只實例化一次 ) 
多例Bean 在工程執行getBean時 纔會實例化 (每調用一次,返回不一樣對象 )

Bean的生命週期 
能夠經過 init-method屬性配置 Bean對象初始化執行方法,destory-method屬性配置Bean對象銷燬的方法 
(初始化方法和構造方法 有區別? 構造方法做用申請空間,爲對象基本屬性初始化 , 初始化方法 對象複雜構造過程 , java語言建議將對象複雜構造過程單獨抽取 初始化方法 )

public class LifeCycleBean implements IHello {
    public LifeCycleBean() {
        System.out.println("LifeCycleBean 構造...");
    }

    public void setup() {
        System.out.println("LifeCycleBean 初始化...");
    }

    public void teardown() {
        System.out.println("LifeCycleBean 銷燬...");
    }

    @Override
    public void sayHello() {
        System.out.println("hello ,itcast...");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

配置

<!-- 第四部分  bean的生命週期 -->
    <bean id="lifeCycleBean" class="cn.itcast.spring.d_lifecycle.LifeCycleBean" 
        init-method="setup" destroy-method="teardown" />
  • 1
  • 2
  • 3

在對象構造後,當即執行初始化init , 默認沒有執行destroy 銷燬方法

public class LifeCycleTest {
    @Test
    public void testInitDestroy() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        IHello lifeCycleBean = (IHello) applicationContext.getBean("lifeCycleBean");
        System.out.println(lifeCycleBean);
        lifeCycleBean.sayHello();

        // 必須手動調用 容器銷燬的方法 --- web服務器tomcat,自動調用容器銷燬
        applicationContext.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

後處理器(後處理Bean)— 補充

在Spring構造Bean對象過程當中,有一個環節對Bean對象進行 後處理操做 (鉤子函數) —– Spring 提供 BeanPostProcessor 接口 
能夠自定義類,實現 BeanPostProcessor 接口,配置到Spring容器中,在構造對象時,spring容器會調用接口中方法 

後處理器,在對象構造過程當中,提供代理, 是AOP自動代理核心 ! 

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    /**
     * bean 表明Spring容器建立對象
     * beanName 表明配置對象對應 id屬性
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            System.out.println("後處理器 初始化後執行...");
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        // 針對bean id 爲 lifeCycleBean的對象 進行代理
        if (beanName.equals("lifeCycleBean")) {
            System.out.println("後處理器 初始化前執行...");
            return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("執行代理.....");
                    return method.invoke(bean, args);
                }
            });
        }
        return bean;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

Bean的依賴注入

三種注入方式 

構造參數的屬性輸入

public class Car {
    private String name;
    private double price;

    // 爲Car類 提供構造方法
    public Car(String name, double price) {
        super();
        this.name = name;
        this.price = price;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

經過constructor-arg 屬性進行構造參數注入

<!-- 構造方法屬性注入 -->
    <bean id="car" class="cn.itcast.spring.e_di.Car">
        <!-- 經過constructor-arg 注入構造函數的參數 -->
        <!-- index 表明參數順序 ,第一個參數 0
             type 表明參數類型 
             name 表明參數的名稱 
             value 注入參數的值
             ref  引用另外一個bean元素的id
         -->
        <constructor-arg index="0" type="java.lang.String" value="寶馬"/>
        <constructor-arg index="1" type="double" value="1000000"/>
    </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

setter方法屬性注入

public class Employee {
    private int id;
    private String name;

    private Car car;// 複雜元素

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

    public void setName(String name) {
        this.name = name;
    }

    public void setCar(Car car) {
        this.car = car;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在配置文件 使用 元素完成setter屬性注入

<!-- setter方法屬性注入 -->
    <bean id="employee" class="cn.itcast.spring.e_di.Employee" >
        <!-- 經過property 注入setter方法屬性 (屬性名稱, 由setter方法推理得到)-->
        <!-- 
            name 屬性名稱 (由setter方法得到)
            value 注入參數的值
            ref 引用另外一個Bean元素的id
         -->
        <property name="id" value="100001" />
        <property name="name" value="張三" />
        <!-- 注入複雜對象 -->
        <property name="car" ref="car" />
    </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

屬性注入深刻 
【知識點】 p名稱空間的使用  
P名稱空間,在spring2.5版本後引入,爲了簡化屬性依賴注入(setter方法) 
第一步: 在配置文件,引入p名稱空間 

xmlns:p="http://www.springframework.org/schema/p"
  • 1

第二步: 簡化setter方法注入配置 

<!-- 使用p命名空間注入 -->
    <bean id="employee2" class="cn.itcast.spring.e_di.Employee" 
        p:eid="100002" p:name="李四" p:car-ref="car"/>
  • 1
  • 2
  • 3

【知識點】 spEL表達式的使用 
在spring3.0以後,引入spEL 表達式語言,簡化屬性注入  
參考 「Spring_表達式語言.pdf」 學習 
語法: #{表達式} 
用法一: 直接經過value注入,引用一個Bean對象  
用法二: 引用一個Bean對象屬性  
用法三: 直接調用對象的方法 

public class ValueBean {
    private int id = 10003;
    private String name = "jor";

    public int getId() {
        return id;
    }

    public String pickName() {
        return name;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
<!-- spEL使用 -->
    <bean id="valueBean" class="cn.itcast.spring.e_di.ValueBean" />
    <bean id="employee3" class="cn.itcast.spring.e_di.Employee" >
        <!-- 調用valueBean的getId -->
        <property name="eid" value="#{valueBean.id}" />
        <!-- 直接調用對象的方法 -->
        <property name="name" value="#{valueBean.pickName().toUpperCase()}" />
        <!-- #{car} 效果相似 ref  -->
        <property name="car" value="#{car}" />
    </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

集合元素類型屬性注入

Spring爲每種集合都提供一個元素標籤 進行注入

public class CollectionBean {
    private List<String> list;
    private Set<Integer> set;
    private Map<String, Integer> map;
    private Properties properties;

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<Integer> set) {
        this.set = set;
    }

    public void setMap(Map<String, Integer> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
<!-- 集合類型屬性注入 -->
    <bean id="collectionBean" class="cn.itcast.spring.e_di.CollectionBean">
        <!-- 
            array 注入數組
            list 注入List集合
            set 注入Set集合
            map 注入Map集合
            props 注入 Properties 集合
         -->
        <property name="list">
            <list>
                <!-- 
                    value 注入基本數據類型, String 類型
                    ref 注入引用Bean的id
                 -->
                 <value>aaa</value>
                 <value>bbb</value>
                 <value>ccc</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>10</value>
                <value>10</value>
                <value>20</value>
            </set>
        </property>
        <property name="map">
            <map>
                <!-- map中每一個元素都是鍵值對 -->
                <entry key="abc" value="10"></entry>
                <entry key="def" value="20"></entry>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="qwe123">asd456</prop>
                <prop key="tyu567">hjk789</prop>
            </props>
        </property>
    </bean>