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 框架主要的好處
-
Spring 可以使開發人員使用 POJOs 開發企業級的應用程式。只使用 POJOs 的好處是你不需要一個 EJB 容器產品,比如一個應用程式伺服器,但是你可以選擇使用一個健壯的 servlet 容器,比如 Tomcat 或者一些商業產品。
-
Spring 在一個單元模式中是有組織的。即使包和類的數量非常大,你只要擔心你需要的,而其它的就可以忽略了。
-
Spring 不會讓你白費力氣做重複工作,它真正的利用了一些現有的技術,像ORM 框架、日誌框架、JEE、Quartz 和 JDK 計時器,其他檢視技術。
-
測試一個用 Spring 編寫的應用程式很容易,因為環境相關的程式碼被移動到這個框架中。此外,通過使用 JavaBean-style POJOs,它在使用依賴注入注入測試資料時變得更容易。
-
Spring 的 web 框架是一個設計良好的 web MVC 框架,它為比如 Structs 或者其他工程上的或者不怎麼受歡迎的 web 框架提供了一個很好的供替代的選擇。
-
Spring 對JavaEE開發中非常難用的一些API(JDBC、JavaMail、遠端呼叫等),都提供了封裝,使這些API應用難度大大降低。
-
輕量級的 IOC 容器往往是輕量級的,例如,特別是當與 EJB 容器相比的時候。這有利於在記憶體和 CPU 資源有限的計算機上開發和部署應用程式。
-
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模組建立在由core和 beans 模組的基礎上建立起來的,它以一種類似於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的幾個概念
-
Aspect(切面):通常是一個類,裡面可以定義切入點和通知
-
JointPoint(連線點):程式執行過程中明確的點,一般是方法的呼叫(方法執行前 拋異常 返回資料 )
-
Advice(通知):AOP在特定的切入點上執行的增強處理,有before,after,afterReturning,afterThrowing,around
-
Pointcut(切入點):就是帶有通知的連線點,在程式中主要體現為書寫切入點表示式
-
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、事務特點
-
原子性(Atomicity):事務是一個原子操作,由一系列動作組成。事務的原子性確保動作要麼全部完成,要麼完全不起作用。
-
一致性(Consistency):一旦事務完成(不管成功還是失敗),系統必須確保它所建模的業務處於一致的狀態,而不會是部分完成部分失敗。在現實中的資料不應該被破壞。
-
隔離性(Isolation):可能有許多事務會同時處理相同的資料,因此每個事務都應該與其他事務隔離開來,防止資料損壞。
隔離級別: 讀完未提交,讀到被人沒有提交的資料,產生髒讀 讀已提交,只能讀到已經提交的資料 , Oracle採取這個級別,但會出現不可重複讀 可重複讀 ,在同一個事務一個事務中,讀取讀取得到一致的效果,MySQL預設的隔離級別 序列化讀, 當一個事務操作過程中,其他事務等待,序列化讀會鎖表,效率低,資料一致性高。
-
永續性(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