Spring框架基礎入門

一、Spring概述與環境搭建

Spring是一個輕量級控制反轉(IoC)和麪向切面(AOP)的容器框架。
Spring 是最受歡迎的企業級 Java 應用程序開發框架,數以百萬的來自世界各地的開發人員使用 Spring 框架來創建性能好、易於測試、可重用的代碼。

Spring 框架是一個開源的 Java 平臺,它最初是由 Rod Johnson 編寫的,並且於 2003 年 6 月首次在 Apache 2.0 許可下發布。

Spring環境搭建

Spring5至少要JDK8,Servlet3.1,Tomcat8.5+。請在實際開發時針對自己的 開發環境,選擇Spring版本,Spring4支持JDK6/7/8。

創建工程
配置pom.xml引入Spring依賴包

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
    </dependencies>

測試是否正常運行
創建一個類

package pojo;

public class Master { 
public void sayHello(){ 
System.out.println("hello world"); } 
}

配置spring.xml配置文件

<bean id="master" class="pojo.Master"></bean> 

創建一個測試類

@Test 
public void testSpring() { 
ApplicationContext context= new ClassPathXmlApplicationContext("spring.xml"); 
Master master = (Master)context.getBean("master"); master.sayHello(); 
}
//運行結果
//hello world

運行結果成功表示環境配置成功

集成Junit

	<!-- JUnit單元測試框架     --> 
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!--集成Junit-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.3.RELEASE</version>
    </dependency>

測試類代碼

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunne r;
import pojo.TestBean;
import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring.xml"})
public class TestUnit {
    @Resource(name = "testBean")
    private TestBean p;

    @Test
    public void testCreatePerson() {
        System.out.println(p);
    }
}

二、控制反轉IoC

在傳統的方式中,如果我們要用到一個類的對象,需要自己new一個出 來,如:

    @Test
    public void testMaster() {
        Master master = new Master();
        master.sayHello();
    }

Master的對象master在testMaster方法中創建,這時master對象的控制權是屬於testMaster的。而在Spring中,我們看不到new Master()的操作。而是通過Spring的ApplicationContext獲得:

ApplicationContext context= new ClassPathXmlApplicationContext("spring.xml"); 
//不是自己new的對象,而是從ApplicationContext中獲得 Master master = (Master) context.getBean("master");

其中:

context.getBean("master") 

getBean方法的參數值master,對應的是spring.xml中的bean的id:

<bean id="master" class="pojo.Master"></bean> 

可見master對象是由Spring創建的,其控制權屬於Sping而不屬於使用者。 這種設計叫做“控制反轉(Inversion of Control,英文縮寫爲IoC)”。

爲什麼要控制反轉
IoC把對象生成放在了XML裏定義,所以當我們需要換一個實現子類將會變成很簡單(一般這樣的對象都是實現於某種接口的),只要修改XML就可以了。
IoC的缺點是,生成一個對象的步驟變複雜了,對於不習慣這種方式的人, 會覺得有些彆扭和不直觀。對象生成因爲是使用反射編程,在效率上有些 損耗。但相對於IoC提高的維護性和靈活性來說,這點損耗是微不足道的, 除非某對象的生成對效率要求特別高。

三、多種方式實現依賴注入

1、屬性注入

屬性注入是通過屬性的setter方法進行注入,需要注入的屬性必須有setter 方法且命名必須符合規範。如屬性名是abc,那麼setter方法應爲setAbc()

基本數據類型
<property name="屬性名" value="基本類型的屬性值"/> 

示例:

<bean id="master" class="pojo.Master">
	<property name="name" value="張三"/>
</bean>
注入對象類型

需要先創建對象bean

<property name="屬性名" ref="對象的bean的id"/>

示例:

<bean id="master" class="pojo.Master">
	<property name="pet" ref="pet"/> 
</bean>
注入集合和數組類型

list和數組注入方式相同

    <!--給數組或者List賦值-->
    <property name="stringList">
        <list>
            <!--普通類型-->
            <value>abc</value>
            <value>efg</value>
            <!--注入對象類型-->
            <!--<ref bean="其他bean的id"/>-->
        </list>
    </property>
    <!--注入Set類型-->
    <property name="stringSet">
        <set>
            <!--普通類型-->
            <value>abc</value>
            <value>abc2</value>
            <!--注入對象類型-->
            <!--<ref bean="其他bean的id"/>-->
        </set>
    </property>
    <!--注入Map-->
    <property name="objectMap">
        <map>
            <!--注入普通類型-->
            <entry key="a" value="aa"/>
            <!--注入對象類型-->
            <!--<entry key="b" value-ref="其它bean的id"/>-->
        </map>
    </property>
2、構造方法注入

Spring創建對象默認使用的是類的無參構造方法。所以在某個類添加帶參構造函數的時候,記得將無參構造寫出來,否則會導致某些情況下Spring無法創建對象。

按類型注入

constructor-arg標籤用於給構造方法注入參數,type是參數類型,value是參數值。
對於有特殊字符的屬性,在value子節點中只用CDATA。如示例中,注入的 String類型參數值爲“”,其中<>是xml中的節點符號,屬於特殊字符。
注入對象類型,使用ref引用其他bean的id。

<constructor-arg type="屬性類型" value="基本類型屬性值"/>
<constructor-arg type="屬性類型" ref="對象的bean的id"/>

示例:

    <bean id="petBean" class="pojo.Cat"></bean>
    <bean id="master" class="pojo.Master">
        <!--注入基本數據類型-->
        <constructor-arg type="java.lang.Integer" value="20"/> <!-- 對於包含特殊字符的屬性值,可以在value子節點使用CDATA -->
        <constructor-arg type="java.lang.String">
            <value><![CDATA[<sansan>]]></value>
        </constructor-arg>
        <!--ref用於注入對象類型-->
        <constructor-arg type="pojo.Pet" ref="petBean"/>
    </bean>
按位置注入

如果構造方法裏有多個參數的類型是相同的,可以使用按位置注入的方式
constructor-arg 的index屬性用於指定構造方法中參數的索引,從0開始。
示例:

    <bean id="petBean" class="pojo.Cat"></bean>
    <bean id="master" class="pojo.Master">
        <constructor-arg value="20" index="1"/>
        <constructor-arg value="張三" index="0"/>
        <constructor-arg ref="pet" index="2"/>
    </bean>
按名稱注入(最常用)
    <bean id="pet" class="pojo.Cat"></bean>
    <bean id="master" class="pojo.Master">
        <!--注入基本數據類型-->
        <constructor-arg name="age" value="20"/>
        <!-- 對於包含特殊字符的屬性值,可以在value子節點使用CDATA -->
        <constructor-arg name="name">
            <value><![CDATA[<sansan>]]></value>
        </constructor-arg>
        <!--ref用於注入對象類型-->
        <constructor-arg name="pet" ref="pet"/>
    </bean>
P命名空間注入

P命名空間注入需要先引入頭文件:

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

在標籤上使用p:屬性名=“屬性值”進行值注入。使用p:屬性名-ref=”bean的id” 進行對象注入。
示例:

<bean class="pojo.Master" id="master5" p:name="築夢者" p:age="18" p:cat-ref="pet"/>
工廠方法注入

靜態工廠注入
工廠bean的class都是工廠類型,但實際的bean的對象是通過factory- method獲得的。getCat()和getDog()返回的都是Pet類型的對象。所以 catBean和dogBean都是Pet類型的。

PetShop類:

package pojo;

public class PetShop {
    public static Pet getDog() {
        return new Dog();
    }

    public static Pet getCat() {
        return new Cat();
    }

    public static Pet getPet(String name) {
        if (name == "cat") {
            return new Cat();
        } else {
            return new Dog();
        }
    }

配置文件寫法

    <!--創建工廠bean,調用不帶參數的方法-->
    <bean id="catBean" class="pojo.PetShop" factory- method="getCat"/>
    <!--創建工廠bean,調用帶參數的方法-->
    <bean id="dogBean" class="pojo.PetShop" factory- method="getPet">
        <constructor-arg value="dog"/>
    </bean>
    <!--可以給master分別注入catBean和dogBean,看運行結果-->
    <bean id="master" class="pojo.Master">
        <property name="pet" ref="dogBean"/>
    </bean>

測試

@Test 
public void testFactory() { 
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); 
Master master = (Master) context.getBean("master"); master.getPet().shout(); 
}

實例工廠注入

實例工廠和靜態工廠的區別在於實例工廠必須先初始化工廠對象,工廠中的方法沒有static
示例:

package pojo;

public class PetShop {
    public Pet getDog() {
        return new Dog();
    }

    public Pet getCat() {
        return new Cat();
    }

    public Pet getPet(String name) {
        if (name == "cat") {
            return new Cat();
        } else {
            return new Dog();
        }
    }
} 

配置文件

    <bean id="shopBean" class="pojo.PetShop"></bean>
    <bean id="petBean" factory-bean="shopBean" factory- method="getCat"></bean>
    <bean id="master" class="pojo.Master">
        <property name="pet" ref="petBean"/>
    </bean>
使用註解

使用註解可以批量生成bean,掃描註解修飾的類。需要引入頭文件。

xmlns:context="http://www.springframework.org/schema/context"
//不指定id,默認id是類名首字母小寫 
@Component("person")
public class Person { 
@Qualifier("cat")//注入其它bean 
private Pet pet; 
//getter/setter方法略 
}

@Component註解修飾一個類,這個類會創建成bean。@Component(“bean 的id”),如果不指定id,默認的id是類名首字母小寫。

@Qualifier註解用於注入其它bean(按bean的id注入)。

@Autowired註解是按類型注入,默認情況下它要求依賴對象必須存在,如 果允許null值,可以設置它的required屬性爲false。當有多個類型一樣的 bean存在時,會出現異常。

@Resource註解是java的註解,spring提供了支持。它可以實現按名稱注入 和按類型注入。

//@Resource(name = "cat")//按名稱注入 
@Resource(type = Cat.class)//按類型注入 
private Pet pet;

@Qualifier, @Autowired, @Resource可以用在屬性上,也可以放在setter方法 上。

其它註解:
@Controller 分層開發中用於修飾web層的類。

@Service 分層開發時用於修飾service層的類。

@Repository 分層開發時用於修飾dao層的類。

這三個註解都是對於@Component更具體的實現。

@PostConstruct 依賴注入成功後被調用。構造方法->@Autowire- >@PostConstruct

@PreDestroy 銷燬前執行。

Spring4之後新增一系列註解可以代替xml文件。

import org.springframework.context.annotation.*;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import pojo.Cat;
import pojo.Dog;
import pojo.Pet;

@Configuration//相當於代替一個xml文件 
@ComponentScan(basePackages = "service",
        excludeFilters =
        @ComponentScan.Filter(Repository.class),
        includeFilters = @ComponentScan.Filter(Service.class), useDefaultFilters = false)
public class AppConfig {
    @Bean//默認單例 
    public Pet cat() {
        return new Cat();
    }

    @Bean
    @Scope("prototype")//每次都獲得一個新的實例 
    public Dog dog() {
        return new Dog();
    }
} 

四、Bean的生命週期和作用域

單例Bean的生命週期

在這裏插入圖片描述
1、實例化一個Bean--也就是我們常說的new;

2、按照Spring上下文對實例化的Bean進行配置--也就是IOC注入;

3、如果這個Bean已經實現了BeanNameAware接口,會調用它實現的 setBeanName(String)方法,此處傳遞的就是Spring配置文件中Bean的 id值

4、如果這個Bean已經實現了BeanFactoryAware接口,會調用它實現的setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自身
(可以用這個方式來獲取其它Bean,只需在Spring配置文件中配置一 個普通的Bean就可以);

5、 如果這個Bean已經實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文
(同樣這個方式也可以實現步驟4的內容,但比4更好,因爲 ApplicationContext是BeanFactory的子接口,有更多的實現方法);

6、如果這個Bean關聯了BeanPostProcessor接口,將會調用postProcessBeforeInitialization(Object obj, String s)方法, BeanPostProcessor經常被用作是Bean內容的更改,並且由於這個是在 Bean初始化結束時調用那個的方法,也可以被應用於內存或緩存技術;

7、 如果Bean在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法。

8、如果這個Bean關聯了BeanPostProcessor接口,將會調用 postProcessA

9、當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean 這個接口,會調用那個其實現的destroy()方法;

10、最後,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法。

 

Bean的作用域

當通過Spring容器創建一個Bean實例時,不僅可以完成Bean實例的實例化,還可以爲Bean指定特定的作用域。Spring支持如下5種作用域:
singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的Bean將只有一個實例
prototype:原型模式,每次通過容器的getBean方法獲取prototype定義的 Bean時,都將產生一個新的Bean實例。Bean完全交給客戶端管理,容器不在跟蹤生命週期。
request:對於每次HTTP請求,使用request定義的Bean都將產生一個新實例,即每次HTTP請求將會產生不同的Bean實例。只有在Web應用中使用 Spring時,該作用域纔有效
session:對於每次HTTP Session,使用session定義的Bean都將產生一個新實例。同樣只有在Web應用中使用Spring時,該作用域纔有效
application:web環境中,一個ServletContext生命週期中的單例對象。
websocket:WebSocket環境中使用。

其中比較常用的是singleton和prototype兩種作用域。對於singleton作用域的Bean,每次請求該Bean都將獲得相同的實例。容器負責跟蹤Bean實例的狀態,負責維護Bean實例的生命週期行爲;如果一個Bean被設置成 prototype作用域,程序每次請求該id的Bean,Spring都會新建一個Bean實 例,然後返回給程序。在這種情況下,Spring容器僅僅使用new關鍵字創建Bean實例,一旦創建成功,容器不在跟蹤實例,也不會維護Bean實例的狀態。

如果不指定Bean的作用域,Spring默認使用singleton作用域。Java在創建Java實例時,需要進行內存申請;銷燬實例時,需要完成垃圾回收,這些工作都會導致系統開銷的增加。因此,prototype作用域Bean的創建、銷燬代價比較大。而singleton作用域的Bean實例一旦創建成功,可以重複使用。因此,除非必要,否則儘量避免將Bean被設置成prototype作用域。

五、面向切面AOP

核心概念
a)、Aspect 切面,一個模塊化的關注點,它橫跨多個類。事務管理是企業 Java應用程序中橫切關注點的一個很好的例子。在Spring AOP中,切面通過使用常規類(基於模式的方法)或通過@Aspect註解修飾的類來實現。

b)、Join point 連接點。程序執行過程中的一個點,如在方法上進行統一的異 常處理。SpringAOP中,某個方法執行前、執行後、拋出異常後等一些具有 邊界性質的特定點,稱作連接點。

c)、Advice 增強。在一個特定切入點上採取的動作。

d)、Pointcut 切點。某個特定的連接點就是切點。

e)、Introduction 引介。引介是一種特殊的增強,它爲類添加一些屬性和方法. 這樣,即使一個業務類原本沒有實現某一個接口,通過AOP的引介功能,也可以 動態地爲該業務類添加接口的實現邏輯.讓業務類成爲這個接口的實現類。

f)、Target object 目標對象。增強邏輯的織入目標類。如果沒有AOP,那麼目標 業務類需要自己實現所有邏輯,如果使用AOP可以把一些非邏輯性代碼通過 AOP織入到主程序代碼上。由於通過使用運行時代理來實現Spring AOP,所 以該對象始終是代理對象。

g)、AOP proxy AOP框架爲了實現切面增強而創建的對象。在Spring框架中, AOP代理是JDK動態代理或CGLIB代理。

h)、Weaving 織入。織入是將增強添加到目標類具體鏈接點上的過程

Spring中的增強有五種:

1、前置增強(Before): 在方法執行前調用。
2、後置增強(AfterReturning): 在方法執行後調用(方法正常結束,沒有異
常退出)。
3、異常拋出增強(AfterThrowing): 在方法拋出異常時調用。。
4、環繞增強(Around): 在方法執行前後都調用。。
5、最終增強(After): 在方法執行後執行(不管是正常退出還是異常退出)

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