1. 程式人生 > >spring (轉載老師講義,博主只圖檢視方便)

spring (轉載老師講義,博主只圖檢視方便)

SpringFramework

一、介紹

Spring 是最受歡迎的企業級 Java 應用程式開發框架,數以百萬的來自世界各地的開發人員使用 Spring 框架來建立效能好、易於測試、可重用的程式碼。Spring 框架是一個開源的 Java 平臺,它最初是由 Rod Johnson 編寫的,並且於 2003 年 6 月首次在 Apache 2.0 許可下發布。Spring 是輕量級的框架,其基礎版本只有 2 MB 左右的大小。Spring 框架的核心特性是可以用於開發任何 Java 應用程式,但是在 Java EE 平臺上構建 web 應用程式是需要擴充套件的。 Spring 框架的目標是使 J2EE 開發變得更容易使用,通過啟用基於 POJO 程式設計模型來促進良好的程式設計實踐。

1、Spring 框架主要的好處

  1. Spring 可以使開發人員使用 POJOs 開發企業級的應用程式。只使用 POJOs 的好處是你不需要一個 EJB 容器產品,比如一個應用程式伺服器,但是你可以選擇使用一個健壯的 servlet 容器,比如 Tomcat 或者一些商業產品。

  2. Spring 在一個單元模式中是有組織的。即使包和類的數量非常大,你只要擔心你需要的,而其它的就可以忽略了。

  3. Spring 不會讓你白費力氣做重複工作,它真正的利用了一些現有的技術,像ORM 框架、日誌框架、JEE、Quartz 和 JDK 計時器,其他檢視技術。

  4. 測試一個用 Spring 編寫的應用程式很容易,因為環境相關的程式碼被移動到這個框架中。此外,通過使用 JavaBean-style POJOs,它在使用依賴注入注入測試資料時變得更容易。

  5. Spring 的 web 框架是一個設計良好的 web MVC 框架,它為比如 Structs 或者其他工程上的或者不怎麼受歡迎的 web 框架提供了一個很好的供替代的選擇。

  6. Spring 對JavaEE開發中非常難用的一些API(JDBC、JavaMail、遠端呼叫等),都提供了封裝,使這些API應用難度大大降低。

  7. 輕量級的 IOC 容器往往是輕量級的,例如,特別是當與 EJB 容器相比的時候。這有利於在記憶體和 CPU 資源有限的計算機上開發和部署應用程式。

  8. Spring提供了一致的事務管理介面,可向下擴充套件到(使用一個單一的資料庫,例如)本地事務並擴充套件到全域性事務(例如,使用 JTA)。

2、spring 兩大核心特性

2.1、IOC依賴注入(DI)

Spring 最認同的技術是控制反轉的依賴注入(DI)模式。控制反轉(IoC)是一個通用的概念,它可以用許多不同的方式去表達,依賴注入僅僅是控制反轉的一個具體的例子。

當編寫一個複雜的 Java 應用程式時,應用程式類應該儘可能的獨立於其他的 Java 類來增加這些類可重用可能性,當進行單元測試時,可以使它們獨立於其他類進行測試。依賴注入(或者有時被稱為配線)有助於將這些類粘合在一起,並且在同一時間讓它們保持獨立。

到底什麼是依賴注入?讓我們將這兩個詞分開來看一看。這裡將依賴關係部分轉化為兩個類之間的關聯。例如,類 A 依賴於類 B。現在,讓我們看一看第二部分,注入。所有這一切都意味著類 B 將通過 IoC 被注入到類 A 中。

依賴注入可以以向建構函式傳遞引數的方式發生,或者通過使用 setter 方法 post-construction。由於依賴注入是 Spring 框架的核心部分,所以我將在一個單獨的章節中利用很好的例子去解釋這一概念。

2.2、面向方面的程式設計(AOP):

Spring 框架的一個關鍵元件是面向方面的程式設計(AOP)框架。一個程式中跨越多個點的功能被稱為橫切關注點,這些橫切關注點在概念上獨立於應用程式的業務邏輯。有各種各樣常見的很好的關於方面的例子,比如日誌記錄、宣告性事務、安全性,和快取等等。

在 OOP 中模組化的關鍵單元是類,而在 AOP 中模組化的關鍵單元是方面。AOP 幫助你將橫切關注點從它們所影響的物件中分離出來,然而依賴注入幫助你將你的應用程式物件從彼此中分離出來。

Spring 框架的 AOP 模組提供了面向方面的程式設計實現,可以定義諸如方法攔截器和切入點等,從而使實現功能的程式碼徹底的解耦出來。使用原始碼級的元資料,可以用類似於.Net屬性的方式合併行為資訊到程式碼中。我將在一個獨立的章節中討論更多關於 Spring AOP 的概念。

3、Spring 框架結構

          3.1、體系結構圖

 

Spring 有可能成為所有企業應用程式的一站式服務點,然而,Spring 是模組化的,允許你挑選和選擇適用於你的模組,不必要把剩餘部分也引入。Spring 框架提供約 20 個模組,可以根據應用程式的要求來使用。

  • spring-core模組提供了框架的基本組成部分,包括 IoC 和依賴注入功能。

  • spring-beans 模組提供 BeanFactory,工廠模式的微妙實現,它移除了編碼式單例的需要,並且可以把配置和依賴從實際編碼邏輯中解耦。

  • context模組建立在由corebeans 模組的基礎上建立起來的,它以一種類似於JNDI註冊的方式訪問物件。Context模組繼承自Bean模組,並且添加了國際化(比如,使用資源束)、事件傳播、資源載入和透明地建立上下文(比如,通過Servelet容器)等功能。Context模組也支援Java EE的功能,比如EJB、JMX和遠端呼叫等。ApplicationContext介面是Context模組的焦點。spring-context-support提供了對第三方庫整合到Spring上下文的支援,比如快取(EhCache, Guava, JCache)、郵件(JavaMail)、排程(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。

  • spring-expression模組提供了強大的表示式語言,用於在執行時查詢和操作物件圖。它是JSP2.1規範中定義的統一表達式語言的擴充套件,支援set和get屬性值、屬性賦值、方法呼叫、訪問陣列集合及索引的內容、邏輯算術運算、命名變數、通過名字從Spring IoC容器檢索物件,還支援列表的投影、選擇以及聚合等。

  • JDBC 模組提供了JDBC抽象層,它消除了冗長的JDBC編碼和對資料庫供應商特定錯誤程式碼的解析。

  • ORM 模組提供了對流行的物件關係對映API的整合,包括JPA、JDO和Hibernate等。通過此模組可以讓這些ORM框架和spring的其它功能整合,比如前面提及的事務管理。

  • OXM 模組提供了對OXM實現的支援,比如JAXB、Castor、XML Beans、JiBX、XStream等。

  • JMS 模組包含生產(produce)和消費(consume)訊息的功能。從Spring 4.1開始,集成了spring-messaging模組。。

  • 事務模組為實現特殊介面類及所有的 POJO 支援程式設計式和宣告式事務管理。(注:程式設計式事務需要自己寫beginTransaction()、commit()、rollback()等事務管理方法,宣告式事務是通過註解或配置由spring自動處理,程式設計式事務粒度更細)

  • Web 模組提供面向web的基本功能和麵向web的應用上下文,比如多部分(multipart)檔案上傳功能、使用Servlet監聽器初始化IoC容器等。它還包括HTTP客戶端以及Spring遠端呼叫中與web相關的部分。。

  • Web-MVC 模組為web應用提供了模型檢視控制(MVC)和REST Web服務的實現。Spring的MVC框架可以使領域模型程式碼和web表單完全地分離,且可以與Spring框架的其它所有功能進行整合。

  • Web-Socket 模組為 WebSocket-based 提供了支援,而且在 web 應用程式中提供了客戶端和伺服器端之間通訊的兩種方式。

  • Web-Portlet 模組提供了用於Portlet環境的MVC實現,並反映了spring-webmvc模組的功能。

  • AOP 模組提供了面向方面的程式設計實現,允許你定義方法攔截器和切入點對程式碼進行乾淨地解耦,從而使實現功能的程式碼徹底的解耦出來。使用原始碼級的元資料,可以用類似於.Net屬性的方式合併行為資訊到程式碼中。

  • Aspects 模組提供了與 AspectJ 的整合,這是一個功能強大且成熟的面向切面程式設計(AOP)框架。

  • Instrumentation 模組在一定的應用伺服器中提供了類 instrumentation 的支援和類載入器的實現。

  • Messaging 模組為 STOMP 提供了支援作為在應用程式中 WebSocket 子協議的使用。它也支援一個註解程式設計模型,它是為了選路和處理來自 WebSocket 客戶端的 STOMP 資訊。

  • 測試模組支援對具有 JUnit 或 TestNG 框架的 Spring 元件的測試。

二、IOC&DI

1、IoC 容器

Spring 容器是 Spring 框架的核心。容器將建立物件,把它們連線在一起,配置它們,並管理他們的整個生命週期從建立到銷燬。Spring 容器使用依賴注入(DI)來管理組成一個應用程式的元件。這些物件被稱為 Spring Beans。

Spring通過閱讀配置元資料提供的指令,容器知道對哪些物件進行例項化,配置和組裝。配置元資料可以通過 XML,Java 註釋或 Java 程式碼來表示。下圖是 Spring 如何工作的高階檢視。 Spring IoC 容器利用 Java 的 POJO 類和配置元資料來生成完全配置和可執行的系統或應用程式。

 

2、Spring 各種IOC 容器

容器型別-介面 實現類 描述
ApplicationContext 容器 FileSystemXmlApplicationContext 該容器從 XML 檔案中載入已被定義的 bean;你需要提供給構造器 XML 檔案的完整路徑
  ClassPathXmlApplicationContext 該容器從 XML 檔案中載入已被定義的 bean。在這裡,你不需要提供 XML 檔案的完整路徑,只需正確配置 CLASSPATH 環境變數即可,因為,容器會從 CLASSPATH 中搜索 bean 配置檔案。
  WebXmlApplicationContext 該容器會在一個 web 應用程式的範圍內載入在 XML 檔案中已被定義的 bean

三、基於XML配置

1、註冊一個Bean到容器

開發流程:

①建立工程 並在pom檔案中新增依賴

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

②建立pojo類 Car.java

package com.xingxue.spring.domain;
​
public class Car  {
    
    private  String  brand; //品牌
    private  String  carName; //型號名稱
    private  String  carType; // 型別
    private  Double  price // 售價
    
    //此處省略...sets and  gets
}

③建立spring的配置檔案 applicationContext.xml 位於 classpath 下

<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">
    <!--註冊向IOC中註冊一個Bean-->
    <bean name="mycar" class="com.xingxue.spring.domain.Car">
        <!--注入屬性:底層呼叫set方法-->
        <property name="brand" value="寶馬"/>
        <property name="carName" value="X5"/>
        <property name="carType" value="SUV"/>
        <property name="price" value="400000"/>
    </bean>
</beans>

④建立容器並測試

     @Test
    public  void test1(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Object car=  ctx.getBean("mycar");
        System.out.println(car);
    }

2、bean 的作用域

當在 Spring 中定義一個 bean 時,你必須宣告該 bean 的作用域的選項。例如,為了強制 Spring 在每次需要時都產生一個新的 bean 例項,你應該宣告 bean 的作用域的屬性為 prototype。同理,如果你想讓 Spring 在每次需要時都返回同一個bean例項,你應該宣告 bean 的作用域的屬性為 singleton

作用域 描述
singleton 在spring IoC容器僅存在一個Bean例項,Bean以單例方式存在,預設值
prototype 每次從容器中呼叫Bean時,都返回一個新的例項,即每次呼叫getBean()時,相當於執行newXxxBean()
request 每次HTTP請求都會建立一個新的Bean,該作用域僅適用於WebApplicationContext環境
session 同一個HTTP Session共享一個Bean,不同Session使用不同的Bean,僅適用於WebApplicationContext環境
global-session 一般用於Portlet應用環境,該運用域僅適用於WebApplicationContext環境
   <bean name="mycar2" class="com.xingxue.spring.domain.Car" scope="prototype">
        <!--注入屬性:底層呼叫set方法-->
        <property name="brand" value="寶馬"/>
        <property name="carName" value="X5"/>
        <property name="carType" value="SUV"/>
        <property name="price" value="400000"/>
    </bean>

這裡只討論 singleton prototype ,其他三個需要在使用web環境下有效

3、bean 的生命週期

生命週期指bean床建立到初始化到銷燬的一個過程,spring中可以監聽到這個過程

    
   <bean name="mycar3" class="com.xingxue.spring.domain.Car" scope="prototype" 
        init-method="begin"    
        destroy-method="end">
        <!--注入屬性:底層呼叫set方法-->
        <property name="brand" value="寶馬"/>
        <property name="carName" value="X5"/>
        <property name="carType" value="SUV"/>
        <property name="price" value="400000"/>
    </bean>

init-method="begin" 監聽bean初始化 begin為bean 中的一個方法

destroy-method="end" 監聽bean銷燬 end為bean 中的一個方法

4、依賴注入

當註冊一個bean時,bean的屬性需要賦值,為這些屬性賦值就是為其依賴賦值,賦值的過程就是依賴注入的過程spring中有兩種方式。

  • 屬性set方法注入(呼叫Bean中提供的set方法)

           <!-- 向ioc容器中註冊了一個bean -->
           <bean id="car"  class="com.xingxue.spring.pojo.Car" scope="prototype"
                 destroy-method="destory" init-method="init">
                <!--為bean屬性賦值-->
                <property name="brand" value="寶馬"/>
                <property name="carName" value="X5"/>
                <property name="type"  value="SUV" />
                <property name="price" value="500000"/>
           </bean>    
    ​
           <bean id="p1" class="com.xingxue.spring.pojo.Person">
                    <property name="name" value="譚倉龍"/>
                    <property name="age" value="24"/>
                    <property name="car" ref="car">    
           </bean>

    這裡使用的是外部 bean, 如果只想用一次可以使用內部 bean

     <bean id="p1" class="com.xingxue.spring.pojo.Person">
                    <property name="name" value="譚倉龍"/>
                    <property name="age" value="24"/>
                    <!--<property name="car" ref="car">-->
                    <property name="car">
                          <bean class="com.xingxue.spring.pojo.Car">
                                   <property name="brand"  value="Audi"/>
                                   <property name="type" value="B級車"/>
                                   <property name="carName" value="a6l" />
                                   <property name="price" value="300000"/>
                          </bean>
                    </property>
     </bean>

    Spring中簡單的資料型別 如 8大基本資料型別及其包裝類和String 型別可以通過 value直接賦值,但如果是自定義的型別,需要使用 ref 引用賦值,或者使用內部bean賦值。


    List[陣列] Set Map Properties 型別注入

    ①List

    <bean id="st" class="com.xingxue.spring.pojo.Student">
                <property name="name" value="王紅"/>
                <property name="hobby">
                        <list>
                            <value>Maven 入門到精通</value>
                            <value>Mybatis</value>
                            <value>LOL</value>
                        </list>
                </property>
         </bean>

    ②Set

        <bean id="st2" class="com.xingxue.spring.pojo.Student">
            <property name="name" value="楊詢"/>
            <property name="skills">
                  <set>
                      <ref bean="car"></ref>
                      <value>扔石頭</value>
                      <value>打劫</value>
                  </set>
            </property>
        </bean>

    ③Map

        <bean id="st4" class="com.xingxue.spring.pojo.Student">
               <property name="name" value="李心浩"/>
               <property name="maps">
                     <map>
                           <entry key="m1" value="范冰冰"/>
                           <entry key="m2" value="成龍"/>
                           <entry key="m3" value="摩登兄弟"/>
                     </map>
               </property>
        </bean>

     

    ④Properties

     <bean id="st4" class="com.xingxue.spring.pojo.Student">
               <property name="name" value="李心浩"/>
               <property name="maps">
                     <map>
                           <entry key="m1" value="范冰冰"/>
                           <entry key="m2" value="成龍"/>
                           <entry key="m3" value="摩登兄弟"/>
                     </map>
               </property>
        </bean>

補充: 讀取properties檔案中的資料賦值給 javaBean的屬性。

db.properties檔案

user=root
password=admin
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///xxx

dbutis.java

public class DButils {
        private  String  user;
        private  String  password;
        private  String  url;
        private  String  driver;
        // 此處省略 get set方法
}

spring 配置檔案

    <!--  註冊一個bean 讀取外部propeties 檔案  -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
          <property name="location" value="db.properties"/>
    </bean>
    <bean class="com.xingxue.spring.pojo.DButils" id="dbutis">
            <property name="user" value="${user}"/>
            <property name="url" value="${url}"/>
            <property name="password" value="${password}"/>
            <property name="driver" value="${driver}"/>
    </bean>

${ } :spring EL 表示式

  • 構造器注入

public class CarServiceImpl implements CarService {
    //注入如 Dao 物件
    CarDao  carDao;
​
    public  CarServiceImpl(CarDao carDao){
        this.carDao=carDao;
    }
​
    @Override
    public List<Car> list() throws Exception {
        System.out.println("服務層執行 呼叫Dao層........");
        return carDao.selectAll();
    }
}
 <bean class="com.xingxue.spring.dao.impl.CarDaoImpl" id="carDao"/>
​
 <bean class="com.xingxue.spring.service.impl.CarServiceImpl" id="carService">
        <constructor-arg name="carDao" ref="carDao"/>
 </bean>

5、自動裝配

  • byType

        <bean class="com.xingxue.spring.pojo.Person" autowire="byType">
            <property name="name" value="趙浩"/>
            <property name="age" value="40"/>
        </bean>

    autowire="byType" : 開啟自動裝配,根據l型別裝配,IOC 容器把選擇型別一致的Bean注入,當有多個bean同時滿足注入條件,可以使用 autowire-candidate="false" 禁用預設滿足條件的bean(加了這個屬性的bean放棄注入)

  • byName

        <bean class="com.xingxue.spring.pojo.Person" autowire="byName">
              <property name="name" value="趙浩"/>
             <property name="age" value="40"/>
        </bean>

    autowire="byName" : 開啟自動裝配,根據名字裝配,IOC容器會把 現有Bean名字跟被裝配屬性名字一致Bean 自動注入

  • constructor

    <bean class="com.xingxue.spring.pojo.Person" autowire="constructor">
            <property name="name" value="趙浩"/>
            <property name="age" value="40"/>
     </bean>

和byType類似,根據構造器引數型別注入

  • no

    autowire="no" 就是不注入

四、基於註解配置

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    <!--  包掃描 -->
    <context:component-scan base-package="com.xingxue.spring"/>
    <!--  開啟註解配置-->
    <context:annotation-config/>
   
</beans>

java程式碼

控制器

@Controller
public class CarController {
     // 自動注入服務層依賴
     @Autowired
     private CarService carService;
​
     public  void  getAll() throws Exception{
          System.out.println("呼叫CarService");
          carService.list();
      }
      public void setCarService(CarService carService) {
        this.carService = carService;
      }
}

服務層

@Service
public class CarServiceImpl implements CarService {
    // 自動注入如 持久層 依賴
    @Autowired
    private  CarDao  carDao;
    @Override
    public List<Car> list() throws Exception {
        System.out.println("服務層執行 呼叫Dao層........");
        return carDao.selectAll();
    }
    public void setCarDao(CarDao carDao) {
        this.carDao = carDao;
    }
}

持久層

@Repository
public class CarDaoImpl implements CarDao {
    @Override
    public List<Car> selectAll() throws Exception {
        System.out.println("連線資料庫查詢到了全部資料..........");
        return  null;
    }
}

各種註解:

@Component : 通用註解,表示一個元件,沒有業務含義

@Controller: 控制器層專門註解,表示控制器

@Service: 服務層註解,表示業務邏輯

@Repository:持久層註解,表示持久層

@Autowired:自動注入依賴

五、Aop程式設計

AOP面向方面,是一種程式設計思想,是對面向物件(OOP)的完善和補充,彌補了OOP橫向切入程式設計能力,AOP的主要作用是把非業務邏輯程式碼和業務邏輯程式碼分離。用於處理 日誌,事務,許可權驗證,資料校驗等場景。SprinpAOP預設使用JDK物件代理,當被代理物件沒有介面時使用cglib動態代理技術。

1、動態代理

動態代理:首先aop實現的底層技術就是代理技術,代理技術是一種軟體設計模式,叫代理模式,代理模式分按用途分可以有很多種,但是按照建立方式分為,靜態代理和動態代理,靜態代理,要建立N個代理類,動態代理動態生成代理物件,無需手動建立代理類,JDK也提供了動態代理機制。

以下設計靜態代理示例程式碼:

public class IGirl {
    public  void cook() ;
}
public class  SimpleGirl  implments IGirl{
    public  void cook() {
        System.out.println("做飯....");
    }
}
class  ProxyGirl implments  IGirl {
     private  Girl  relGril;
     public  ProxyGirl(Girl girl){
         this.relGril=girl;
     }
     public  void cookie(){
             System.out.println("買菜....");
              this.relGril.cook();
    }
}

以下是動態代理的示例程式碼:

public interface IFly {
         public void fly();
}
public class Birds  implements  IFly {
    @Override
    public void fly() {
        System.out.println("鳥兒飛........");
    }
}
public class Plane implements  IFly {
    @Override
    public void fly() {
        System.out.println("飛機飛..........");
    }
}
public class MyInvocationHandler implements InvocationHandler {
​
    //把被代理對物件傳入
    private  IFly  relobj;
    public MyInvocationHandler(   IFly  obj  ){
          this.relobj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        System.out.println("----------- 前置 增強 ---------------");
        Object rs= null;
        try {
            //核心業務
            rs = method.invoke(relobj ,  args );
            System.out.println("----------返回 增強------------");
        } catch (Exception e) {
            System.out.println("----------異常 增強------------");
        }
        System.out.println("----------- 後置 增強 --------------");
        return rs;
    }
}

2、使用SpringAOP( 註解配置 )

① 引入依賴

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.14.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.9</version>
    </dependency>

② 編寫 切面

@Component
@Aspect
public class LogAspect {
      @Before("execution( * com.xingxue.spring.aop.SimpleCaculate.add(..) )")
      public  void  logBefore(JoinPoint joinPoint){
          System.out.println( "開始執行"+joinPoint.getSignature()+ System.currentTimeMillis() );
      }
​
      @After("execution(* com.xingxue.spring.aop.SimpleCaculate.sub(..))")
      public void logAfter( JoinPoint joinPoint ){
          System.out.println("結束執行"+joinPoint.getSignature()+ System.currentTimeMillis());
      }
}
​

③ 編寫 介面和實現類

package com.xingxue.spring.aop;
/**
 * 計算器介面
 */
public interface Icaculate {
    public  int  add(int a, int b );
    public  int  sub(int a, int b);
}

編寫 實現類

package com.xingxue.spring.aop;
import org.springframework.stereotype.Component;
/**
 * 計算器實現類
 */
@Component
public class SimpleCaculate implements  Icaculate {
    @Override
    public int add(int a, int b) {
        return a+b;
    }
    @Override
    public int sub(int a, int b) {
        return a-b;
    }
}

④ 配置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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
​
        <!--開啟包掃描-->
        <context:component-scan base-package="com.xingxue.spring.aop"/>
        <!--開啟註解配置-->
        <context:annotation-config/>
        <!-- 開啟動態代理-->
        <aop:aspectj-autoproxy />
</beans>

⑤測試

     @Test
      public void test(){
         ApplicationContext  ctx = new ClassPathXmlApplicationContext("aop.xml");
             Icaculate  cc= ctx.getBean(Icaculate.class);
              cc.add(1,1);
              cc.sub(1,1);
      }

開始執行int com.xingxue.spring.aop.Icaculate.add(int,int)1536031653004結束執行int com.xingxue.spring.aop.Icaculate.sub(int,int)1536031653004

3、關於Aop的幾個概念

  1. Aspect(切面):通常是一個類,裡面可以定義切入點和通知

  2. JointPoint(連線點):程式執行過程中明確的點,一般是方法的呼叫(方法執行前 拋異常 返回資料 )

  3. Advice(通知):AOP在特定的切入點上執行的增強處理,有before,after,afterReturning,afterThrowing,around

  4. Pointcut(切入點):就是帶有通知的連線點,在程式中主要體現為書寫切入點表示式

  5. AOP代理:AOP框架建立的物件,代理就是目標物件的加強。Spring中的AOP代理可以使JDK動態代理,也可以是CGLIB代理,前者基於介面,後者基於子類

4、aop中的五種通知

      // @Before: 前置通知,目標方法執行前執行
      @Before("execution( * com.xingxue.spring.aop.SimpleCaculate.add(..) )")
      public  void  logBefore(JoinPoint point){
          System.out.println(new Date()+"開始執行"+ point.getSignature().getName()+"引數為:"+ Arrays.toString(point.getArgs()));
      }
    //  @ After:後置通知,目標方法無論執行成功與否,都會通知
    @After("execution( * com.xingxue.spring.aop.SimpleCaculate.add(..) )")
    public  void logAfter(JoinPoint point){
        System.out.println(new Date()+"結束執行"+ point.getSignature().getName()+"引數為:"+ Arrays.toString(point.getArgs()));
    }
    //  @AfterReturning:返回通知,目標方法執行沒有異常,返回了資料,會通知
    @AfterReturning(value = "execution( * com.xingxue.spring.aop.SimpleCaculate.add(..) )", returning ="rs")
    public void  logAfterReturn( JoinPoint point ,int rs ){
        System.out.println(new Date()+"返回通知:"+ rs);
    }
    // @AfterThrowing : 異常通知,目標方法執行失敗,丟擲異常則通知
@AfterThrowing(value = "execution( * com.xingxue.spring.aop.SimpleCaculate.add(..) )", throwing ="ex")
    public void  logAfterReturn( JoinPoint point ,Exception ex ){
        System.out.println(new Date()+"異常通知:"+ ex);
    }
//   @Around : 環繞通知,是上述幾個通知的總合,各種通知環繞目標方法周圍
   @Around("execution( * com.xingxue.spring.aop.SimpleCaculate.add(..) )")
   public  Object  arround(ProceedingJoinPoint point ){
       Object rs=null;
        System.out.println("前置通知");
        try {
            rs  =  point.proceed( point.getArgs() );
            System.out.println("返回通知");
​
        } catch (Throwable throwable) {
            System.out.println("異常通知");
            throwable.printStackTrace();
        }
        System.out.println("後置後置");
        return  rs;
    }

JoinPoint :連線點,獲得連線點資訊,如方法簽名,引數 等資訊,ProceedingJoinPoint 也是連線點,但是隻能是環繞通知使用。

5、切點表示式

1、切點表示式

  • 用於表示切入點的表示式, (scope) 包名.類名.方法名(引數型別,...)

  • scope: 方法訪問許可權: public private protected ,通常使用 * 表示任意

  • 包名:不解釋,實際情況下可能是多級,如com.xingxue.service.impl ,通常簡寫 com.xingxue.. ; .. 表示省略多級

  • 類名:不解釋,如 UserService 通常使用 * 表示任意類名 ,com.xingxue.service.*

  • 方法名:不解釋,如save 通常使用 * 表示任意方法名,如 com.xingxue.service.UserService.*

  • 引數:不解釋,如 int,int 通常使用 .. 表示任意多個引數 ,public com.xingxue.service.UserService.save(..)

  • 最終通常寫為 : * com.xingxue.service . . * . * ( . . )

2、切入點函式

中心思想是重用切點表示式,把表示式跟函式進行繫結,函式不需要實現

@Pointcut(value ="execution( * com.xingxue.spring..*.*(..) )")
public static void beforePintcut(){
}
@Before(value = "beforePintcut()")
public  void  logBefore(JoinPoint point){
          System.out.println(new Date()+"開始執行"+ point.getSignature().getName()+"引數為:"+                              Arrays.toString(point.getArgs()));
}

跨類使用 切點

   @Before(value = "com.xingxue.spring.aop.LogAspect.beforePintcut()")
    public  void  numValidate(JoinPoint point){
        Object[]  args=  point.getArgs();
        if( Integer.parseInt(args[0].toString() )  >0 &&  Integer.parseInt(args[1].toString() )  >0   ){
            System.out.println("符合要求");
        }else{
            System.out.println("引數有誤");
        }
    }

6、切面的優先順序

同一個目標切點多個切面,可以配置優先順序,跳轉執行順序 @Order(n) n越小優先順序越高,反之越低。

@Component
@Aspect
@Order(2)
public class LogAspect {
    // 日誌切面
}
@Component
@Aspect
@Order(1)
public class ValidateAspect {
    // 資料驗證切面
}

7、集合log4j處理日誌

log4j依賴

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

 

log4j配置檔案(參考網上)

log4j.rootLogger=debug,stdout,info,debug,warn,error 
#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=d:/logs/log_info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout 
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
  Logger logger = Logger.getLogger(LogAspect.class);
​
  logger.info("xxxx");

 

8、使用xml配置aop

      <!--註冊bean-->
       <bean id="caculate" class="com.xingxue.spring.aopxml.SimpleCaculate"/>
      <!--註冊切面-->
       <bean  id="log"  class="com.xingxue.spring.aopxml.LogAspect"/>
        <!-- aop 配置 -->
       <aop:config>
              <!--配置切點-->
              <aop:pointcut id="pointcut" expression="execution(*  com.xingxue.spring.aopxml..*.*(..))" />
              <!--配置切面  oder:優先順序   ref:引用宣告的切面-->
               <aop:aspect order="1" ref="log" >
                     <!--配置通知,method:切面中的方法   pointcut-ref:引用宣告切點 -->
                    <aop:before method="logBefore" pointcut-ref="pointcut"/>
                    <aop:after method="logAfter" pointcut-ref="pointcut"/>
               </aop:aspect>
       </aop:config>

六、Spring-jdb支援

Spring 對jdbc 提供支援,簡化jdbc開發流程,類似於Dbutils,使用Spring-jdbc 適用於簡單的資料訪問操作,當使用ORM框架對應當前系統比較麻煩的時候,可以採取spring-jdbc。spring-jdbc提供了一個操作資料的簡單工具JdbcTemplate。

使用流程

① 新增依賴

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.14.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.46</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.3.14.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.9</version>
    </dependency>

② 資料庫連線資訊

#mysql connection information
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql:///mybatis
jdbc.driverClassName=com.mysql.jdbc.Driver

③ spring 配置

 
       <!-- 引入外部配置檔案(自動註冊 PropertyPlaceholderConfigurer Bean) -->
        <context:property-placeholder location="classpath*:db.properties"/>
       <!--配置資料來源-->
       <bean  id="dataSource"  class="com.alibaba.druid.pool.DruidDataSource">
               <property name="username" value="${jdbc.user}"/>
               <property name="password" value="${jdbc.password}"/>
               <property name="url" value="${jdbc.url}"/>
               <property name="driverClassName" value="${jdbc.driverClassName}"/>
       </bean>
      <!-- 註冊jdbctemplate -->
      <bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate">
             <property name="dataSource" ref="dataSource"/>
      </bean>
​
 
​

④測試

    ---------------------------查詢多個物件---------------------------
    query(String sql , RowMapper  mapper , Object...args):List<T>
    說明:     sql:  執行的sql語句
            mapper:  對映規則RoMapper是介面,通常使用 BeanPropertyRowMapper 實現,作用是把欄位轉換到                    Bean的屬性
              args:  可變引數,用於替換sql語句中的?,沒有?可不填
    -----------------------------------------------------------------
    @Test
    public  void test(){
        JdbcTemplate tp = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        String sql = "select  *  from  tb_employee ";
        // BeanPropertyRowMapper: 查詢欄位對映到 物件屬性
        List<EmpModel> list = tp.query(sql,  new BeanPropertyRowMapper<>(EmpModel.class) );
        System.out.println(list);
    }
    
   ---------------------------查詢單個物件---------------------------
       queryForObject(String sql , RowMapper  mapper , Object...args):T
    說明:     sql:  執行的sql語句
            mapper:  對映規則RoMapper是介面,通常使用 BeanPropertyRowMapper 實現,作用是把欄位轉換到                    Bean的屬性
              args:  可變引數,用於替換sql語句中的?沒有?可不填
    ------------------------------------------------------------------
    //@Test
    public  void test3(){
        JdbcTemplate tp = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        String sql = "select  *  from  tb_employee where emp_no=? ";
        // BeanPropertyRowMapper: 查詢欄位對映到 物件屬性
        EmpModel  model=  tp.queryForObject(sql,new BeanPropertyRowMapper<>(EmpModel.class),2);
        System.out.println(model);
    }
    
    ---------------------------查詢某個欄位---------------------------
    queryForObject(String sql , Class<T> cls , Object...args):T
    說明:     sql:  執行的sql語句
               cls:  Class 表示類型別,這裡表示需要把查詢的欄位 轉換成 什麼型別返回
              args:  可變引數,用於替換sql語句中的?沒有?可不填
    ------------------------------------------------------------------  
    @Test
    public  void test4(){
        JdbcTemplate tp = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        String sql = "select  count(*) from  tb_employee";
        Integer count=  tp.queryForObject(sql, Integer.class );
        System.out.println(count);
    }
​
    ---------------------------查詢多行記錄 返回List<Map>---------------------------
    queryForList(String sql ,Object...args):List<Map>
    說明:     sql:  執行的sql語句
              args:  可變引數,用於替換sql語句中的?沒有?可不填
    -------------------------------------------------------------------------------
   @Test
    public  void test6(){
        JdbcTemplate tp = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        String sql ="select  *  from tb_employee e, tb_department  d  where e.dept_no = d.dept_no";
        List<Map<String, Object>> maps = tp.queryForList(sql);
        System.out.println(maps);
    }
​
    ---------------------------查詢單行記錄 返回Map---------------------------
    queryForMap(String sql ,Object...args):Map
    說明:     sql:  執行的sql語句
              args:  可變引數,用於替換sql語句中的?沒有?可不填
    -------------------------------------------------------------------------
   @Test
    public  void test6(){
        JdbcTemplate tp = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        String sql ="select  *  from tb_employee e, tb_department  d  where e.dept_no = d.dept_no a                      nd e.emp_no=?";
        List<Map<String, Object>> maps = tp.queryForMap(sql,1);
        System.out.println(maps);
    }
​
 ---------------------------更新操作(insert  delete  update)-------------------------
    update(String sql , Object..args): int
       說明:  sql:  執行的sql語句
              args:  可變引數,用於替換sql語句中的?沒有?可不填
 -------------------------------------------------------------------------------------
    @Test
    public  void test7(){
        JdbcTemplate tp = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        String sql ="insert into tb_employee values(?,?,?,?,?)";
        int row=  tp.update(sql,111,"小東","123323","重慶",11);
        System.out.println(row);
    }
    
​
​
------------------------批量更新操作(insert  delete  update)---------------------------
    batchUpdate(sql,params):int[]
       說明:  sql:  執行的sql語句
              args:  可變引數,用於替換sql語句中的?沒有?可不填
    @Test
    public  void test8(){
        JdbcTemplate tp = (JdbcTemplate) ctx.getBean("jdbcTemplate");
        String sql ="insert into tb_employee values(?,?,?,?,?)";
        List<Object[]> params = new ArrayList<Object[]>();
        params.add(   new Object[]{222,"a","123232","aaaa",11  }    );
        params.add(   new Object[]{333,"a","123232","aaaa",11  }    );
        params.add(   new Object[]{444,"a","123232","aaaa",11  }    );
​
        int[] rows=  tp.batchUpdate(sql,params);
        System.out.println(Arrays.toString(rows));
    }
​

七、Spring事務管理

1、事務概念

事務(Transaction)是併發控制的單位,是使用者定義的一個操作序列。這些操作要麼都做,要麼都不做,是一個不可分割的工作單位。通過事務,資料庫能將邏輯相關的一組操作繫結在一起,以便伺服器保持資料的完整性。

2、事務特點

  1. 原子性(Atomicity):事務是一個原子操作,由一系列動作組成。事務的原子性確保動作要麼全部完成,要麼完全不起作用。

  2. 一致性(Consistency):一旦事務完成(不管成功還是失敗),系統必須確保它所建模的業務處於一致的狀態,而不會是部分完成部分失敗。在現實中的資料不應該被破壞。

  3. 隔離性(Isolation):可能有許多事務會同時處理相同的資料,因此每個事務都應該與其他事務隔離開來,防止資料損壞。

     隔離級別:  
    
                         讀完未提交,讀到被人沒有提交的資料,產生髒讀
    
                         讀已提交,只能讀到已經提交的資料 ,  Oracle採取這個級別,但會出現不可重複讀  
    
                         可重複讀 ,在同一個事務一個事務中,讀取讀取得到一致的效果,MySQL預設的隔離級別
    
                         序列化讀, 當一個事務操作過程中,其他事務等待,序列化讀會鎖表,效率低,資料一致性高。
    
    
    
  4. 永續性(Durability):一旦事務完成,無論發生什麼系統錯誤,它的結果都不應該受到影響,這樣就能從任何系統崩潰中恢復過來。通常情況下,事務的結果被寫到持久化儲存器中。

3、Spring 事務管理

Spring 把事務處理流程做了一致性的抽象(PlatformTransactionManager介面),具體的事務管理交由各種事務管理器提供實現,類似於JDBC介面規範和各種資料庫實現。

 

4、Spring宣告式事務(註解)

①pom依賴

  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>4.3.14.RELEASE</version>
  </dependency>

② 持久層 介面

public interface DeptDao {
     public  Integer add(DeptModel model) throws  Exception;
}
public interface EmpDao {
     public  Integer  add(EmpModel model) throws  Exception;
}
   持久層 實現


@Repository
public class DeptDaoImpl implements DeptDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public Integer add(DeptModel model) throws Exception {
        String sql = "insert into tb_department(dept_no,dept_name,dept_loc) values ( ?, ?, ?)";
       int row= jdbcTemplate.update(sql,model.getDeptNo(),model.getDeptName(),model.getDeptLoc());
        return row;
    }
}
​
@Repository
public class EmpDaoImpl implements EmpDao {
    @Autowired
    private JdbcTemplate  jdbcTemplate;
    @Override
    public Integer add(EmpModel model) throws Exception {
        String sql = "insert into tb_employee values( ?,?,?,?,? )";
        int row=   jdbcTemplate.update(sql, model.getEmpNo(),model.getEmpName(),model.getEmpTel(),model.getEmpAddress(),model.getDeptNo());
        return row;
    }
}

③服務層 介面

public interface EmpService {
    //儲存 員工 
    public Integer   saveEmp(EmpModel empModel, DeptModel deptModel) throws  Exception;
}

④服務層 實現

@Service("empService")
public class EmpServiceImpl  implements EmpService {
    @Autowired
    private EmpDao  empDao;
    @Autowired
    private DeptDao  deptDao;
  
    @Override
    @Transactional
    public Integer saveEmp(EmpModel empModel, DeptModel deptModel) throws Exception {
        int row1=  deptDao.add(deptModel);
        int row2=  empDao.add(empModel);
        return row1+row2;
    }
}

⑤ 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:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
      <!--包掃描-->
      <context:component-scan base-package="com.xingxue.spring"/>
      <!--註解配置-->
      <context:annotation-config/>
      <!-- 引入外部配置檔案(自動註冊 PropertyPlaceholderConfigurer Bean) -->
      <context:property-placeholder location="classpath*:db.properties"/>
      <!--配置資料來源-->
      <bean  id="dataSource"  class="com.alibaba.druid.pool.DruidDataSource">
               <property name="username" value="${jdbc.user}"/>
               <property name="password" value="${jdbc.password}"/>
               <property name="url" value="${jdbc.url}"/>
               <property name="driverClassName" value="${jdbc.driverClassName}"/>
       </bean>
      <!-- 註冊jdbctemplate -->
      <bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate">
             <property name="dataSource" ref="dataSource"/>
      </bean>
      <!-- 配置事務管理器 -->
      <bean id="transactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
             <property name="dataSource" ref="dataSource"></property>
      </bean>
     <!--  開啟事務註解配置 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

 

xml依賴配置

    <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
       <version>1.8.13</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.3.14.RELEASE</version>
    </dependency>

xml 配置

    <!--Aop 宣告式事務配置 -->
    <aop:config>
           <aop:pointcut id="pointcut" expression="execution( *  com.xingxue.spring.service..*.*(..) )"/>
           <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>
    <!--事務通知-->
    <tx:advice transaction-manager="transactionManager" id="txAdvice">
               <tx:attributes>
                   <tx:method name="*"/>
                   <tx:method name="get*" read-only="true"/>
                   <tx:method name="find*" read-only="true"/>
               </tx:attributes>
    </tx:advice>

 

 

 

 

 

 

 

 

 

 

&nb