Spring - 基礎筆記

配置環境

安裝Eclipse插件

springsource-tool-suite-3.4.0.RELEASE-e4.3.1-updatesite.zip
只選擇4個Spring IDE即可。

導入jar包

這裏寫圖片描述

HelloWorld

HelloWorld.java

public class HelloWorld {
    private String name;

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

    public void hello(){
        System.out.println("hello:" + name);
    }
}

applicationContext.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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="helloWorld" class="com.atguigu.spring.beans.HelloWorld">
        <property name="name" value="Spring"></property>
    </bean>
</beans>

Main.java

public static void main(String[] args) {
    //1. 創建Spring的IOC容器
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    //2. 從容器中獲取Bean
    HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
    //3. 調用方法
    helloWorld.hello();
}

配置bean

配置bean有兩種形式:

  • 基於XML文件的方式
  • 基於註解的方式

基於XML文件的方式

Bean的配置方式:

  • 通過全類名(反射)
  • 通過工廠方法(靜態工廠方法&實例工廠方法)
  • FactoryBean

在xml文件中通過bean節點來配置bean

<bean id="helloWorld" class="com.atguigu.spring.beans.HelloWorld">
</bean>

id:Bean的名稱

  • 在IOC容器中必須是唯一的
  • 若id沒有指定,Spring自動將全類名的首字母小寫作爲Bean的名字
  • id可以指定多個名字,名字之間可用逗號、分號或空格分隔

在 Spring IOC 容器讀取 Bean 配置創建 Bean 實例之前, 必須對它進行實例化. 只有在容器實例化後, 纔可以從 IOC 容器裏獲取 Bean 實例並使用。(我們在HelloWorld中創建Spring的IOC容器的時候,已經對HelloWorld.java進行了實例化,所以必須要先創建IOC容器的實例化對象才能獲取到Bean實例)

Spring提供了兩種類型的IOC容器實現

  • BeanFactory:IOC容器的基本實現,是 Spring 框架的基礎設施,面向 Spring 本身
  • ApplicationContext:提供了更多的高級特性,是BeanFactory的子接口。面向使用 Spring 框架的開發者,幾乎所有的應用場合都直接使用 ApplicationContext 而非底層的 BeanFactory。

無論使用何種方式,配置文件時相同

ApplicationContext

這裏寫圖片描述

ApplicationContext代表了IOC容器。
ApplicationContext的主要實現類:

  • ClassPathXmlApplicationContext:從類路徑下加載配置文件
  • FileSystemXmlApplicationContext:從文件系統中加載配置文件
  • WebApplicationContext:是專門爲 WEB 應用而準備的,它允許從相對於 WEB 根目錄的路徑中完成初始化工作

ConfigurableApplicationContext 擴展於 ApplicationContext,新增加兩個主要方法:refresh() 和 close(), 讓 ApplicationContext 具有啓動、刷新和關閉上下文的能力

ApplicationContext 在初始化上下文時就實例化所有單例的 Bean。

applicationContext.xml

<bean id="helloWorld" class="com.atguigu.spring.beans.HelloWorld">
    <property name="name" value="Spring"></property>
</bean>

Main.java

public static void main(String[] args) {
    //1. 創建Spring的IOC容器
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    //2. 從容器中獲取Bean,ApplicationContext提供了多種獲取bean的方法
    //利用類型返回IOC容器中的Bean,但要求IOC容器中必須只能有一個該類型的Bean,不然IOC容器不知道到底返回哪個Bean
    HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
    //3. 調用方法
    helloWorld.hello();
}

依賴注入

Spring支持三種依賴注入的方式

  • 屬性注入
  • 構造器注入
  • 工廠方法注入(很少使用,不推薦)

屬性注入

屬性注入即通過 setter 方法注入Bean 的屬性值或依賴的對象

屬性注入使用 <property> 元素, 使用 name 屬性指定 Bean 的屬性名稱,value 屬性或 <value> 子節點指定屬性值

屬性注入是實際應用中最常用的注入方式

<bean id="helloWorld" class="com.atguigu.spring.beans.HelloWorld">
    <property name="name" value="Spring"></property>
</bean>

構造器注入

通過構造方法注入Bean 的屬性值或依賴的對象,它保證了 Bean 實例在實例化後就可以使用。

構造器注入在 <constructor-arg> 元素裏聲明屬性, <constructor-arg> 中沒有 name 屬性

使用構造方法注入有兩種入參方式:

  • 按索引匹配入參
  • 按類型匹配入參

Car.java

public class Car {
    private String brand;
    private String corp;
    private double price;
    private int maxSpeed;

    public Car(String brand, String corp, double price) {
        super();
        this.brand = brand;
        this.corp = corp;
        this.price = price;
    }

    public Car(String brand, String corp, int maxSpeed) {
        super();
        this.brand = brand;
        this.corp = corp;
        this.maxSpeed = maxSpeed;
    }

    public String toString() {
        return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price
                + ", maxSpeed=" + maxSpeed + "]";
    }   
}

applicationContext.xml

<bean id="car" class="com.atguigu.spring.beans.Car">
    <constructor-arg value="Audi" index="0"></constructor-arg>
    <constructor-arg value="ShangHai" index="1"></constructor-arg>
    <constructor-arg value="30000" type="double"></constructor-arg>     
</bean>

<bean id="car2" class="com.atguigu.spring.beans.Car">
    <constructor-arg value="BaoMa" type="java.lang.String"></constructor-arg>
    <constructor-arg value="ShangHai" type="java.lang.String"></constructor-arg>
    <constructor-arg value="240" type="int"></constructor-arg>      
</bean>

Main.java

public class Main {
    public static void main(String[] args) {
        //1. 創建Spring的IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2. 從容器中獲取Bean
        //3. 調用方法
        Car car = (Car) ctx.getBean("car");
        System.out.println(car);

        Car car2 = (Car) ctx.getBean("car2");
        System.out.println(car2);
    }
}

字面值

字面值:可用字符串表示的值,可以通過 <value> 元素標籤或 value 屬性進行注入。

基本數據類型及其封裝類、String 等類型都可以採取字面值注入的方式

若字面值中包含特殊字符,可以使用

<bean id="car2" class="com.atguigu.spring.beans.Car">
    <constructor-arg value="BaoMa" type="java.lang.String"></constructor-arg>
    <constructor-arg type="java.lang.String">
        <value><![CDATA[<ShangHai>]]></value>
    </constructor-arg>
    <constructor-arg type="int">
        <value>250</value>
    </constructor-arg>      
</bean>

引用其他Bean

組成應用程序的 Bean 經常需要相互協作以完成應用程序的功能. 要使 Bean 能夠相互訪問, 就必須在 Bean 配置文件中指定對 Bean 的引用

在 Bean 的配置文件中, 可以通過 <ref> 元素或 ref 屬性爲 Bean 的屬性或構造器參數指定對 Bean 的引用.

也可以在屬性或構造器裏包含 Bean 的聲明, 這樣的 Bean 稱爲內部 Bean

Person.java

public class Person {
    private String name;
    private int age;

    private Car car;
    ... ...
}

applicationContext.xml

<bean id="car2" class="com.atguigu.spring.beans.Car">
    <constructor-arg value="BaoMa" type="java.lang.String"></constructor-arg>
    <constructor-arg value="ShangHai" type="java.lang.String"></constructor-arg>
    <constructor-arg type="int">
        <value>250</value>
    </constructor-arg>      
</bean>

<bean id="person" class="com.atguigu.spring.beans.Person">
    <property name="name" value="Tom"></property>
    <property name="age" value="24"></property>
    <!-- 可以使用property的ref屬性建立bean之間的引用關係,ref指向ioc容器中的其他的bean -->
    <property name="car" ref="car2"></property>
    <!--
        <property name="car">
            <ref bean="car2"/>
        </property>
    -->
</bean>

Main.java

Person person = ctx.getBean(Person.class);
System.out.println(person);

內部Bean

當 Bean 實例僅僅給一個特定的屬性使用時, 可以將其聲明爲內部 Bean. 內部 Bean 聲明直接包含在 <property> 或 <constructor-arg> 元素裏, 不需要設置任何 id 或 name 屬性

內部 Bean 不能使用在任何其他地方

applicationContext.xml

<bean id="person" class="com.atguigu.spring.beans.Person">
    <property name="name" value="Tom"></property>
    <property name="age" value="24"></property>
    <property name="car">
        <bean class="com.atguigu.spring.beans.Car">
            <constructor-arg value="Ford"></constructor-arg>
            <constructor-arg value="Changan"></constructor-arg>
            <constructor-arg value="200000" type="double"></constructor-arg>
        </bean>
    </property>
</bean>

null值和級聯屬性

null值

可以使用專用的 <null/> 元素標籤爲 Bean 的字符串或其它對象類型的屬性注入 null 值

<bean id="person" class="com.atguigu.spring.beans.Person">
    <property name="name" value="Tom"></property>
    <property name="age" value="24"></property>
    <property name="car"></null></property>
</bean>

其實只要不賦值,在person.java中默認也是null

級聯屬性

和 Struts、Hiberante 等框架一樣,Spring 支持級聯屬性的配置。

<bean id="car" class="com.atguigu.spring.beans.Car">
    <constructor-arg value="Audi" index="0"></constructor-arg>
    <constructor-arg value="ShangHai" index="1"></constructor-arg>
    <constructor-arg value="30000" type="double"></constructor-arg>     
</bean>

<bean id="person" class="com.atguigu.spring.beans.Person">
    <property name="name" value="Tom"></property>
    <property name="age" value="24"></property>
    <property name="car" ref="car"></property>
    <!-- car的maxSpeed屬性沒有賦值,可以使用級聯屬性爲其賦值,Car類中必須有setSpeed方法 -->
    <property name="car.maxSpeed" value="250"></property>
</bean>

集合屬性

在 Spring中可以通過一組內置的 xml 標籤(例如: <list>, <set> 或 <map>) 來配置集合屬性.

List集合

配置 java.util.List 類型的屬性, 需要指定 <list> 標籤, 在標籤裏包含一些元素. 這些標籤可以通過 <value> 指定簡單的常量值, 通過 <ref> 指定對其他 Bean 的引用. 通過<bean> 指定內置 Bean 定義. 通過 <null/> 指定空元素. 甚至可以內嵌其他集合.

給Person類添加List<Car>屬性
修改Spring配置文件

<bean id="person3" class="com.atguigu.spring.beans.collection.Person">
    <property name="name" value="Mike"></property>
    <property name="age" value="25"></property>
    <property name="cars" >
        <list>
            <ref bean="car"/>
            <ref bean="car2"/>
            <bean class="com.atguigu.spring.beans.Car">
                <constructor-arg value="Ford" type="java.lang.String"></constructor-arg>
                <constructor-arg value="Changan" type="java.lang.String"></constructor-arg>
                <constructor-arg type="int">
                    <value>270</value>
                </constructor-arg>
            </bean>
        </list>
    </property>
</bean> 

數組和Set集合

數組的定義和 List 一樣, 都使用 <list>

配置 java.util.Set 需要使用 <set> 標籤, 定義元素的方法與 List 一樣.

Map集合

Java.util.Map 通過 <map> 標籤定義, <map> 標籤裏可以使用多個 <entry> 作爲子標籤. 每個條目包含一個鍵和一個值.

必須在 <key> 標籤裏定義鍵

因爲鍵和值的類型沒有限制, 所以可以自由地爲它們指定 <value>, <ref>, <bean> 或 <null> 元素.

可以將 Map 的鍵和值作爲 <entry> 的屬性定義: 簡單常量使用 key 和 value 來定義; Bean 引用通過 key-ref 和 value-ref 屬性定義

新建NewPerson類,添加字段Map<String, Car> cars
修改配置文件

<bean class="com.atguigu.spring.beans.collection.NewPerson" id="newPerson">
    <property name="name" value="Rose"></property>
    <property name="age" value="30"></property>
    <property name="cars">
        <map>
            <entry key="AA" value-ref="car"></entry>
            <entry key="BB" value-ref="car2"></entry>
        </map>
    </property>
</bean>

Properties

使用 <props> 定義 java.util.Properties, 該標籤使用多個 <prop> 作爲子標籤. 每個 <prop> 標籤必須定義 key 屬性

DataSource.java

public class DataSource {
    private Properties properties;
    ··· ···
}

applicationContext.xml

<bean class="com.atguigu.spring.beans.collection.DataSource" id="dataSource">
    <property name="properties">
        <props>
            <prop key="user">root</prop>
            <prop key="password">123</prop>
            <prop key="jdbcUrl">jdbc:mysql:///test</prop>
            <prop key="driverClass">com.mysql.jdbc.Driver</prop>
        </props>
    </property>
</bean>

使用utility scheme定義集合

使用基本的集合標籤定義集合時, 不能將集合作爲獨立的 Bean 定義, 導致其他 Bean 無法引用該集合, 所以無法在不同 Bean 之間共享集合.

可以使用 util schema 裏的集合標籤定義獨立的集合 Bean. 需要注意的是, 必須在 <beans> 根元素裏添加 util schema 定義

點擊Namespaces添加util名稱空間

applicationContext.xml

<util:list id="cars">
    <ref bean="car"/>
    <ref bean="car2"/>
</util:list>

<bean class="com.atguigu.spring.beans.collection.Person" id="person4">
    <property name="name" value="Jack"></property>
    <property name="age" value="35"></property>
    <property name="cars" ref="cars"></property>
</bean>

使用p命名空間

爲了簡化 XML 文件的配置,越來越多的 XML 文件採用屬性而非子元素配置信息。

Spring 從 2.5 版本開始引入了一個新的 p 命名空間,可以通過 <bean> 元素屬性的方式配置 Bean 的屬性。

使用 p 命名空間後,基於 XML 的配置方式將進一步簡化

點擊Namespaces添加p命名空間

applicationContext.xml

<util:list id="cars">
    <ref bean="car"/>
    <ref bean="car2"/>
</util:list>

<bean class="com.atguigu.spring.beans.collection.Person" id="person5" p:name="Queen" p:age="40" p:cars-ref="cars"></bean>

自動裝配

Spring IOC 容器可以自動裝配 Bean. 需要做的僅僅是在 <bean> 的 autowire 屬性裏指定自動裝配的模式

byType(根據類型自動裝配): 若 IOC 容器中有多個與目標 Bean 類型一致的 Bean. 在這種情況下, Spring 將無法判定哪個 Bean 最合適該屬性, 所以不能執行自動裝配.

byName(根據名稱自動裝配): 必須將目標 Bean 的名稱和屬性名設置的完全相同.

constructor(通過構造器自動裝配): 當 Bean 中存在多個構造器時, 此種自動裝配方式將會很複雜. 不推薦使用

Address.java

public class Address {
    private String city;
    private String street;
    ... ...
}

Car.java

public class Car {
    private String brand;
    private double price;
    ... ...
}

Person.java

public class Person {
    private String name;
    private Address address;
    private Car car;
    ... ...
}

beans-autowire.xml

<bean class="com.atguigu.spring.beans.autowire.Address" id="address" p:city="ShiJiaZhuang" p:street="PingAnDaJie"></bean>

<bean class="com.atguigu.spring.beans.autowire.Car" id="car" p:brand="Audo" p:price="300000"></bean>

<bean class="com.atguigu.spring.beans.autowire.Person" id="person" p:name="Tom" autowire="byName"></bean>

bean之間的關係

bean之間有兩種關係:

  • 繼承
  • 依賴

繼承Bean配置

Spring 允許繼承 bean 的配置, 被繼承的 bean 稱爲父 bean. 繼承這個父 Bean 的 Bean 稱爲子 Bean
子 Bean 從父 Bean 中繼承配置, 包括 Bean 的屬性配置

子 Bean 也可以覆蓋從父 Bean 繼承過來的配置

父 Bean 可以作爲配置模板, 也可以作爲 Bean 實例. 若只想把父 Bean 作爲模板, 可以設置 的abstract 屬性爲 true, 這樣 Spring 將不會實例化這個 Bean

並不是 <bean> 元素裏的所有屬性都會被繼承. 比如: autowire, abstract 等.

也可以忽略父 Bean 的 class 屬性, 讓子 Bean 指定自己的類, 而共享相同的屬性配置. 但此時 abstract 必須設爲 true

beans-relation.xml

<bean class="com.atguigu.spring.beans.autowire.Address" id="address" p:city="ShiJiaZhuang" p:street="PingAnDaJie"></bean>
<bean class="com.atguigu.spring.beans.autowire.Address" id="address2" p:street="ZhongShanLu" parent="address"></bean>

Main.java

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-relation.xml");
    Address address = (Address) ctx.getBean("address");
    Address address2 = (Address) ctx.getBean("address2");
    System.out.println(address);
    System.out.println(address2);
}

依賴Bean配置

Spring 允許用戶通過 depends-on 屬性設定 Bean 前置依賴的Bean,前置依賴的 Bean 會在本 Bean 實例化之前創建好

如果前置依賴於多個 Bean,則可以通過逗號,空格或的方式配置 Bean 的名稱
beans-relation.xml

<bean class="com.atguigu.spring.beans.autowire.Car" id="car" p:brand="Audo" p:price="250000"></bean>
<!-- 要求在配置Person時,必須有一個關聯的car,換句話說,person這個bean依賴於Car這個bean -->
<bean class="com.atguigu.spring.beans.autowire.Person" id="person" p:address-ref="address" p:name="Tom" depends-on="car"></bean>

Bean的作用域

在 Spring 中, 可以在 元素的 scope 屬性裏設置 Bean 的作用域.

默認情況下, Spring 只在初始化時爲每個在 IOC 容器裏聲明的 Bean 創建唯一一個實例,單例的,整個 IOC 容器範圍內都能共享該實例:所有後續的 getBean() 調用和 Bean 引用都將返回這個唯一的 Bean 實例.該作用域被稱爲 singleton, 它是所有 Bean 的默認作用域.

這裏寫圖片描述

使用外部屬性文件

在配置文件裏配置 Bean 時, 有時需要在 Bean 的配置裏混入系統部署的細節信息(例如: 文件路徑, 數據源配置信息等). 而這些部署細節實際上需要和 Bean 配置相分離,否則修改的時候需要在大量的bean配置文件中進行查找,過於麻煩。

Spring 提供了一個 PropertyPlaceholderConfigurer 的 BeanFactory 後置處理器, 這個處理器允許用戶將 Bean 配置的部分內容外移到屬性文件中. 可以在 Bean 配置文件裏使用形式爲 ${var} 的變量, PropertyPlaceholderConfigurer 從屬性文件里加載屬性, 並使用這些屬性來替換變量.

Spring 還允許在屬性文件中使用 ${propName},以實現屬性之間的相互引用。

Spring2.5之後,可通過引入context命名空間來註冊 PropertyPlaceholderConfigurer

  • <beans> 中添加 context Schema 定義
  • 在配置文件中加入如下配置:<context:property-placeholder location=”classpath:db.properties”/>

db.properties

user=root
password=1230
driverclass=com.mysql.jdbc.Driver
jdbcurl=jdbc:mysql:///test

beans-properties.xml

<context:property-placeholder location="classpath:db.properties"/>
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
    <property name="user" value="${user}"></property>
    <property name="password" value="${password}"></property>
    <property name="driverClass" value="${driverclass}"></property>
    <property name="jdbcUrl" value="${jdbcurl}"></property>
</bean>

Main.java

public static void main(String[] args) throws SQLException {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-properties.xml");
    DataSource dataSource = (DataSource) ctx.getBean("dataSource");

    System.out.println(dataSource.getConnection());
}

SpEL

Spring 表達式語言(簡稱SpEL):是一個支持運行時查詢和操作對象圖的強大的表達式語言。

語法類似於 EL:SpEL 使用 #{…} 作爲定界符,所有在大框號中的字符都將被認爲是 SpEL

SpEL 爲 bean 的屬性進行動態賦值提供了便利

Bean的生命週期

Spring IOC 容器可以管理 Bean 的生命週期, Spring 允許在 Bean 生命週期的特定點執行定製的任務.

Spring IOC 容器對 Bean 的生命週期進行管理的過程:

  • 通過構造器或工廠方法創建 Bean 實例
  • 爲 Bean 的屬性設置值和對其他 Bean 的引用
  • 調用 Bean 的初始化方法
  • Bean 可以使用了
  • 當容器關閉時, 調用 Bean 的銷燬方法

在 Bean 的聲明裏設置 init-method 和 destroy-method 屬性, 爲 Bean 指定初始化和銷燬方法.

Car.java

public class Car {
    private String brand;

    public void init(){
        System.out.println("init...");
    }

    public void destory(){
        System.out.println("destory...");
    }

    ... ...
}

beans-cycle.xml

<bean class="com.atguigu.spring.beans.cycle.Car" id="car" init-method="init" destroy-method="destory">
    <property name="brand" value="Audi"></property>
</bean>

Main.java

public static void main(String[] args) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");
    Car car = (Car) ctx.getBean("car");
    System.out.println(car);
    ctx.close();
}

Bean後置處理器

Bean 後置處理器允許在調用初始化方法前後對 Bean 進行額外的處理.

Bean 後置處理器對 IOC 容器裏的所有 Bean 實例逐一處理, 而非單一實例. 其典型應用是: 檢查 Bean 屬性的正確性或根據特定的標準更改 Bean 的屬性.

對Bean 後置處理器而言, 需要實現 org.springframework.beans.factory.config.BeanPostProcessor 接口. 在初始化方法被調用前後, Spring 將把每個 Bean 實例分別傳遞給上述接口的以下兩個方法:

這裏寫圖片描述

Spring IOC 容器對 Bean 的生命週期進行管理的過程:

  • 通過構造器或工廠方法創建 Bean 實例
  • 爲 Bean 的屬性設置值和對其他 Bean 的引用
  • 將 Bean 實例傳遞給 Bean 後置處理器的 postProcessBeforeInitialization 方法
  • 調用 Bean 的初始化方法
  • 將 Bean 實例傳遞給 Bean 後置處理器的 postProcessAfterInitialization方法
  • Bean 可以使用了
  • 當容器關閉時, 調用 Bean 的銷燬方法

bean-cycle.xml

<bean class="com.atguigu.spring.beans.cycle.Car" id="car" init-method="init" destroy-method="destory">
    <property name="brand" value="Audi"></property>
</bean>
<!-- 配置bean的後置處理器,不需要配置id,IOC容器自動識別是一個BeanPostProcessor -->
<bean class="com.atguigu.spring.beans.cycle.MyBeanPostProcessor"></bean>

MyBeanPostProcessor.java

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        System.out.println("postProcessAfterInitialization: " + bean + ", " + beanName);
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        System.out.println("postProcessBeforeInitialization: " + bean + ", " + beanName);
        //bean:bean實例本身
        //beanName:IOC容器配置的bean的名字
        //返回值:是實際上返回給用戶的那個Bean,注意:可以在以上兩個方法中修改返回的bean,甚至返回一個新的bean
        Car car = new Car("Ford");
        return car;
    }
}

通過工廠方法配置Bean

通過調用靜態工廠方法創建Bean

調用靜態工廠方法創建 Bean是將對象創建的過程封裝到靜態方法中. 當客戶端需要對象時, 只需要簡單地調用靜態方法, 而不同關心創建對象的細節.

要聲明通過靜態方法創建的 Bean, 需要在 Bean 的 class 屬性裏指定擁有該工廠的方法的類, 同時在 factory-method 屬性裏指定工廠方法的名稱. 最後, 使用 <constrctor-arg> 元素爲該方法傳遞方法參數

Car.java

public class Car {
    private String brand;
    private double price;
    ... ...
}

StaticCarFactory.java

public class StaticCarFactory {
    private static Map<String, Car> cars = new HashMap<String, Car>();

    static{
        cars.put("audi", new Car("audi", 300000));
        cars.put("ford", new Car("ford", 400000));
    }

    public static Car getCar(String name){
        return cars.get(name);
    }
}

beans-factory.xml

<!-- 通過靜態工廠方法配置Bean,注意不是配置靜態工廠方法實例,而是配置bean的實例 -->
<!-- 
    class屬性:指向靜態工廠方法的全類名
    factory-method:指向靜態工廠方法的名字
    constructor-arg:如果工廠方法需要傳入參數,則使用constructor-arg來配置參數
 -->
<bean class="com.atguigu.spring.beans.factory.StaticCarFactory" id="car" factory-method="getCar">
    <constructor-arg value="audi"></constructor-arg>
</bean>

Main.java

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");
    Car car = (Car) ctx.getBean("car");
    System.out.println(car);
}

通過調用實例工廠方法創建Bean

實例工廠方法: 將對象的創建過程封裝到另外一個對象實例的方法裏. 當客戶端需要請求對象時, 只需要簡單的調用該實例方法而不需要關心對象的創建細節.

要聲明通過實例工廠方法創建的 Bean

  • 在 bean 的 factory-bean 屬性裏指定擁有該工廠方法的 Bean
  • 在 factory-method 屬性裏指定該工廠方法的名稱
  • 使用 construtor-arg 元素爲工廠方法傳遞方法參數

InstanceCarFactory.java

public class InstanceCarFactory {
    private Map<String, Car> cars = null;
    public InstanceCarFactory() {
        cars = new HashMap<String, Car>();
        cars.put("audi", new Car("audi", 300000));
        cars.put("ford", new Car("ford", 400000));
    }

    public Car getCar(String brand) {
        return cars.get(brand);
    }
}   

beans-factory.xml

<!-- 配置工廠的實例 -->
<!-- 
    class屬性:指向實例工廠方法的全類名
    factory-method:指向實例工廠方法的名字
    constructor-arg:如果工廠方法需要傳入參數,則使用constructor-arg來配置參數
 -->
<bean class="com.atguigu.spring.beans.factory.InstanceCarFactory" id="instanceCarFactory"></bean>
<!-- 通過實例工廠方法來配置bean -->
<bean id="car2" factory-bean="instanceCarFactory" factory-method="getCar">
    <constructor-arg value="ford"></constructor-arg>
</bean>

Main.java

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");

        Car car2 = (Car) ctx.getBean("car2");
        System.out.println(car2);
    }
}

通過FactoryBean配置Bean

Spring 中有兩種類型的 Bean, 一種是普通Bean, 另一種是工廠Bean, 即FactoryBean.

工廠 Bean 跟普通Bean不同, 其返回的對象不是指定類的一個實例, 其返回的是該工廠 Bean 的 getObject 方法所返回的對象

Car.java

public class Car {
    private String brand;
    private double price;
    ... ...
}

CarFactoryBean.java

public class CarFactoryBean implements FactoryBean<Car> {

    private String brand;

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Car getObject() throws Exception {
        return new Car(brand, 500000);
    }

    public Class<?> getObjectType() {
        return Car.class;
    }

    public boolean isSingleton() {
        return true;
    }
}

beans-factorybean.xml

<!-- 
    通過FactoryBean來配置Bean的實例
    class:指向FactoryBean的全類名
    property:配置FactoryBean的屬性

    但實際返回的實例確實FactoryBean的getObject()方法返回的實例
 -->
 <bean class="com.atguigu.spring.beans.factorybean.CarFactoryBean" id="car">
    <property name="brand" value="BMW"></property>
 </bean>

Main.java

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factorybean.xml");
        Car car = (Car) ctx.getBean("car");
        System.out.println(car);
    }
}

基於註解的方式

組件掃描(component scanning): Spring 能夠從 classpath 下自動掃描, 偵測和實例化具有特定註解的組件.

特定組件包括:

  • @Component: 基本註解, 標識了一個受 Spring 管理的組件
  • @Respository: 標識持久層組件
  • @Service: 標識服務層(業務層)組件
  • @Controller: 標識表現層組件

對於掃描到的組件, Spring 有默認的命名策略: 使用非限定類名, 第一個字母小寫. 也可以在註解中通過 value 屬性值標識組件的名稱

當在組件類上使用了特定的註解之後, 還需要在 Spring 的配置文件中聲明 <context:component-scan> :

  • base-package 屬性指定一個需要掃描的基類包,Spring 容器將會掃描這個基類包裏及其子包中的所有類.
  • 當需要掃描多個包時, 可以使用逗號分隔.
  • 如果僅希望掃描特定的類而非基包下的所有類,可使用 resource-pattern 屬性過濾特定的類
  • <context:include-filter> 子節點表示要包含的目標類
  • <context:exclude-filter> 子節點表示要排除在外的目標類
  • <context:component-scan> 下可以擁有若干個 <context:include-filter> 和 <context:exclude-filter> 子節點
  • <context:include-filter> 和 <context:exclude-filter> 子節點支持多種類型的過濾表達式:
    這裏寫圖片描述

UsreRepository.java

package com.atguigu.spring.beans.annotation.repository;

public interface UserRepository {
    void save();
}

UserRepositoryImpl.java

package com.atguigu.spring.beans.annotation.repository;
import org.springframework.stereotype.Repository;

@Repository(value="userRepository")
public class UserRepositoryImpl implements UserRepository {
    @Override
    public void save() {
        System.out.println("UserRepositoryImpl's save...");
    }
}

UserService.java

package com.atguigu.spring.beans.annotation.service;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void add() {
        System.out.println("UserService add...");
    }
}

UserController.java

package com.atguigu.spring.beans.annotation.controller;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    public void execute(){
        System.out.println("UserController execute...");
    }
}

TestObject.java

package com.atguigu.spring.beans.annotation;
import org.springframework.stereotype.Component;

@Component
public class TestObject {
}

beans-annotation.xml

    <!-- 
    <context:component-scan base-package="com.atguigu.spring.beans.annotation">
    </context:component-scan>
     -->

    <!-- 添加resource-pattern屬性表示只掃描該路徑下的類 -->
    <!-- 
    <context:component-scan base-package="com.atguigu.spring.beans.annotation"
        resource-pattern="controller/*.class"
    ></context:component-scan>
    -->
    <!-- context:exclude-filter子節點指定排除哪些指定表達式的組件 -->
    <!-- 
    <context:component-scan base-package="com.atguigu.spring.beans.annotation">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
     -->
    <!-- context:include-filter子節點指定包含哪些指定表達式的組件,該組件需配合use-default-filters使用 -->
    <!-- 
    <context:component-scan base-package="com.atguigu.spring.beans.annotation" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
     -->

    <!-- 過濾使用assignable類型 --> 
    <context:component-scan base-package="com.atguigu.spring.beans.annotation" use-default-filters="false">
        <context:include-filter type="assignable" expression="com.atguigu.spring.beans.annotation.repository.UserRepository"/>
    </context:component-scan>

Main.java

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");

        TestObject to = (TestObject) ctx.getBean("testObject");
        System.out.println(to);

        UserRepository userRepository = (UserRepository) ctx.getBean("userRepository");
        System.out.println(userRepository);

        UserService userService = (UserService) ctx.getBean("userService");
        System.out.println(userService);

        UserController userController = (UserController) ctx.getBean("userController");
        System.out.println(userController);
    }
}

組件裝配

當Bean之間存在有關聯關係的時候,我們就可以使用組件的自動裝配

<context:component-scan> 元素會自動註冊 AutowiredAnnotationBeanPostProcessor 實例, 該實例可以自動裝配具有 @Autowired 和 @Resource 、@Inject註解的屬性.

使用 @Autowired 自動裝配 Bean

@Autowired 註解自動裝配具有兼容類型的單個 Bean屬性
- 構造器, 普通字段(即使是非 public), 一切具有參數的方法都可以應用@Authwired 註解

  • 默認情況下, 所有使用 @Authwired 註解的屬性都需要被設置. 當 Spring 找不到匹配的 Bean 裝配屬性時, 會拋出異常, 若某一屬性允許不被設置, 可以設置 @Authwired 註解的 required 屬性爲 false

  • 默認情況下, 當 IOC 容器裏存在多個類型兼容的 Bean 時, 通過類型的自動裝配將無法工作. 此時可以在 @Qualifier 註解裏提供 Bean 的名稱. Spring 允許對方法的入參標註 @Qualifiter 已指定注入 Bean 的名稱

  • @Authwired 註解也可以應用在數組類型的屬性上, 此時 Spring 將會把所有匹配的 Bean 進行自動裝配.

  • @Authwired 註解也可以應用在集合屬性上, 此時 Spring 讀取該集合的類型信息, 然後自動裝配所有與之兼容的 Bean.

  • @Authwired 註解用在 java.util.Map 上時, 若該 Map 的鍵值爲 String, 那麼 Spring 將自動裝配與之 Map 值類型兼容的 Bean, 此時 Bean 的名稱作爲鍵值

UserRepository.java

public interface UserRepository {
    void save();
}

UserRepositoryImpl.java

//@Repository(value="userRepository")
@Repository
public class UserRepositoryImpl implements UserRepository {

    @Autowired(required=false)
    private TestObject testObject;

    public void save() {
        System.out.println("UserRepositoryImpl's save...");
        System.out.println(testObject);
    }

}

UserJdbcRepository.java

@Repository
public class UserJdbcRepository implements UserRepository{

    public void save() {
        System.out.println("UserJdbcRepository save...");
    }
}

UserService.java

@Service
public class UserService {
    //@Autowired
    //@Qualifier("userRepositoryImpl")
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(@Qualifier("userRepositoryImpl") UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void add() {
        System.out.println("UserService add...");
        userRepository.save();
    }
}

UserController.java

@Controller
public class UserController {
    @Autowired
    private UserService userService;

    public void execute(){
        System.out.println("UserController execute...");
        userService.add();
    }
}

TestObject.java

//@Component
public class TestObject {

}

Main.java

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");
        UserController userController = (UserController) ctx.getBean("userController");

        System.out.println(userController);
        userController.execute();
    }
}

注意:
1.當bean之間存在關聯關係的時候,只要在屬性private UserService userService;前加@Autowired註解就可以自動裝配;如果存在set方法,在set方法前加上註解也可以自動裝配。
2.如果IOC容器中bean不存在,即如果調用了ObjectTest並且加上了自動裝配,但ObjectTest前並沒有加@Component註釋,就會報錯
3.如果IOC容器中找不到bean,但是註釋自動裝配的時候添加了屬性required=false,name結果是null,但不會報錯
4.如果IOC有好幾個類型匹配的bean,既有UserRespositoryImpl也有UserJdbcRespository,那麼會先匹配名字

  • 如果註釋的時候添加了屬性@Repository(value=”userRepository”),那麼會匹配它;
  • 如果沒有添加屬性,只有一個註釋@Repository,那麼會報錯;
  • 或者在自動裝配的時候添加註釋的時候提供bean的名稱@Qualifier(“userRepositoryImpl”),或者在入參的時候進行指定

泛型依賴注入

Spring 4.x 中可以爲子類注入子類對應的泛型類型的成員變量的引用
這裏寫圖片描述

BaseRepository.java

public class BaseRepository<T> {
}

BaseService.java

public class BaseService<T> {

    @Autowired
    protected BaseRepository<T> repository;

    public void add(){
        System.out.println("add...");
        System.out.println(repository);
    }
}

UserRepository.java

@Repository
public class UserRepository extends BaseRepository<User> {
}

UserService.java

@Service
public class UserService extends BaseService<User> {
}

beans-generic.xml

<context:component-scan base-package="com.atguigu.spring.beans.generic.di"></context:component-scan>

Main.java

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-generic.xml");
        UserService userService = (UserService) ctx.getBean("userService");
        userService.add();
    }

輸出結果:

add...
com.atguigu.spring.beans.generic.di.UserRepository@6632060c

當BaseService中有BaseRepository的引用變量,並且這個引用變量被@Autowired標識,
UserService類中添加註解@Service,交給IOC容器來管理
UserRepository類中註解@Repository,交給IOC容器來管理
這樣當我們在Main中使用UserService中的方法時,會自動注入泛型

AOP

AOP基礎

AOP(Aspect-Oriented Programming, 面向切面編程): 是一種新的方法論, 是對傳統 OOP(Object-Oriented Programming, 面向對象編程) 的補充.

AOP 的主要編程對象是切面(aspect), 而切面模塊化橫切關注點.

在應用 AOP 編程時, 仍然需要定義公共功能, 但可以明確的定義這個功能在哪裏, 以什麼方式應用, 並且不必修改受影響的類. 這樣一來橫切關注點就被模塊化到特殊的對象(切面)裏.

AOP 的好處:

  • 每個事物邏輯位於一個位置, 代碼不分散, 便於維護和升級
  • 業務模塊更簡潔, 只包含核心業務代碼.

AOP術語

切面(Aspect): 橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象

通知(Advice): 切面必須要完成的工作

目標(Target): 被通知的對象

代理(Proxy): 向目標對象應用通知之後創建的對象

連接點(Joinpoint):程序執行的某個特定位置:如類某個方法調用前、調用後、方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點;相對點表示的方位。例如 ArithmethicCalculator#add() 方法執行前的連接點,執行點爲 ArithmethicCalculator#add(); 方位爲該方法執行前的位置

切點(pointcut):每個類都擁有多個連接點:例如 ArithmethicCalculator 的所有方法實際上都是連接點,即連接點是程序類中客觀存在的事務。AOP 通過切點定位到特定的連接點。類比:連接點相當於數據庫中的記錄,切點相當於查詢條件。切點和連接點不是一對一的關係,一個切點匹配多個連接點,切點通過 org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作爲連接點的查詢條件。

註解通知

前置通知

1.加入jar包
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar

commons-logging-1.1.1.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar

2.在配置文件中加入aop的命名空間

3.基於註解的方式:
①在配置文件中加入如下配置:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
②把橫切關注點的代碼抽象到切面的類中
i. 切面首先是一個IOC中的bean,即加入@Component註解
ii.切面還需要加入@Aspect註解
③在類中聲明各種通知
AspectJ 支持 5 種類型的通知註解:

  • @Before: 前置通知, 在方法執行之前執行
  • @After: 後置通知, 在方法執行之後執行
  • @AfterRunning: 返回通知, 在方法返回結果之後執行
  • @AfterThrowing: 異常通知, 在方法拋出異常之後
  • @Around: 環繞通知, 圍繞着方法執行

i.聲明一個方法
ii.在方法前加入@Before註釋
④可以在通知方法中聲明一個類型爲JoinPoint的參數,然後就能訪問鏈接細節,如方法名稱和參數值

ArithmeticCaculator.java

public interface ArithmeticCalculator {

    int add(int i, int j);
    int sub(int i, int j);

    int mul(int i, int j);
    int div(int i, int j);
}

ArithmeticCalculatorImpl.java

@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}

LoggingAspect.java

//把這個類聲明爲一個切面:需要把該類放入到IOC容器中,再聲明爲一個切面
@Aspect
@Component
public class LoggingAspect {

    //聲明該方法是一個前置通知: 在目標方法開始之前執行
    //* 代表匹配任意修飾符及任意返回值, 參數列表中的 .. 匹配任意數量的參數

    //@Before("execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.add(int, int))")
    @Before("execution(* com.atguigu.spring.aop.impl.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("The method " + methodName + " begins with " + args);
    }
}

applicationContext.xml

<context:component-scan base-package="com.atguigu.spring.aop.impl"></context:component-scan>

<!-- 使AspjectJ註釋起作用:自動爲匹配的類生成代理對象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

Main.java

public class Main {

    public static void main(String[] args) {
        //1. 創建 Spring 的IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2. 從IOC容器中獲取 bean 的實例
        ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class); 
        //3. 使用bean
        int result = arithmeticCalculator.add(1, 3);
        System.out.println("result: " + result);

        result = arithmeticCalculator.div(6, 2);
        System.out.println("result: " + result);
    }
}

後置通知

LoggingAspect.java

@Aspect
@Component
public class LoggingAspect {

    //後置通知:在目標方法執行後(無論是否發生異常),執行的通知
    //在後置通知中還不能訪問目標方法執行的結果
    @After("execution(* com.atguigu.spring.aop.impl.*.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }
}

返回通知

LoggingAspect.java

@Aspect
@Component
public class LoggingAspect {

    /**
     * 返回通知
     * 在方法正常結束後執行的代碼
     * 返回通知是可以訪問到方法的返回值的
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value="execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(..))", returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends with " + result);
    }
}

異常通知

LoggingAspect.java

@Aspect
@Component
public class LoggingAspect {

    /**
     * 異常通知
     * 在目標方法出現異常時會執行的代碼
     * 可以訪問到異常對象,且可以指定在出現特定異常時再執行通知代碼
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value="execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(..))", throwing="e")
    public void afterThrowing(JoinPoint joinPoint, Exception e){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs excetion " + e);
    }
}

環繞通知

LoggingAspect.java

@Aspect
@Component
public class LoggingAspect {

    /**
     * 環繞通知需要攜帶ProceedingJoinPoint類型的參數
     * 環繞通知類似於動態代理的全過程:ProceedingJpinPoint 類型的參數可以決定是否執行目標方法
     * 且環繞通知必須有返回值,返回值即爲目標方法的返回值
     */

    @Around("execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(..))")
    public Object arroundMethod(ProceedingJoinPoint pjd){

        Object result = null;
        String methodName = pjd.getSignature().getName();
        try {
            //前置通知
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
            //執行目標方法
            result = pjd.proceed();
            //返回通知
            System.out.println("The method ends with " + result);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            //異常通知
            System.out.println("The method " + methodName + " occurs exception:" + e);
            throw new RuntimeException(e);
        }
        //後置通知
        System.out.println("The method " + methodName + " ends");
        return result;
    }
}

切面優先級

如果有兩個或多個切面,可以通過在類上添加Order註解來表示優先級,即切面的先後順序
VlidationAspect.java

//可以使用@Order註解指定切面的優先級,值越小優先級越高
@Order(1)
@Aspect
@Component
public class VlidationAspect {
    @Before(value="execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        System.out.println("validate:" + Arrays.asList(joinPoint.getArgs()));
    }
}

重用切點表達式

LoggingAspect.java

@Order(2)
@Aspect
@Component
public class LoggingAspect {

    /**
     * 定義一個方法,用於聲明切入點表達式,一般的,該方法中再不需要填入其他的代碼
     * 使用@Pointcut來聲明切入點表達式
     */
    @Pointcut(value="execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(..))")
    public void declareJointPointExpression(){}

    //聲明該方法是一個前置通知: 在目標方法開始之前執行
    //* 代表匹配任意修飾符及任意返回值, 參數列表中的 .. 匹配任意數量的參數

    //@Before("execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.add(int, int))")
    @Before("declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("The method " + methodName + " begins with " + args);
    }

    //後置通知:在目標方法執行後(無論是否發生異常),執行的通知
    //在後置通知中還不能訪問目標方法執行的結果
    @After("declareJointPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }
}

如果在其他類中使用添加類名,如果在其他包中使用添加包名和類名

VlidationAspect.java

@Order(1)
@Aspect
@Component
public class VlidationAspect {
    @Before(value="com.atguigu.spring.aop.impl.LoggingAspect.declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint){
        System.out.println("validate:" + Arrays.asList(joinPoint.getArgs()));
    }
}

基於配置文件的方式配置AOP

除了使用 AspectJ 註解聲明切面, Spring 也支持在 Bean 配置文件中聲明切面. 這種聲明是通過 aop schema 中的 XML 元素完成的.

正常情況下, 基於註解的聲明要優先於基於 XML 的聲明. 通過 AspectJ 註解, 切面可以與 AspectJ 兼容, 而基於 XML 的配置則是 Spring 專有的. 由於 AspectJ 得到越來越多的 AOP 框架支持, 所以以註解風格編寫的切面將會有更多重用的機會.

當使用 XML 聲明切面時, 需要在 <beans> 根元素中導入 aop Schema
applicationContext-xml.xml

<!-- 配置bean -->
<bean class="com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl" id="arithmeticCalculator"></bean>

<!-- 配置切面的bean -->
<bean class="com.atguigu.spring.aop.impl.LoggingAspect" id="loggingAspect"></bean>
<bean class="com.atguigu.spring.aop.impl.VlidationAspect" id="vlidationAspect"></bean>

<!-- 配置AOP -->
<aop:config>
    <!-- 配置切點表達式 -->
    <aop:pointcut expression="execution(* com.atguigu.spring.aop.impl.ArithmeticCalculator.*(..))" id="pointcut"/>
    <!-- 配置切面及通知 -->
    <aop:aspect ref="loggingAspect" order="2">
        <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
        <aop:after method="afterMethod" pointcut-ref="pointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
        <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
        <!-- 
        <aop:around method="arroundMethod" pointcut-ref="pointcut"/>
         -->
    </aop:aspect>
    <aop:aspect ref="vlidationAspect" order="1">
        <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章