簡單談談Spring框架【1】

1、Spring定義

1.1 百度摘要

Spring是一個開放源代碼的設計層面框架,他解決的是業務邏輯層和其他各層的鬆耦合問題,因此它將面向接口的編程思想貫穿整個系統應用。Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson創建。簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。(由Rod Johnson創建的一個開源框架)

1.2 Spring的一些優點

  • 方便解耦,簡化開發
  • 核心IOC與DI的思想
  • AOP編程的支持
  • 聲明式的事物支持
  • 包容性,能夠集成各種優秀的框架
  • 降低JavaEE API的使用難度
  • 方便程序的測試
  • Spring屬於低侵入,代碼污染極低

1.3 Spring的各個包

Spring的模塊圖

Spring項目的各模塊
附帶:【Spring的各個包的作用】
Spring AOP:Spring的面向切面編程,提供AOP(面向切面編程)的實現
Spring Aspects:Spring提供的對AspectJ框架的整合
Spring Beans:Spring IOC的基礎實現,包含訪問配置文件、創建和管理bean等。
Spring Context:在基礎IOC功能上提供擴展服務,此外還提供許多企業級服務的支持,有郵件服務、任務調度、JNDI定位,EJB集成、遠程訪問、緩存以及多種視圖層框架的支持。
spring-context-indexer:
Spring Context Support:Spring context的擴展支持,用於MVC方面。
Spring Core:Spring的核心工具包
Spring expression:Spring表達式語言
spring-framework-bom:統一管理jar包版本
Spring Instrument:Spring對服務器的代理接口
Spring Instrument Tomcat:Spring對tomcat連接池的集成
Spring jcl : JCL採用了設計模式中的“適配器模式”,它對外提供統一的接口,然後在適配類中將對日誌的操作委託給具體的日誌框架,比如Log4J,Java Logging API等
Spring JDBC:對JDBC 的簡單封裝
Spring JMS:爲簡化jms api的使用而做的簡單封裝
Spring Messaging:集成messaging api和消息協議提供支持。
Spring orm:整合第三方的orm實現,如hibernate,ibatis,jdo以及spring 的jpa實現
Spring oxm:Spring對於object/xml映射的支持,可以讓JAVA與XML之間來回切換
Spring test:對JUNIT等測試框架的簡單封裝
Spring tx:爲JDBC、Hibernate、JDO、JPA等提供的一致的聲明式和編程式事務管理。
Spring web:包含Web應用開發時,用到Spring框架時所需的核心類,包括自動載入WebApplicationContext特性的類、Struts與JSF集成類、文件上傳的支持類、Filter類和大量工具輔助類。
Spring webmvc:包含SpringMVC框架相關的所有類。包含國際化、標籤、Theme、視圖展現的FreeMarker、JasperReports、 Tiles、Velocity、XSLT相關類。當然,如果你的應用使用了獨立的MVC框架,則無需這個JAR文件裏的任何類。
Spring webmvc portlet:Spring MVC的增強
Spring websocket:提供 Socket通信, web端的推送功能

2、Spring 入門案例

2.1 幾個小案例

2.1.1 包管理

使用maven管理

<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>
        <spring-version>5.1.3.RELEASE</spring-version>
    </properties>
    <dependencies>
        <!-- 是apache最早提供的日誌的門面接口。提供簡單的日誌實現以及日誌解耦功能 -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!-- spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!-- spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!-- spring-expression -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.1.2 IOC的入門小例子

IoC(Inversion of Control,控制反轉)。
所謂IoC,對於spring框架來說,就是由spring來負責控制對象的生命週期和對象間的關係。
簡單來說:初始化一個對象,不再是使用傳統的new方式,而是需要的時候,從Spring的容器中取。
  • 接口類
public interface UserService {

   void addUser();

   void updateUser(String code);

   void delUser(String code);

   User findUserAll();

}
  • 實現類
public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("進入了addUser的方法.............");
    }

    @Override
    public void updateUser(String code) {
        System.out.println("進入了updateUser的方法.............");
    }

    @Override
    public void delUser(String code) {
        System.out.println("進入了delUser的方法.............");
    }

    @Override
    public User findUserAll() {
        System.out.println("進入了findUserAll的方法.............");
        User user = null;
        return user;
    }
}
  • 配置文件
<?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">
    <!-- 配置service
        <bean> 配置需要創建的對象
            id :用於之後從spring容器獲得實例時使用的  【組件掃描的情況:默認的id號或者bean的name是類名的首字母小寫。】
            class :需要創建實例的全限定類名
    -->
    <bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl"></bean>
</beans>

測試類

public class UserTest {
    /**
     * 測試通過Spring的容器獲取bean
     */
    @Test
    public void test() {
        //獲取Spring的容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
        //獲得bean .不是傳統的需要new 出來,只需要通過Spring容器中取
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.addUser();
    }
}

2.1.3 DI 的入門小例子

  • Dao 接口類
public interface UserDao {

    int saveUser(User user);
    int updateUser(String code);
    int delUser(String code);
    User findUser();
}

  • Dao 實現類
public class UserDaoImpl implements UserDao {

    @Override
    public int saveUser(User user) {
        System.out.println("進入了Dao層的saveUser的方法.............");
        return 0;
    }

    @Override
    public int updateUser(String code) {
        System.out.println("進入了Dao層的updateUser的方法.............");
        return 0;
    }

    @Override
    public int delUser(String code) {
        System.out.println("進入了Dao層的delUser的方法.............");
        return 0;
    }

    @Override
    public User findUser() {
        System.out.println("進入了Dao層的findUser的方法.............");
        return null;
    }
}
  • Service接口類

使用IOC例子中的接口

  • Service接口類實現類
public class UserServiceImpl implements UserService {
    /**
     * 注入的對象
     */
    private UserDao userDao;

    /**
     * 注入對象,需要使用到setXXX方法
     * @param userDao
     */
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        userDao.saveUser(new User());
        System.out.println("進入了addUser的方法.............");
    }

    @Override
    public void updateUser(String code) {
        System.out.println("進入了updateUser的方法.............");
    }

    @Override
    public void delUser(String code) {
        System.out.println("進入了delUser的方法.............");
    }

    @Override
    public User findUserAll() {
        System.out.println("進入了findUserAll的方法.............");
        User user = null;
        return user;
    }
}
  • 配置文件
<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="userDaoId" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
    <!-- 配置service
        <bean> 配置需要創建的對象
            id :用於之後從spring容器獲得實例時使用的  【組件掃描的情況:默認的id號或者bean的name是類名的首字母小寫。】
            class :需要創建實例的全限定類名
    -->
    <bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl">
        <!--使用property進行對象注入
        name :bean的屬性名
        ref : 指向具體的實現對象引用
        -->
        <property name="userDao" ref="userDaoId"></property>
    </bean>
</beans>
  • 測試例子
public class UserTest {
    /**
     * 測試通過Spring的容器獲取bean
     */
    @Test
    public void test() {
        //獲取Spring的容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
        //獲得bean .不是傳統的需要new 出來,只需要通過Spring容器中取
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.addUser();
    }
}
  • 結果
    運行結果

2.1.4 依賴注入裝配Bean 屬性

  • 依賴注入方式:構造器注入和setter注入
  • 裝配方式(創建應用對象之間協作關係的行爲稱爲裝配):手動裝配與自動裝配

2.1.4.1 基於bean的注入

  • bean對象 :User
public class User {

    public int age;

    String code;

    public String name;

    public User() {
        
    }

    public User(int age, String code, String name) {
        this.age = age;
        this.code = code;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

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

public class Person {

    private User user;

    private String unicode ;

    public Person() {
    }

    public Person(User user, String unicode) {
        this.user = user;
        this.unicode = unicode;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getUnicode() {
        return unicode;
    }

    public void setUnicode(String unicode) {
        this.unicode = unicode;
    }

    @Override
    public String toString() {
        return "Person{" +
                "user=【" + user.getCode() + "," + user.getAge() + "," + user.getName() +
                "】, unicode='" + unicode + '\'' +
                '}';
    }
}

spring的xml配置文件

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

    <bean id="userDaoId" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
    <!-- 配置service
        <bean> 配置需要創建的對象
            id :用於之後從spring容器獲得實例時使用的  【組件掃描的情況:默認的id號或者bean的name是類名的首字母小寫。】
            class :需要創建實例的全限定類名
    -->
    <bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl">
        <!--使用property進行對象注入
        name :bean的屬性名
        ref : 指向具體的實現對象引用
        -->
        <property name="userDao" ref="userDaoId"></property>
    </bean>

    <!--基於構造器方法注入
        <constructor-arg> 用於配置構造方法一個參數argument
            name :參數的名稱
            value:設置普通數據

            ref:引用數據,一般是另一個bean id值
            index :參數的索引號,從0開始 。如果只有索引,匹配到了多個構造方法時,默認使用第一個。
            type :確定參數類型
    -->
    <bean id="user" class="com.mall.spring.bean.User">
        <!--方式1-->
       <!-- <constructor-arg name="age" value="11" />
        <constructor-arg name="code" value="00001" />
        <constructor-arg name="name" value="張三" />-->

        <!--方式2-->
        <constructor-arg index="0" type="int" value="12"></constructor-arg>
        <constructor-arg index="1" type="java.lang.String" value="00001"></constructor-arg>
        <constructor-arg index="2" type="java.lang.String" value="張三"></constructor-arg>
    </bean>

    <!--基於setter方法
        * 普通數據
            <property name="" value="值">
            等效
            <property name="">
                <value>值
            等效
            <property p:屬性=值 />
        * 引用數據
            <property name="" ref="另一個bean">
            等效
            <property name="">
                <ref bean="另一個bean"/>
    -->
    <bean id="userOther" class="com.mall.spring.bean.User" p:name="李四">
        <property name="age" value="13"></property>
        <property name="code">
            <value>00002</value>
        </property>
    </bean>
    <bean id="person" class="com.mall.spring.bean.Person" p:unicode="00000001" >
        <property name="user" ref="userOther"></property>
    </bean>
</beans>
  • 測試類
  @Test
    public void testBean() {
        User user = (User) applicationContext.getBean("user");
        System.out.println("user = " + user);
        User userOther = (User) applicationContext.getBean("userOther");
        System.out.println("userOther = " + userOther);
        Person person = (Person) applicationContext.getBean("person");
        System.out.println("person = " + person);
    }

結果:
測試類

2.1.4.2 基於集合的注入

  • 定義bean
public class Person {

    private User user;

    private String unicode ;

    private String[] arrData ;

    private List list;

    private Set set;

    private Map map;

    public Set getSet() {
        return set;
    }

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

    public Map getMap() {
        return map;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public List getList() {
        return list;
    }

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

    public String[] getArrData() {
        return arrData;
    }

    public void setArrData(String[] arrData) {
        this.arrData = arrData;
    }

    public Person() {
    }

    public Person(User user, String unicode) {
        this.user = user;
        this.unicode = unicode;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getUnicode() {
        return unicode;
    }

    public void setUnicode(String unicode) {
        this.unicode = unicode;
    }

    @Override
    public String toString() {
        return "Person{" +
                "user=【" + user.getCode() + "," + user.getAge() + "," + user.getName() +
                "】, unicode='" + unicode + '\'' +
                '}';
    }
}
  • 配置文件
 <bean id="person" class="com.mall.spring.bean.Person" p:unicode="00000001" >
        <property name="user" ref="userOther"></property>
        <!--數組的注入-->
        <property name="arrData">
            <array>
                <value>Dene</value>
                <value>Denr</value>
                <value>Dent</value>
                <value>Deny</value>
                <value>Denu</value>
                <value>Deno</value>
            </array>
        </property>
        <!--List集合的注入-->
        <property name="list">
            <list>
                <value>12</value>
                <value>你好</value>
                <value>是的</value>
                <value>我不是</value>
                <value>你就是</value>
            </list>
        </property>
        <!--Set 集合的注入-->
        <property name="set">
            <set>
                <value>121</value>
                <value>你好1</value>
                <value>是的1</value>
                <value>我不是1</value>
                <value>你就是1</value>
            </set>
        </property>
        <!-- Map集合 -->
        <property name="map">
            <map>
                <entry key="name1" value="Lucy"></entry>
                <entry key="name2" value="Jack"></entry>
                <entry>
                    <key><value>name3</value></key>
                    <value>李白</value>
                </entry>
            </map>
        </property>
    </bean>
  • 測試類
@Test
    public void testBean() {
        User user = (User) applicationContext.getBean("user");
        System.out.println("user = " + user);
        User userOther = (User) applicationContext.getBean("userOther");
        System.out.println("userOther = " + userOther);
        Person person = (Person) applicationContext.getBean("person");
        System.out.println("person = " + person);
        System.out.println("person 中的數組 = " + Arrays.toString(person.getArrData()));
        System.out.println("person 中的數組 = " + Arrays.toString(person.getArrData()));
        System.out.println("person 中的List數組 = " + person.getList());
        System.out.println("person 中的Map = " + person.getMap());
    }

測試結果
測試結果
結論:
上面所提到的都是手動裝配的方式。當一個對象的屬性是另一個對象時,實例化時,需要爲這個對象屬性進行實例化,這就是裝配。如果一個對象只通過接口來表明依賴關係,那麼這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實現進行切換。但是這樣會存在一個問題,在傳統的依賴注入配置中,我們必須要明確要給屬性裝配哪一個bean的引用,一旦bean很多,就不好維護了(**配置文件就會很臃腫 **)。基於這種場景,spring使用註解來進行自動裝配,解決這個問題。自動裝配就是開發人員不必知道具體要裝配哪個bean的引用,這個識別的工作會由spring來完成。與自動裝配配合的還有“自動檢測”,這 個動作會自動識別哪些類需要被配置成bean,進而來進行裝配。這樣我們就明白了,自動裝配是爲了將依賴注入“自動化”的一個簡化配置的操作。

2.1.5 裝配方式

2.1.5.1 裝配分四種:byName, byType, constructor, autodetect。 【可以使用autowire進行配置,不配置,默認使用byName】

比如:
配置文件:

<bean id="userDao" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoII" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<!--使用 property 進行裝配-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" >
        <property name="userDao" ref="userDaoll"></property>
    </bean>
  • byName:根據屬性名自動裝配。此選項將檢查容器並根據名字查找 ,與屬性完全一致的bean,並將其與屬性自動裝配。
    例子:<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" autowire="byName" />

  • byType 如果容器中存在一個與指定屬性類型相同的bean,那麼將與 該屬性自動裝配;如果存在多個該類型bean,那麼拋出異常, 並指出不能使用byType方式進行自動裝配;如果沒有找 到相匹配的bean,則什麼事都不發生,也可以通過設置 。
    例子:<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" autowire="byType">

  • constructor 就是通過構造器來將類型與參數相同的bean進行裝配。

  • autodetect 是constructor與byType的組合,會先進行constructor,如果不成功,再進行byType。

2.1.5.2 註解開啓與一些自動裝配的註解

  • 開啓註解 : <context:annotation-config />
  • 掃描註解包路徑: <context:component-scan base-package="com.mall.spring" />
2.1.5.2.1 常用的自動裝配註解有以下幾種:@Autowired,@Resource,@Inject,@Qualifier,@Named
  • @Autowired 使用
    解釋:@Autowired註解是byType類型的,這個註解可以用在屬性上面,setter方面上面以及構造器上面。使用這個註解時,就不需要在類中爲屬性添加setter方法了。但是這個屬性是強制性的,也就是說必須得裝配上,如果沒有找到合適的bean能夠裝配上,就會拋出異常。
    這時可以使用required=false來允許可以不被裝配上,默認值爲true。
    當required=true時,@Autowired要求必須裝配,但是在沒有bean能裝配上時,就會拋出異常:NoSuchBeanDefinitionException,如果required=false時,則不會拋出異常。
  • @Qualifier註解
    解釋:@Qualifier註解使用byName進行裝配,這樣可以在多個類型一樣的bean中,明確使用哪一個名字的bean來進行裝配。@Qualifier註解起到了縮小自動裝配候選bean的範圍的作用,@Qualifier不能單獨使用。
    代碼展示:
	@Autowired
    @Qualifier(value = "animal")
    private Animal animal;
  • @Resource 註解
    解釋:@Resource如有指定的name屬性,先按該屬性進行byName方式查找裝配;其次再進行默認的byName方式進行裝配;如果以上都不成功,則按byType的方式自動裝配。都不成功,則報異常。(註解也是java ee的)
    如代碼:
    @Resource(name = "person")
    private Person person;
    @Resource
    private User user;
  • @Inject註解
    解釋:與@Autowired註解作用一樣,也是byType類型,而且是java ee提供的,完全可以代替@Autowired註解,但是@Inject必須是強制裝配的,沒有required屬性,也就是不能爲null,如果不存在匹配的bean,會拋出異常。
    @Inject也有一個組合的註解,就是@Named註解,與@Qualifier作用一樣,也是byName,但是不是spring的,是java ee標準的。這樣就出現了兩套自動裝配的註解組合,@Autowired與@Qualifier是spring提供的,@Inject與@Named是java ee的
2.1.5.2.2 構造器註解:@Controller,@Components,@Service,@Repository和使用@Component標註的自定義註解
	作用:生成的bean的ID默認爲類的非限定名,也就是把類的名字的首字母換成小寫。可以在這些註解的值中寫名bean id的值,比如:@Component(value = "animal")。

幾個註解的簡單說明:
@Controller註解 只能用控制器類上
@Service註解 只能用在業務類上
@Repository註解 只能用在dao類上
@Component註解 無法按照上面三個註解分類,就用此註解

3、Spring AOP 切面

3.1 概念

3.1.1定義:

定義:
AOP(Aspect Oriented Programming),即面向切面編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承、多態等概念來建立一種對象層次結構,用於模擬公共行爲的一個集合。不過OOP允許開發者定義縱向的關係,但並不適合定義橫向的關係,例如日誌功能。日誌代碼往往橫向地散佈在所有對象層次中,而與它對應的對象的核心功能毫無關係對於其他類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散佈在各處的無關的代碼被稱爲橫切(cross cutting),在OOP設計中,它導致了大量代碼的重複,而不利於各個模塊的重用。
AOP技術恰恰相反,它利用一種稱爲"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊之間的耦合度,並有利於未來的可操作性和可維護性。
使用"橫切"技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在覈心關注點的多處,而各處基本相似,比如權限認證、日誌、事物。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。

3.1.2 基本概念圖

概念圖

3.1.3 通知類型介紹

介紹
(1)Before:在目標方法被調用之前做增強處理,@Before只需要指定切入點表達式即可
(2)AfterReturning:在目標方法正常完成後做增強,@AfterReturning除了指定切入點表達式後,還可以指定一個返回值形參名returning,代表目標方法的返回值
(3)AfterThrowing:主要用來處理程序中未處理的異常,@AfterThrowing除了指定切入點表達式後,還可以指定一個throwing的返回值形參名,可以通過該形參名
來訪問目標方法中所拋出的異常對象
(4)After:在目標方法完成之後做增強,無論目標方法時候成功完成。@After可以指定一個切入點表達式
(5)Around:環繞通知,在目標方法完成前後做增強處理,環繞通知是最重要的通知類型,像事務,日誌等都是環繞通知,注意編程中核心是一個ProceedingJoinPoint。

3.2 案例

3.2.1 增加包

使用spring的包:
<!-- spring-aop 依賴 start -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!--處理事務和AOP所需的包 切面-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!-- spring-aop 依賴 end -->

或許:

 <!-- spring-aop 依賴 start -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!-- aspectjweaver 處理事務和AOP所需的包  -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
         aopalliance Spring AOP 的接口支持 三個主要業務實體:Advice 、Interceptor、Joinpoint
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- spring-aop 依賴 end -->

3.2.2 基於XML的切面小案例

  • 切入類:
public class TimeHandler {
    public void printTime() {
        System.out.println("當前時間是:" + System.currentTimeMillis());
    }
}
  • 接口類:前面例子的接口類 【interface UserService】
  • 實現類: 前面例子的類 【class UserServiceImpl implements UserService】
  • 配置文件:
<aop:config>
        <aop:aspect id = "time" ref="timeHandler">
            <!--切入Service的所有方法-->
            <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
            <!--切入Service的以add開頭的方法-->
           <!-- <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.add*(..))" />-->
            <aop:before method="printTime" pointcut-ref="timeMethod" />
            <aop:after method="printTime" pointcut-ref="timeMethod" />
        </aop:aspect>
    </aop:config>
  • 測試類
 @Test
    public void test() {
        //獲得bean .不是傳統的需要new 出來,只需要通過Spring容器中取
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.addUser();
        System.out.println("------------------------------------------------------------------");
        userService.updateUser("name");
    }
  • 運行結果:
    結果

3.2.3 基於XML的切面小案例增加橫切點,關鍵字 order 使用

order屬性的數字就是橫切關注點的順序

  • 新增日誌模擬類:
public class LogHamdler {
    public void logBefore() {
        System.out.println("日誌打印前.....before.....");
    }
    public void logAfter() {
        System.out.println("日誌打印後.....after.....");
    }
}
  • xml配置
<aop:config>
        <aop:aspect id = "time" ref="timeHandler" order="1">
            <!--切入Service的所有方法-->
            <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
            <!--切入Service的以add開頭的方法-->
           <!-- <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.add*(..))" />-->
            <aop:before method="printTime" pointcut-ref="timeMethod" />
            <aop:after method="printTime" pointcut-ref="timeMethod" />
        </aop:aspect>
        <aop:aspect id = "log" ref="logHandler" order="2">
            <aop:pointcut id="logMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
            <aop:before method="logBefore" pointcut-ref="logMethod" />
            <aop:after method="logAfter" pointcut-ref="logMethod" />
        </aop:aspect>
    </aop:config>
  • 測試類運行後結果得到
    測試結果

3.2.4 基於註解方式小案例

  • 配置文件
<!--配置自動匹配 aspectJ 註解的 Java 類生成代理對象 -->
<aop:aspectj-autoproxy />
  • 接口類:
public interface PersonService {

    Person addPerson(Person person);

    void delPerson(String code);
}
  • 接口實現類
@Service("personService")
public class PersonServiceImpl implements PersonService {

    @Resource(name = "person")
    private Person person;
    @Resource
    private User user;

    @Override
    public Person addPerson(Person person) {
        System.out.println("進入了PersonServiceImpl類的addPerson()...............");
        return person;
    }

    @Override
    public void delPerson(String code) {
        int i = 10;
        int count = i / 0 ;
        System.out.println("進入了PersonServiceImpl類的delPerson()...............");
    }
}
  • 切面類 一:
package com.mall.spring.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
 * @author 超
 * Create by fengc on  2018/12/2 22:54
 */
@Order(2)
@Aspect
@Component
public class LoggingAspect {

    /**
     * 定義一個方法, 用於聲明切入點表達式. 一般地, 該方法中再不需要添入其他的代碼.
     * 使用 @Pointcut 來聲明切入點表達式.
     * 後面的其他通知直接使用方法名來引用當前的切入點表達式.
     */
    @Pointcut("execution(* com.mall.spring.service.PersonService.*(..))")
    public void declareJointPointExpression(){}

    /**
     * 在 com.mall.spring.service.PersonService 接口的每一個實現類的每一個方法開始之前執行一段代碼
     */
    @Before("declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();
        Object object = joinPoint.getTarget();
        System.out.println("The object = " +object+ ",The method = " + methodName + ", begins with = " + Arrays.asList(args) );
    }

    /**
     * 在方法執行之後執行的代碼. 無論該方法是否出現異常
     */
    @After("declareJointPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }

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

    /**
     * 在目標方法出現異常時會執行的代碼.
     * 可以訪問到異常對象; 且可以指定在出現特定異常時在執行通知代碼
     */
    @AfterThrowing(value="declareJointPointExpression()",throwing="e")
    public void afterThrowing(JoinPoint joinPoint, Exception e){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs excetion:" + e);
    }
    /**
     * 環繞通知需要攜帶 ProceedingJoinPoint 類型的參數.
     * 環繞通知類似於動態代理的全過程: ProceedingJoinPoint 類型的參數可以決定是否執行目標方法.
     * 且環繞通知必須有返回值, 返回值即爲目標方法的返回值
     */
   /* @Around("execution(* com.mall.spring.service.PersonService.*(..))")
    public Object aroundMethod(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 " + methodName + " ends with " + result);
        } catch (Throwable e) {
            //異常通知
            System.out.println("The method " + methodName + " occurs exception:" + e);
            throw new RuntimeException(e);
        }
        //後置通知
        System.out.println("The method " + methodName + " ends");
        return result;
    }*/
}

  • 切面類二 【爲了測試 order關鍵字 order越小,橫切關注點的順序越先】
@Order(1)
@Aspect
@Component
public class VlidationAspect {

    @Before("com.mall.spring.config.LoggingAspect.declareJointPointExpression()")
    public void validateArgs(JoinPoint joinPoint) {
        System.out.println("-------validateArgs:【Arrays.asList(joinPoint.getArgs())】:" + Arrays.asList(joinPoint.getArgs()));
    }
}

測試類:

package com.mall.spring.service;

import com.mall.spring.bean.Person;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author 超
 * Create by fengc on  2018/12/2 23:06
 */
public class PersonTest {

    //獲取Spring的容器
    ApplicationContext applicationContext = null;

    @Before
    public void getApplicationContextInstance() {
        applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
    }

    @Test
    public void test() {
        PersonService personService = (PersonService) applicationContext.getBean("personService");
        personService.addPerson(new Person());
        System.out.println("---------------------------------------------------------------------------");
        personService.delPerson("21321");
    }
}

運行結果:
運行結果

總結論
上面總結了Spring的一些基本用法,當做筆記。
總的來說,Spring是一個十分優秀的框架。它顛覆了我們對編程的一些傳統觀念。
上面主要讓大家瞭解了Spring的主要核心分別是:Spring的IOC(控制反轉)和DI(依賴注入),
Spring的AOP 切面的簡單效果。

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