Spring入門詳解【基礎掃盲】

Spring基礎介紹

http://blog.csdn.net/a1n9n7e/article/details/77802773

什麼是Spring、Strust、Hibernate

struts 是 web 框架(jsp/action/actionfrom)

hibernate 是 orm框架,處於持久層.

spring 是容器框架,用於配置bean,並維護bean之間關係的框架

Spring中重要概念:IOC / DI

IOC是什麼?

ioc(inverse of controll ) 控制反轉: 所謂控制反轉就是把創建對象(bean),和維護對象(bean)的關係的權利從程序中轉移到spring的容器(applicationContext.xml),而程序本身不再維護.

DI是什麼?

di(dependency injection) 依賴注入: 實際上di和ioc是同一個概念,spring設計者認爲di更準確表示spring核心技術。

依賴注入(DI)背後的基本原理是對象之間的依賴關係(即一起工作的其它對象)只會通過以下幾種方式來實現:構造器的參數、工廠方法的參數,或給由構造函數或者工廠方法創建的對象設置屬性。因此,容器的工作就是創建bean時注入那些依賴關係。相對於由bean自己來控制其實例化、直接在構造器中指定依賴關係或者類似服務定位器(Service Locator)模式這3種自主控制依賴關係注入的方法來說,控制從根本上發生了倒轉,這也正是控制反轉(Inversion of Control, IoC) 名字的由來。

應用DI原則後,代碼將更加清晰。而且當bean自己不再擔心對象之間的依賴關係(甚至不知道依賴的定義指定地方和依賴的實際類)之後,實現更高層次的鬆耦合將易如反掌。DI主要有兩種注入方式,即Setter注入構造器注入

AOP編程

容器和bean

什麼是bean

在Spring中,那些組成你應用程序的主體(backbone)及由Spring IoC*容器所管理的對象,被稱之爲*bean
簡單地講,bean就是由Spring*容器*初始化、裝配及管理的對象,除此之外,bean就與應用程序中的其他對象沒有什麼區別了。
而bean定義以及bean相互間的依賴關係將通過配置元數據來描述。

什麼是容器

org.springframework.beans.factory.BeanFactory 是Spring IoC*容器*的實際代表者,IoC容器負責容納此前所描述的bean,並對bean進行管理。

在Spring中,BeanFactory是IoC容器的核心接口。 它的職責包括:實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。

Spring爲我們提供了許多易用的BeanFactory實現, XmlBeanFactory就是最常用的一個。該實現將以XML方式描述組成應用的對象 以及對象間的依賴關係。XmlBeanFactory類將獲取此XML*配 置元數據*,並用它來構建一個完全可配置的系統或應用。

Bean的作用域

作用域 描述
singleton 在每個Spring IoC容器中一個bean定義對應一個對象實例。
prototype 一個bean定義對應多個對象實例。
request 在一次HTTP請求中,一個bean定義對應一個實例;即每次HTTP請求將會有各自的bean實例, 它們依據某個bean定義創建而成。該作用域僅在基於web的Spring ApplicationContext情形下有效。
session 在一個HTTP Session中,一個bean定義對應一個實例。該作用域僅在基於web的Spring ApplicationContext情形下有效。
global session 在一個全局的HTTP Session中,一個bean定義對應一個實例。典型情況下,僅在使用portlet context的時候有效。該作用域僅在基於web的Spring ApplicationContext情形下有效。

注意:儘量使用scop=”singleton“,避免使用propotype,以爲propotype對性能影響比較大。

Bean的生命週期

Bean的實例化與銷燬

spring實例化bean或銷燬bean時,有時需要作一些處理工作,因此spring可以在創建和拆卸bean的時候調用bean的兩個生命週期方法。

<!-- 通過 配置init-method和destory-method,實現對bean的初始化,和銷燬bean時的相關操作-->
<bean class="Foo" init-method="init" destory-method="destroy">
初始化回調

實現org.springframework.beans.factory.InitializingBean接口允許容器在設置好bean的所有必要屬性後,執行初始化事宜。InitializingBean接口僅指定了一個方法:

void afterPropertiesSet() throws Exception;

通常,要避免使用InitializingBean接口並且不鼓勵使用該接口,因爲這樣會將代碼和Spring耦合起來,有一個可選的方案是,可以在Bean定義中指定一個普通的初始化方法,然後在XML配置文件中通過指定init-method屬性來完成。如下面的定義所示:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}

…效果與下面完全一樣…

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {

    public void afterPropertiesSet() {
        // do some initialization work
    }
}
析構回調

實現org.springframework.beans.factory.DisposableBean接口的bean允許在容器銷燬該bean的時候獲得一次回調。DisposableBean接口也只規定了一個方法:

void destroy() throws Exception;

通常,要避免使用DisposableBean標誌接口而且不鼓勵使用該接口,因爲這樣會將代碼與Spring耦合在一起,有一個可選的方案是,在bean定義中指定一個普通的析構方法,然後在XML配置文件中通過指定destroy-method屬性來完成。如下面的定義所示:

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

…效果與下面完全一樣…

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {

    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

… 但是沒有將代碼與Spring耦合在一起。

注意:

spring也提供了兩個接口來實現相同的功能:

InitializingBean和DisposableBean.

InitializingBean接口提供了一個afterPropertiesSet()方法。

DisposableBean接口提供了destroy().

不推薦使用該接口,它將你的bean和springAPI邦定在一起。

裝配Bean

在spring容器內拼湊bean叫做裝配。裝配bean的時候,需要告訴容器哪些bean 以及容器如何使用依賴注入將它們配合在一起。

使用XML裝配

xml是最常見的spring應用系統配置源。

幾種spring容器都支持使用xml裝配bean,包括:

  1. XmlBeanFactory:調用ClassPathResource載入上下文定義文件(比如applicationContext.xml)。
  2. ClassPathXmlApplicationContext:從類路徑載入上下文定義文件。
  3. XmlWebApplicationContext:從web應用上下文中載入定義文件。
裝配方式

上下文定義文件的根元素是.有多個子元素。每個元素定義了一個bean如何被裝配到spring容器中。

<beans>
       <bean id="foo" class="...Foo"/>
       <bean id="bar" class="...Bar"/>
</beans>

對bean的最基本的配置包括bean的ID和他的全稱類名。

繼承裝配

① 繼承

② 繼承配置

③ 覆蓋父 Bean配置

④ 可以設置 的abstract 屬性爲 true, Spring 不會實例化該Bean

通過添加 parent 屬性

<bean id="graduate" parent="student" class="com.inherit.Graduate">
實例

父類

public class Student {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

子類

public class Graduate extends Student {
    private String degree;
    public String getDegree() {
        return degree;
    }
    public void setDegree(String degree) {
        this.degree = degree;
    }
}

beans.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"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    <!-- 配置一個學生對象 -->
    <bean id="student" class="com.inherit.Student">
        <property name="name" value="Nick" />
        <property name="age" value="25" />
    </bean>
    <!-- 配置子類 -->
    <bean id="graduate" parent="student" class="com.inherit.Graduate">
        <!-- 如果自己配置屬性name,age,可以覆蓋父類屬性 -->
        <property name="degree"  value="master" />
        <property name="name" value="Tom" />
    </bean>
</beans>

測試類

public class App {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/inherit/beans.xml");
        Graduate graduate = (Graduate) ac.getBean("graduate");

        System.out.println(graduate.getName());
        System.out.println(graduate.getAge());
        System.out.println(graduate.getDegree());
    }
}

輸出:

Tom //注意這裏通過對子類屬性的配置覆蓋了父類屬性,java繼承的特性
25
master

自動裝配Bean的屬性值(重點)

Spring IoC容器可以自動裝配(autowire)相互協作bean之間的關聯關係。

* Autowiring modes*

模式 說明
no
byName 根據屬性名自動裝配。此選項將檢查容器並根據名字查找與屬性完全一致的bean,並將其與屬性自動裝配。例如,在bean定義中將autowire設置爲by name,而該bean包含master屬性(同時提供setMaster(..)方法),Spring就會查找名爲master的bean定義,並用它來裝配給master屬性。
byType 如果容器中存在一個與指定屬性類型相同的bean,那麼將與該屬性自動裝配。如果存在多個該類型的bean,那麼將會拋出異常,並指出不能使用byType方式進行自動裝配。若沒有找到相匹配的bean,則什麼事都不發生,屬性也不會被設置。如果你不希望這樣,那麼可以通過設置dependency-check="objects"讓Spring拋出異常。
constructor byType的方式類似,不同之處在於它應用於構造器參數。如果在容器中沒有找到與構造器參數類型一致的bean,那麼將會拋出異常。
autodetect 通過bean類的自省機制(introspection)來決定是使用constructor還是byType方式進行自動裝配。如果發現默認的構造器,那麼將使用byType方式。

如果直接使用propertyconstructor-arg注入依賴的話,那麼將總是覆蓋自動裝配。而且目前也不支持簡單類型的自動裝配,這裏所說的簡單類型包括基本類型、StringClass以及簡單類型的數組(這一點已經被設計,將考慮作爲一個功能提供)。byTypeconstructor自動裝配模式也可用於數組和指定類型的集合。在這種情況下容器中的所有匹配的自動裝配對象將被用於滿足各種依賴。

使用說明
  1. byName的用法:
<!-- 配置一個master對象 -->

<bean id="master"class="com.hsp.autowire.Master" autowire="byName">

<property name="name">

<value>順平</value>

</property>

</bean>

<!-- 配置dog對象 -->

<bean id="dog" class="com.hsp.autowire.Dog">

<property name="name"value="小黃"/>

<property name="age"value="3"/>

</bean>
  1. byType: byType:尋找和屬性類型相同的bean,找不到,裝不上,找到多個拋異常。

  2. constructor: autowire=”constructor”

說明 :查找和bean的構造參數一致的一個或多個bean,若找不到或找到多個,拋異常。按照參數的類型裝配

  1. autodetect

說明 : autowire=”autodetect” (3)和(2)之間選一個方式。不確定性的處理與(3)和(2)一致。

  1. defualt

這個需要在

注入依賴

依賴注入(DI)背後的基本原理是對象之間的依賴關係(即一起工作的其它對象)只會通過以下幾種方式來實現:構造器的參數、工廠方法的參數,或給由構造函數或者工廠方法創建的對象設置屬性。也就是在創建Bean時通過容器向Bean中注入Bean與Bean之間的各種依賴關係

構造器注入

構造器參數解析

構造器參數解析根據參數類型進行匹配,如果bean的構造器參數類型定義非常明確,那麼在bean被實例化的時候,bean定義中構造器參數的定義順序就是這些參數的順序,依次進行匹配,比如下面的代碼

package x.y;

public class Foo {

    public Foo(Bar bar, Baz baz) {
        // ...
    }
}

上述例子中由於構造參數非常明確(這裏我們假定 BarBaz之間不存在繼承關係)。因此下面的配置即使沒有明確指定構造參數順序(和類型),也會工作的很好。

<beans>
    <bean name="foo" class="x.y.Foo">
        <constructor-arg>
            <bean class="x.y.Bar"/>
        </constructor-arg>
        <constructor-arg>
            <bean class="x.y.Baz"/>
        </constructor-arg>
    </bean>
</beans>

我們再來看另一個bean,該bean的構造參數類型已知,匹配也沒有問題(跟前面的例子一樣)。但是當使用簡單類型時,比如<value>true<value>,Spring將無法知道該值的類型。不借助其他幫助,他將無法僅僅根據參數類型進行匹配,比如下面的這個例子:

package examples;

public class ExampleBean {

    // No. of years to the calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
構造器參數類型匹配

針對上面的場景可以通過使用'type'屬性來顯式指定那些簡單類型的構造參數的類型,比如:

<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg type="int" value="7500000"/>
  <constructor-arg type="java.lang.String" value="42"/>
</bean>
構造參數索引

我們還可以通過index屬性來顯式指定構造參數的索引,比如下面的例子:

<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg index="0" value="7500000"/>
  <constructor-arg index="1" value="42"/>
</bean>

通過使用索引屬性不但可以解決多個簡單屬性的混淆問題,還可以解決有可能有相同類型的2個構造參數的混淆問題了,注意index是從0開始

Setter方法注入

對比兩種注入方式:

set注入的缺點是無法清晰表達哪些屬性是必須的,哪些是可選的,構造注入的優勢是通過構造強制依賴關係,不可能實例化不完全的或無法使用的bean。

對集合注入操作(set注入演示)

Bean類

public class Department {
    private String name;
    private String[] empName;
    private List<Employee> emplist;
    private Set<Employee> empSet;
    private HashMap<Integer, Employee> empMaps;

  public void setEmpMaps(HashMap<Integer, Employee> empMaps) {
        this.empMaps = empMaps;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String[] getEmpName() {
        return empName;
    }
    public void setEmpName(String[] empName) {
        this.empName = empName;
    }
    public List<Employee> getEmplist() {
        return emplist;
    }
    public void setEmplist(List<Employee> emplist) {
        this.emplist = emplist;
    }
    public Set<Employee> getEmpSet() {
        return empSet;
    }
    public void setEmpSet(Set<Employee> empSet) {
        this.empSet = empSet;
    }
    public HashMap<Integer, Employee> getEmpMaps() {
        return empMaps;
    }

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

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

}

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"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <bean id="department" class="com.collection.Department">
        <property name="name" value="財務部"/>
        <!-- 給數組注入值 -->
        <property name="empName">
            <list>
                <value>小李</value>
                <value>小王</value>
                <value>小文</value>
                <value>小張</value>
            </list>
        </property>
        <!-- 給list注入值 -->
        <property name="emplist">
            <list>
                <ref bean="emp1"/>
                <ref bean="emp2" />
                <ref bean = "emp3"/>
            </list>
        </property>
        <!-- 給set注入值 -->
        <property name="empSet">
          <!-- 測試set集合的不重複特性-->
            <set>
                <ref bean="emp1"/>
                <ref bean="emp2" />
                <ref bean = "emp3"/>
                <ref bean="emp2" />
                <ref bean = "emp3"/>
                <ref bean="emp2" />
                <ref bean = "emp3"/>
                <ref bean="emp2" />
                <ref bean = "emp3"/>
                <ref bean="emp2" />
                <ref bean = "emp3"/>
            </set>
        </property>
        <!-- 給map注入值 -->
        <property name="empMaps">
            <map>
                <entry key="1" value-ref="emp1"/>
                <entry key="2" value-ref="emp2"/>
                <entry key="3" value-ref="emp3"/>
            </map>
        </property>
    </bean>
    <bean id="emp1" class="com.collection.Employee">
        <property name="name" value="北京" />
        <property name="id" value="1" />
    </bean>
    <bean id="emp2" class="com.collection.Employee">
        <property name="name" value="深圳" />
        <property name="id" value="2" />
    </bean>
    <bean id="emp3" class="com.collection.Employee">
        <property name="name" value="上海" />
        <property name="id" value="3" />
    </bean>
</beans>

測試類

package com.collection;

import java.util.Map.Entry;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/collection/beans.xml");
        Department department = (Department) ac.getBean("department");
        System.out.println(department.getName());
        System.out.println("僱員名稱");
        for (String empName : department.getEmpName()) {
            System.out.println(empName);
        }

        System.out.println("*******通過list集合取出數據******");
        for (Employee employee : department.getEmplist()) {
            System.out.println(employee.getName());
        }

        System.out.println("*******通過set集合取出數據******");
        for (Employee employee : department.getEmpSet()) {
            System.out.println(employee.getName());
        }

        System.out.println("*******通過map集合取出數據******");
        for (Entry<Integer, Employee> entry : department.getEmpMaps().entrySet()) {
            System.out.println("key: " + entry.getKey() + ", value = " + entry.getValue().getName());
        }
    }
}

輸出結果:

財務部
僱員名稱
小李
小王
小文
小張
*******通過list集合取出數據******
北京
深圳
上海
*******通過set集合取出數據******
北京
深圳
上海
*******通過map集合取出數據******
key: 1 vule = 北京
key: 2 vule = 深圳
key: 3 vule = 上海

使用Spring的特殊Bean

讓spring特殊對待這些bean。使它們可以:

  1. 通過配置後加工bean,涉及到Bean和Bean工廠生命週期。

    2.改變依賴注入,將字符串轉換成其它類型。

    3.從屬性文本裝載信息,包括信息國際化。

    4.監聽並處理其它bean及spring發佈的系統消息。

    5.知道自己在spring中的唯一表識。

對bean工廠進行後處理

分散配置(有兩種方式引入文件)

將配置文件分成幾個分散的配置文件。如數據源

<bean class="...PropertyPlaceholderConfigurer">
        <property name="locations">
             <list>
             <value>xx/yy/db.properties</value>
             <value>xx/yy/db2.properties</value>
             </list>
          </property>
    </bean>
   <!– 名字空間配置(2.5) -->
   <context:property-placeholder location="classpath:com/hsp/spring/dispatcher/db.properties"/>

感知其他bean

運行在spring容器中的bean不知道自己的註冊名,運行在哪裏。實現以下三個接口:

  • BeanNameAware:知道自己的名字。

    無需爲該接口的setBeanName()方法作任何處理,bean被載入時,容器會自動調用該方法,爲其設置id或name的值。

  • BeanFactoryAware:所處的bean工廠。

  • ApplicationContextAware:所在上下文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章