<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
安裝JDK、html
設置環境變量:前端
JAVA_HOME = D:\Android\Java\jdk1.8.0_25java
CLASSPATH = .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;mysql
PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;web
開源網站:www.sourceforge.netspring
Shift + Alt + s :能夠調出重構界面,能夠生成實體類sql
安裝TomCat數據庫
CATALINA_HOME = 路徑express
默認訪問地址:http://localhost:8080apache
Java導入普通Web項目
Project Facets:Dynamic - Java - JavaScript
Deployment Assembly:WebRoot
新建項目:mvn archetype:generate
groupid:項目標識(com.chenxy.demo)
artifactId:項目名稱(maven_demo)
pom.xml:定義項目基礎模型
打包: mvn package 會生成jar包。生成在target文件夾裏有jar包
執行jar包:java -cp .\mvndemo-1.0-SNAPSHOT.jar com.chenxy.App
Eclipse設置Maven存儲庫:Windows->Preferences->Maven->Installations添加Maven指向。Maven->User Settings 選擇Maven->conf->setting.xml
設置Maven-JDK:Windows->Preferences->Java->Installed ->Execution。選擇
安裝Eclipse插件
Help->Eclipse Marketplace -> 輸入Maven -> 安裝Maven Intergration for Eclipse
設置Maven倉庫地址
Windows->Preferences->Maven->Installations->Add選擇地址
Windows->Preferences->Maven->User Setting->Add選擇地址
引入Maven項目
import -> Maven -> Existing Maven Project
新建項目
New -> Maven -> Maven Project -> Next -> Filter選擇maven.archetype-quickstart
打包項目
Maven Build -> Goals輸入package
思想:本身維護一些列數據庫連接,須要使用時直接使用其中一個,用完了複用不用銷燬
目前常見技術是:proxool、DBCP、C3PO
proxool是一種JAVA數據庫連接技術,sourceforge下的一個開源項目,提供一個健壯、易用的鏈接池,提供監控功能,方便易用,便於發現泄露的狀況
Apache Maven是一個軟件項目管理和理解工具。基於項目對象模型的概念,Maven能夠生成一箇中心信息管理項目的構建,報告和文檔。
下載Maven後,添加環境變量便可
運行Maven的語法以下
mvn [options] [<goal(s)>] [<phase(s)>]
全部可用選項都記錄在您能夠訪問的內置幫助中
mvn -h
構建Maven項目的典型調用使用Maven生命週期階段,打包完成後就能夠直接調用了
mvn package
mvn package && java -jar target/demo-0.0.1-SNAPSHOT.jar
告訴Maven構建全部模塊,並檢查全部集成測試是否成功。
只須要建立包並將其安裝在本地存儲庫中,便可從其餘項目中重複使用
mvn verify
當不使用項目時,以及在其餘一些用例中,可能但願調用由Maven的一部分實現的特定任務,這稱爲插件的目標
mvn archetype:generate
或
mvn checkstyle:check
Maven中,有構建和報告插件
構建期間將執行構建插件,而後在<build>元素中配置它們。
報告插件將在站點生成期間執行,而且應在<reporting>元素中配置。
全部插件都必需信息:groupId,artifactId,version
建議始終定義構建使用的每一個插件版本,以保證構建的可重現性。推薦作法是在每一個構建插件的build中指定它們。對於報告插件,應該在reporting中指定每一個版本
使用<executions>
經常使用於在參與構建生命週期的某些階段的配置。
若是目標具備默認階段綁定,則它將在該階段執行。若是沒有綁定多任何生命週期階段,那麼它就不會在構建生命週期中執行。
若是有多個綁定到不一樣階段的執行,則對於指示的每個階段執行一次。
<project> ... <build> <plugins> <plugin> <artifactId>maven-myquery-plugin</artifactId> <version>1.0</version> <executions> <execution> <id>execution1</id> <phase>test</phase> <configuration> <url>http://www.foo.com/query</url> <timeout>10</timeout> <options> <option>one</option> <option>two</option> <option>three</option> </options> </configuration> <goals> <goal>query</goal> </goals> </execution> <execution> <id>execution2</id> <configuration> <url>http://www.bar.com/query</url> <timeout>15</timeout> <options> <option>four</option> <option>five</option> <option>six</option> </options> </configuration> <goals> <goal>query</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ... </project>
使用<dependencies>
配置Build插件的依賴項,一般使用更新的依賴項版本。整個項目所須要的jar包
例如:Maven Antrun插件版本1.2使用Ant版本1.65,若是想在運行此插件時使用最新的Ant版本,則須要添加<dependencies>元素
<project> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.2</version> ... <dependencies> <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant-launcher</artifactId> <version>1.7.1</version> </dependency> </dependencies> </plugin> </plugins> </build> ... </project>
使用<inherited>
默認狀況下,插件配置應該傳播到子POM,所以要中斷繼承,可使用inherited標記
<project> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.2</version> <inherited>false</inherited> ... </plugin> </plugins> </build> ... </project>
建立項目
若是須要在某個地方建立項目,須要建立一個目錄了並在該目錄中啓動一個sheel。執行下面的命令
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
首次安裝Maven,會有點慢由於會更新jar包和其餘文件到本地存儲庫中。
POM
pom.xml的文件是Maven中項目的配置核心。它是一個單一的配置文件,包含以您但願的方式構建項目所需的大部分信息。POM是巨大的,其複雜度很高,但沒有必要了解全部
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mvndemo.app</groupId> <artifactId>maven-app</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>maven-app</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
project:全部Maven pom.xml文件中的頂級元素。
modelVersion:元素指示此POM使用的對象模型的版本。
groupId:元素指示建立項目的組織或組的惟一標識符。關鍵標識符之一,一般基於組織的徹底限定域名
artifactld:元素指示此項目生成的惟一基本名稱。項目的主要生成是JAR文件。
version:生成項目的版本
packaging:指項目主要使用的包的類型(例如JAR、WAR等)還能夠指示要在構建過程當中使用的特定的生命週期
name:項目顯示名稱,一般用於Maven生成的文檔中
url:項目站點位置,一般用於Maven生成的文檔中
description:項目的基本描述,一般用於Maven生成的文檔中
完整描述參考:https://maven.apache.org/ref/3.5.4/maven-model/maven.html
打包項目
mvn package
構建生命週期的階段,Maven將執行序列中每一個階段
打包完成後可使用java命令進行測試
java -cp target/maven-app-1.0-SNAPSHOT.jar com.mvndemo.app.App
Maven階段
默認生命週期階段
階段實際上映射到基本目標。每一個階段執行的具體目標取決於項目的包裝類型
mvn clean dependency:copy-dependencies package
此命令將清理項目,複製依賴項並打包項目
mvn clean compile
清理輸出目錄target/
mvn site
生成信息站點
編譯應用程序
mvn compile 編譯Maven項目
第一次執行命令時,須要下載全部插件和依賴項。再次執行,不須要下載。
編譯後的類放在target/class中。
運行單元測試
mvn test 運行
mvn test-compile 編譯不運行
SNAPSHOT版本
pom.xml文件中version標記的後綴:-SNAPSHOT。該值指的是沿着一個發展分支的最新代碼,而且不保證該代碼是穩定的或不變的。發佈版本中沒有這個後綴都是不變的。
也就是開發版本的意思
添加JAR包到資源
經過使用標準的Maven約定,能夠簡單的將資源放在目錄結構中封裝JAR包
my-app
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- com
| | `-- mycompany
| | `-- app
| | `-- App.java
| `-- resources
| `-- META-INF
| `-- application.properties
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
上面示例中咱們添加了目錄${basedir}/src/main/resources,將要包裝的任何資源放入JAR包中。Maven採用的簡單規則是:${basedir}/src/main/resources目錄下的任何目錄或文件都打包在JAR中,其結構與JAR基礎相同。
文件結構示例中,咱們在該目錄中有一個帶有application.properties文件的META-INF目錄。
|-- META-INF
| |-- MANIFEST.MF
| |-- application.properties
| `-- maven
| `-- com.mycompany.app
| `-- my-app
| |-- pom.properties
| `-- pom.xml
`-- com
`-- mycompany
`-- app
`-- App.class
application.properties文件位於META-INF目錄中。還會注意到其餘一些文件,如META-INF/MAINFEST.MF以及pom.xml和pom.properties文件。這些標準在Maven中生成JAR的標準。
能夠建立本身的清單,但若是不這樣作,Maven將生成默認的清單。
在POM文件上操做須要使用一些Maven實用程序,但可使用標準的Java API來使用這些屬性
#Generated by Maven
#Tue Oct 04 15:43:21 GMT-05:00 2005
version=1.0-SNAPSHOT
groupId=com.mycompany.app
artifactId=my-app
將資源添加到單元測試的類路徑中,將遵循與JAR添加資源相同的模式,除了放置資源的目錄是${basedir}/src/main/resources。此時將擁有一個項目目錄結構
my-app
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- com
| | `-- mycompany
| | `-- app
| | `-- App.java
| `-- resources
| `-- META-INF
| |-- application.properties
`-- test
|-- java
| `-- com
| `-- mycompany
| `-- app
| `-- AppTest.java
`-- resources
`-- test.properties
單元測試中,可使用下面所示的簡單代碼片斷來訪問測試所需的資源
InputStream is = getClass().getResourceAsStream( "/test.properties" );
過濾資源文件
資源文件須要包含只能在構建時提供的值。使用語法${<property name>}將包含值的屬性引用到資源文件中。該屬性能夠是pom.xml中定義的值之一,用戶settings.xml中定義的值,外部屬性文件中定義的屬性或系統屬性。
在複製時讓Maven過濾資源,給pom.xml中的資源目錄設置 filtering 爲true
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build>
使用外部依賴項
pom中 dependencies部分列出了咱們項目構建所需的全部外部依賴項。好比下面代碼
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>Maven Quick Start Archetype</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> </project>
對於每一個外部依賴項,至少須要4個內容:groupId,artifactid,version,scope。
groupId和artifactId,version須要與項目給出的相同。scope元素指示項目如何使用該依賴項,能夠是compile,test,runtime之類的值。
Maven會在構建項目時引用依賴關係,查找本地存儲庫以查找全部依賴項。因此能夠將項目安裝到本地存儲庫。而後另外一個項目就能夠經過依賴關係添加到pom.xml來引用該jar包
<dependency> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency>
當項目引用本地存儲庫中不可用的依賴時,Maven就會將依賴項從遠程庫下載到本地庫。
可使用本身的遠程存儲庫來代替默認遠程存儲庫或使用默認遠程存儲庫。
遠程部署
要將jar包部署到外部存儲庫,必須在pom.xml中配置存儲庫URL,並在settings.xml中配置連接到存儲庫的身份驗證信息
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>Maven Quick Start Archetype</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.codehaus.plexus</groupId> <artifactId>plexus-utils</artifactId> <version>1.0.4</version> </dependency> </dependencies> <build> <filters> <filter>src/main/filters/filters.properties</filter> </filters> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> <distributionManagement> <repository> <id>mycompany-repository</id> <name>MyCompany Repository</name> <url>scp://repository.mycompany.com/repository/maven2</url> </repository> </distributionManagement> </project>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> ... <servers> <server> <id>mycompany-repository</id> <username>jvanzyl</username> <!-- Default value is ~/.ssh/id_dsa --> <privateKey>/path/to/identity</privateKey> (default is ~/.ssh/id_dsa) <passphrase>my_key_passphrase</passphrase> </server> </servers> ... </settings>
若是要連接到sshd_confing中將參數PasswordAuthentication設置爲no的openssh,則每次都須要輸入密碼進行用戶名/密碼驗證。
Maven Install
Maven build Goals 輸入 clean compile package
target 裏面就是jar包
執行jar包:java -jar 包名
pom.xml 須要添加主清單
<build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <appendAssemblyId>false</appendAssemblyId> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.java24hours.log4j_demo</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>assembly</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
#
Java沒有屬性這個概念
註釋方法以下:/** 測試 */ 。 註釋內容會在引用時顯示
設置快捷鍵:Windows-Preferences-General-Keys
類:class 抽象類:abstract class 接口:interface
繼承函數:@Override extends:繼承類
基元類型
bool:boolean byte:Byte char:Character double:Double
float:Float int:Integer long:Long short:Short
Net-String.format 等價 Java-MessageFormat.format
排序:Collections.sort
Java是典型的靜態語言,當數組被初始化以後,長度是不可變的。數組必須通過初始化纔可以使用。初始化就是分配內存空間,並設置初始值。
初始化有兩種方式:
一旦初始化完成,長度不可變。
//靜態初始化
String[] books = {"A", "B", "C"};
//動態初始化
String[] books2 = new String[3];
動態初始化時,系統回根據數據類型進行分配初始值。
注意:不要同時使用靜態和動態初始化。
Java的數組變量是引用類型的變量,它並非數組對象自己,只要讓數組變量指向有效的數組對象,便可使用該數組變量。
數組變量知識一個引用變量、一般存放在棧內存中。而數組對象就是保存在堆內存中的連續內存空間。
對數組執行初始化,並非對數組變量執行初始化,而是對數組對象執行初始化。對於數組變量來講,並不須要進行初始化,只要指向一個有效的數組對象便可。
全部局部變量都是存放在棧內存裏保存的,無論是基本類型、仍是引用類型,都是存放在各自方法的棧區。但引用類型變量所引用的對象,則老是存儲在堆內存中。
引用變量本質上只是一個指針,只要經過引用變量訪問屬性,或者調用方法。該引用變量就會由它所引用的對象代替。
#
繼承JFrame,import javax.swing.*;
構造函數必須執行幾種操做
1. 調用超類構造
2. 設置框架的標題
3. 設置框架的大小
4. 設置框架的外觀
5. 定義用戶關閉框架時應執行的操做
設置標題
super("My Main Frame");
setTitle("My Main Frame");
設置大小
super.setSize(350,125);
pack();
設置關閉標識,定義單擊事件
super.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
EXIT_ON_CLOSE:按鈕被單擊時退出程序
DISPOSE_ON_CLOSE:關閉框架,同時繼續運行應用程序
DO_NOTHING_ON_CLOSE:保持框架爲打開狀態並繼續運行
HIDE_ON_CLOSE:關閉框架並繼續運行
設置外觀
try {
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
顯示框架
setVisible(true);
添加組件
add(組件)
按鈕
JButton對象是可單擊的按鈕,能夠是文本、圖像或二者組合
JButton okButton = new JButton("ok");
add(okButton);
佈局
添加組件時,不須要指明組件顯示位置,佈局由佈局管理器的對象決定。
FlowLayout flo = new FlowLayout();
setLayout(flo);
標籤
JLabel pageLabel = new JLabel("Web page");
add(pageLabel)
文本
JTextField page = new JTextField(20);
add(page);
int參數爲寬度字符,page.getText()能夠獲取文本,page.setText()能夠設置文本
複選框
JCheckBox jumbo = new JCheckBox("Jumbo Size");
FlowLayout flo2 = new FlowLayout();
setLayout(flo2);
add(jumbo);
組合框
JComboBox profession = new JComboBox<>();
profession.addItem("A");
profession.addItem("B");
add(profession);
文本區域
JTextArea area = new JTextArea(8,40);
add(area);
面板
JPanel topRow = new JPanel();
add(topRow);
鍵值對
HashMap 非線程安全 TreeMap 線程安全
線程
Thread.sleep() 暫停
建立線程:須要繼承Runnable接口,實現run方法,new Thread(this).start() 啓動
匿名內部類
public void Go() { new Thread(new Runnable() { public void run() { try { Thread.sleep(2000); System.out.println("線程暫停"); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); }
package chenxy.spring.demo; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class ChenxySpringApplicationTests { @Test public void PostCreateTest() { assertNotNull(PostsqlDataSource.create()); } }
測試類中,須要使用註解來代表那些是測試方法。例如@Test是必須的註解。
某些方法的前面有@Before、@Ignore等,就是JUnit的註解,以@做爲開頭
assertEquals是Assert靜態方法。包含了一組靜態的測試方法,用於指望值和實際值的對比是否正確。
下面補充一些 JUnit 的註解。
下面簡單介紹一下上邊用到的靜態類 junit.framework.Assert。該類主要包含七個方法:
@Test public void PostCreateTest() { List mock = Mockito.mock(List.class); when(mock.get(0)).thenReturn(1); assertEquals( "預期返回1", 1, mock.get( 0 ) ); }
Mock能夠直接模式生成對象的操做。
上面代碼是模式List對象,擁有List的全部方法和屬性。when().thenReturn指定當執行了這個方法的時候,返回thenReturn的值。
注意:對於static和final方法,沒法模擬。當連續使用時,只會用最新一次
還能模擬異常拋出
when(i.next()).thenThrow(new RuntimeException());
#
Srping Framework是一個Java平臺框架,爲開發Java應用程序的全面基礎架構提供支持。Spring處理基礎架構,以便你能夠專一於應用程序開發。
Spring Boot旨在儘量快的啓動和運行,只須要最少的Spring前端配置。對構建生產就緒應用持批評態度
Spring Cloud直接基於Spring Boot的創新企業Java方法,經過實施通過驗證的模式簡化分佈式爲服務架構,帶來彈性、可靠性、協調性
Spring Boot文檔:https://spring.io/projects/spring-boot
Spring 項目教程:https://spring.io/guides
Ioc(控制反轉)也稱依賴注入(DI)。能夠經過構造函數參數,工廠方法參數或工廠方法構造或返回的對象實例上設置的屬性來定義它們的依賴關係。而後在建立bean時注入這些依賴項。
這個過程基本上是經過使用類的直接構造或服務定位器模式之類的機制來控制其依賴關係的實例化或位置的逆。
org.springframework.beans|context包是Spring框架的Ioc容器的基礎。
ApplicationContext是一個子界面的BeanFactory
Spring中,構成應用程序主幹而且由Spring IOC容器管理的對象稱爲bean。是一個由Spring Ioc容器實例化、組裝和管理的對象。
不然bean只是應用程序中衆多對象之一。Bean及其之間的依賴關係反映在容器使用的配置元數據中。
ApplicatonContext接口表示Spring IOC容器。經過讀取配置元數據獲取有關要實例化、配置和組裝的對象的指令。
配置元數據以XML、Java註釋或Java代碼表示。
Spring提供了幾種接口實現。一般會建立ClassPathXmlApplicationContext或FileSystemXmlApplicationContext
大多數應用中,不須要顯示用戶代碼來實例化Spring IOC容器的一個或多個實例。
在Web應用場景中,應用程序文件中的web.xml就能夠加載
Spring Ioc容器使用一種配置元數據。告訴應用程序中實例化,配置和組裝對象
Spring配置由容器必須管理的至少一個且不止一個bean定義組成。XML配置中bean配置爲<bean />位於頂級元素<beans />元素中
@Bean一般在@Configuration類中使用註釋方法
基本結構以下,id屬性是標識單個bean定義的字符串,class屬性是徹底限定的類名
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> </beans>
提供ApplicationContext構造函數的位置路徑是資源字符串,容許容器從各類外部資源加載配置元數據CLASSPATH
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
services.xml配置以下
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="itemDao" ref="itemDao"/> <!-- additional collaborators and configuration for this bean go here --> </bean>
daos.xml配置以下
<bean id="accountDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"> <!-- additional collaborators and configuration for this bean go here --> </bean>
ref元素指的是另外一個bean定義的名稱。元素id和ref元素之間的這種聯繫表達了協做對象之間的依賴關係。
讓bean定義跨越多個XML文件會頗有用。每一個單獨的XML配置文件都表明架構中的邏輯層或模塊。
使用應用程序上下文構造函數從全部這些XML片斷加載bean定義。使用一個或多個<import />元素來從另外一個或多個文件加載bean定義。
<beans> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/> </beans>
ApplicationContext是高級工廠接口,可以維護不一樣的bean及其依賴項的註冊表。經過getBean方法能夠檢索Bean實例
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); PetStoreService service = context.getBean("petStore", PetStoreService.class);
實際上,您的應用代碼根本不該該調用getBean方法,所以不依賴於Spring API。容許您經過元數據聲明對特定bean的依賴性。
Spring Ioc容器管理一個或多個bean,是使用體工給容器的配置元數據建立的。
每一個bean都有一個或多個標識符。必須是惟一的。
可使用id屬性,name屬性來定義bean標識符。若是不提供name或id,會自動生成。可是若是要使用ref元素,則必須體工。
命名約定小字母開頭,駝峯。accountManger,accountService
引入別名:<alias />
<alias name="fromName" alias="toName"/>
class屬性中是實例化的對象類型。能夠經過如下Class兩種方式之一使用該屬性
使用構造函數實例化
須要一個默認空構造函數。使用XML配置元數據
<bean id="exampleBean" class="examples.ExampleBean"/> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
使用靜態工廠方法
使用該class屬性指定包含static工廠方法的類和factory-method指定工廠方法自己名稱的屬性。可以調用方法並返回一個活動對象,將其視爲構造函數建立的對象。bean定義的一個用途是static在遺留代碼中調用工廠。
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
類代碼
public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {} public static ClientService createInstance() { return clientService; } }
使用實例工廠方法實例化
使用工廠方法實例化會從容器調用現有bean的非靜態方法來建立。要使用此機制,將class屬性保留爲空,並在factory-bean屬性中指定當前bean的名稱
<bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --> </bean> <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
類代碼
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); public ClientService createClientServiceInstance() { return clientService; } }
一個工廠類也能夠包含多個工廠方法
<bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --> </bean> <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/> <bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/> public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; } }
依賴注入是一個過程,對象只能經過構造函數參數,工廠方法的參數構造對象實例後在對象實例上設置的屬性來定義他們的依賴關係。
使用DI原則的代碼更清晰,當對象提供其依賴項時,解耦更有效。該對象不查找其依賴項,也不知道依賴項的位置或類。類變得更容易測試,特別時當依賴關係在接口或抽象基類上時,容許單元測試使用mock
須要使用<constructor-arg />元素顯示定義構造函數參數索引或類型
<beans> <bean id="thingOne" class="x.y.ThingOne"> <constructor-arg ref="thingTwo"/> <constructor-arg ref="thingThree"/> </bean> <bean id="thingTwo" class="x.y.ThingTwo"/> <bean id="thingThree" class="x.y.ThingThree"/> </beans>
當引用另外一個bean時,類型是已知的,能夠進行匹配。
當使用簡單類型時,Spring沒法肯定值的類型,所以沒法在沒有幫助的狀況下按類型進行匹配。
public class ExampleBean { // Number of years to calculate the Ultimate Answer private int years; // The Answer to Life, the Universe, and Everything private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
上面的構造是基元類型,就須要type屬性
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
可使用index屬性顯示指定構造函數參數的索引
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
可使用名稱
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
必須在啓用調試標誌的狀況下編譯代碼,以即可以從構造函數中查找參數名稱。
調用無參數構造函數或無參數static工廠方法來實例化bean以後,基於setter的DI由bean上的容器調用setter方法完成。
能夠緩和基於構造函數和基於setter的DI,所以將構造函數用於強制依賴項和setter方法或可選依賴項的配置方法是一個很好的經驗法則。
setter方法上面使用@Required註釋可用於使屬性成爲必須的依賴。
Spring團隊提倡構造函數注入。
循環依賴
若是使用構造函數注入,則能夠建立沒法解析的循環依賴關係場景。
例如:A注入須要B實例,B注入須要A實例。此時就須要setter注入
使用<property> 能夠設置setter的DI
<bean id="exampleBean" class="examples.ExampleBean"> <!-- setter injection using the nested ref element --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- setter injection using the neater ref attribute --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
對應Java代碼
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } }
使用static工廠方法來返回對象的實例
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg ref="anotherExampleBean"/> <constructor-arg ref="yetAnotherBean"/> <constructor-arg value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> public class ExampleBean { // a private constructor private ExampleBean(...) { ... } // a static factory method; the arguments to this method can be // considered the dependencies of the bean that is returned, // regardless of how those arguments are actually used. public static ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // some other operations... return eb; } }
能夠將bean屬性和構造函數參數定義爲對其餘託管bean的引用,或內聯
value屬性能夠填寫人類可讀的字符串
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
還能夠配置java.util.Properties實例
<bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- typed as a java.util.Properties --> <property name="properties"> <value> jdbc.driver.className=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb </value> </property> </bean>
Spring容器經過使用JavaBeans機制將value元素呢ide文本轉換爲實例。
idref元素
防錯方法,能夠將id容器中另外一個bean傳遞給構造屬性或setter。注意是字符串而不是引用
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean"/> </property> </bean>
其餘bean
能夠將bean的屬性指定爲另外一個對象的引用。使用<ref />
<bean id="accountService" <!-- bean name is the same as the parent bean --> class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref parent="accountService"/> <!-- notice how we refer to the parent bean --> </property> <!-- insert other configuration and dependencies as required here --> </bean>
經過parent屬性指定目標bean會建立當前容器的父容器中的bean的引用。
內部bean
內部定義不須要ID或名稱。始終是匿名的沒法外部建立
<bean id="outer" class="..."> <!-- instead of using a reference to a target bean, simply define the target bean inline --> <property name="target"> <bean class="com.example.Person"> <!-- this is the inner bean --> <property name="name" value="Fiona Apple"/> <property name="age" value="25"/> </bean> </property> </bean>
集合
List類型,使用<list />
<property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource" /> </list> </property>
Set類型,使用<set />
<property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource" /> </set> </property>
Map類型,使用<map />
<property name="someMap"> <map> <entry key="an entry" value="just some string"/> <entry key ="a ref" value-ref="myDataSource"/> </map> </property>
Propereties類型,使用<props />
<property name="adminEmails"> <props> <prop key="administrator">administrator@example.org</prop> <prop key="support">support@example.org</prop> <prop key="development">development@example.org</prop> </props> </property>
Spring支持合併集合,可讓父子類進行合併。
<beans> <bean id="parent" abstract="true" class="example.ComplexObject"> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.com</prop> <prop key="support">support@example.com</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="adminEmails"> <!-- the merge is specified on the child collection definition --> <props merge="true"> <prop key="sales">sales@example.com</prop> <prop key="support">support@example.co.uk</prop> </props> </property> </bean> <beans>
子集合設置繼承父類全部屬性元素。
能夠標記延遲初始化,在第一次請求時建立
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
lazy在ApplicationContext啓動時不會急切地預先實例化。還能夠經過default-lazy-init上的屬性來控制容器級別的延遲初始化
<beans default-lazy-init="true">
Spring容器能夠自動鏈接協做bean之間的關係。可讓Spring經過檢查bean的內容自動爲您的bean解析其餘bean。
使用基於XML的配置元數據,可使用元素autowire屬性爲bean定義指定autowire模式<bean />
自動裝配功能由四種模式。能夠指定每一個bean的自動裝配。
須要在屬性上面添加@Autowired,接口實現須要添加@Component
建立bean定義時,能夠規定對象的範圍。Spring支持六個範圍。使用scope能夠設定。
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
singleton:單例bean的一個共享實例,實例存儲在此類單例bean的緩存中。
prototype:原型範圍每次發送請求都建立新的bean
request:每次發出對特定bean的請求時都建立新的實例
session:基於ApplicationContext
application:基於ApplicationContext
websocket:基於ApplicationContext
生命週期
與容器的bean生命週期管理進行交互。容器調用afterPropertiesSet,destroy讓bean在初始化和銷燬bean時執行某些操做。
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean { public void init() { // do some initialization work } }
銷燬
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean { public void cleanup() { // do some destruction work (like releasing pooled connections) } }
基於註釋的配置提供了XML設置的替代方案,該配置依賴於字節碼元數據來鏈接組建而不是括號聲明。
使用此配置,須要在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.xsd"> <context:annotation-config/> </beans>
context:annotation-config 僅查找在定義它的同一應用程序上下文中的bean上的註釋。
@Required
該註釋適用於bean屬性setter方法。此註釋必須配置bean中顯示屬性值或自動裝配。
@Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }
@Autowired
能夠用於構造函數、setter方法、方法注入、字段。
實現接口須要@Component 或 @Service
@Primary
自動裝配能夠會致使多個候選人,可使用@Primary註釋。優先選擇特定的bean。須要@Bean、@Primary
實現接口中須要@Primary
字段中同時須要添加
@Qualifier
能夠將限定符值與特定參數相關聯,縮小類型匹配集
實現接口中@Component("main")
字段添加@Qualifier("main")
@PostConstruct和@PreDestroy
初始化時執行、銷燬時執行
public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... } }
@Component:任何Spring管理組建的通用構造型式。能夠用來註解你的組件類
@ComponentScan:自動檢測並加載
@ComponentScan(basePackages = "org.example")
或者使用XML配置
<context:component-scan base-package="org.example"/>
@Component
,@Repository
,@Service:分別表明表示層、數據層、邏輯層
Spring3.0開始,提供依賴注入支持。可使用javax.inject
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
用@Inject 替換 @Autowired。一樣能夠在字段級別、方法級別、構造參數級別注入。
@Configuration註釋類,@Bean註釋方法
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
Environment接口是繼承在容器模型應用環境的兩個關鍵方面的抽象
Environment與屬性相關的對象的做用是爲用戶提供方便的服務接口,用於配置屬性源和從中解析屬性
PropertySource抽象化
Spring的Environment抽象提供了對可配置的屬性源層次結構的搜索操做。
@PropertySource註解提供便利和聲明的機制添加PropertySource到Spring的Environment
.properties包含鍵值對的文件,使用此註解能夠直接訪問
helloentity.name = Chenxy
helloentity.arg = 24
@Configuration @PropertySource("application.properties") public class HelloEntity { @Autowired Environment env; public String HelloEntity() { return env.getProperty("helloentity.name"); } public String name; public Integer arg; }
能夠在ApplicationContext使用ContextLoaderLister進行註冊
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
監聽器檢查contextConfigLocation參數。若是不存在默認applicationContext.xml。
使用Spring Boot會自動加載,因此不須要這步操做。若是使用Spring MVC須要在web.xml中加入
Spring的Resource接口是一個更強大的接口,用於抽象對低級資源的訪問
public interface Resource extends InputStreamSource { boolean exists(); boolean isOpen(); URL getURL() throws IOException; File getFile() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); }
Resource界面中一些最重要的方法是:
其餘方法容許獲取資源的實際URL或File對象
UrlResource
包裝java.net.URL,能夠用於訪問URL的任務對象。例如文件、HTTP、FTP。全部URL都具備標準化String表示。
ClassPathResource
表示從類路徑獲取的資源。使用線程上下文類加載器,給定的類加載器或給定的類來加載資源。
FileSystemResource
支持做爲一個File和一個解決方案URL
ServletContextResource
用於ServletContext解釋相關Web應用根目錄的相對路徑。支持流訪問和URL訪問
接口能夠返回Resource實例的對象實現
全部應用程序上下文都實現了該接口。可使用全部應用程序上下文來獲取Resource實例
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
#
簡稱SPEL,一種強大的表達式語言,支持在運行時查詢和操做對象圖。語言相似於Unified EL,提供了其餘功能,最有名的是方法調用和基本字符串模板功能。
評估
用於評估文字字符串表達式的SpEL
ExpressionParser parser = new SpelExpressionParser(); // 拼接 Expression exp = parser.parseExpression("'Hello World'.concat('!')"); String message = (String) exp.getValue(); // 調用Bytes Expression exp = parser.parseExpression("'Hello World'.bytes"); byte[] bytes = (byte[]) exp.getValue(); // 文字長度 Expression exp = parser.parseExpression("'Hello World'.bytes.length"); int length = (Integer) exp.getValue(); // 轉大寫 Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); String message = exp.getValue(String.class);
SpEL更常見的用法是提供針對特定對象實例計算的表達式字符串。如下示例顯示如何name從Inventor類的實例檢索屬性或建立布爾條件
GregorianCalendar calendar = new GregorianCalendar(); calendar.set(1856, 7,9); Inventor tesla = new Inventor("Nikola Tesla", calendar.getTime(), "Serbian"); ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("name"); String name = (String)expression.getValue(tesla);
expression = parser.parseExpression("name == 'Chenxy'");
boolean result = expression.getValue(tesla, Boolean.class);
Inventor類是一個實體類,裏面有字段叫name;能夠直接動態獲取屬性內容,進行布爾計算
使用標註運算符能夠支持關係表達式
// evaluates to true boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class); // evaluates to false boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
每一個符號運算符能夠指定純字母等價運算符
文本運算符不區分大小寫
SpEL支持如下邏輯運算符
面向切面編程(AOP)經過提供另外一種思考程序結構的方式來補充面向對象編程(OOP)。
OOP中模塊化的關鍵單元是類,AOP中模塊化單元是切面。切面實現了跨越多種類型和對象的關注點的模塊化。能夠進行跨越服務關聯,將影響了多個類的公共行爲封裝到一個可重用模塊。
Spring的一個關鍵組件是AOP框架。雖然Spring Ioc容器不依賴於AOP,可是提供了很是強大的中間件解決方案。
AOP在Spring Framework中用於
介紹一些AOP概念和術語
Spring AOP包括如下類型的建議
圍繞通知是最普通的通知,因爲Spring AOP提供了全方位的通知類型,所以建議使用能夠實現所需行爲的最具體的通知類型。
例如只須要使用方法的返回值更新緩存,那麼最好實現返回後的通知而不是圍繞的通知,通過能夠完成一樣的事情,可是使用最具體的通知類型能夠提供更簡單的編程模型,減小BUG的可能性。
由切入點匹配的鏈接點的概念是AOP的關鍵,將其與僅提供攔截的舊技術區分開來。切入點使得通知能夠獨立於面向對象的層次結構進行定向。
能夠將一個提供聲明性事務管理的通知應用於跨越多個對象的一組方法
Spring AOP是用Java實現的。不須要特殊的編譯過程。Spring AOP不須要控制類加載器層次結構,所以適合在servlet容器或應用程序服務器中使用。
Spring AOP目前僅支持方法執行鏈接點。雖然能夠在不破壞核心Spring AOP的狀況下添加對字段攔截的支持,但未實現字段攔截。若是須要通知字段訪問和更新鏈接點,考慮使用AspectJ
Spring框架的核心原則在於非侵入性,不該該被迫在您的業務或模型中引入特定於框架的類和接口。SpringFramework確實爲您提供了將Spring Framework特定的依賴項引入代碼庫選項。爲您提供此類選項的基本原理是,在某些狀況下,這種方式閱讀或編寫某些特定功能可能更容器。
Spring AOP默認使用AOP代理的標準JDK動態代理。使得任何藉口均可以被代理
Spring AOP也可使用CGLIB代理。這是代理類而不是接口所必須的。若是業務對象未實現接口,則使用CGLIB。因爲優化的作法是編程藉口而不是類,業務類一般實現一個或多個業務接口。
@AspectJ支持
@AspectJ指的是將方面聲明爲使用註釋的常規Java類的樣式。
Spring使用AspectJ提供的庫來解釋與AspectJ5相同的註釋,用於切入點分析和匹配。
但AOP運行時仍然是純Spring AOP,而且不依賴與AspectJ編譯器
啓用支持
Spring配置中使用@AspectJ方面,須要啓用Spring支持,以基於@AspectJ方面配置Spring AOP,並根據這些方面是否通知自動代理bean。經過自動代理,若是Spring肯定bean被一個或多個切面通知,會自動爲該bean生成一個代理來攔截方法調用,並確保根據須要執行通知。
Java配置啓用@AspectJ支持
要讓Java啓動@AspectJ支持,須要在@Configration中添加@EnableAspectJAutoProxy註釋
@SpringBootApplication @EnableAspectJAutoProxy public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
要使用XML配置,須要使用
<aop:aspectj-autoproxy/>
聲明一個切面
啓用支持狀況下,應用程序上下文中定義的任何bean都具備@AspectJ方面的類,Spring會自動檢測並用於配置Spring AOP。切面須要加入@Compont
常規bean定義
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"> <!-- configure properties of the aspect here --> </bean>
註釋定義
package org.xyz; import org.aspectj.lang.annotation.Aspect; @Aspect public class NotVeryUsefulAspect { }
須要在Maven中安裝 aspect 包
切面能夠有方法和字段,與其餘類相同。還能夠包含切入點,通知、引入聲明
@Aspect 不支持自動掃描,須要單獨添加@Component註釋
聲明切入點
切入點肯定須要的鏈接點,從而使咱們可以控制通知什麼時候執行。
Spring AOP僅支持Spring bean的方法執行鏈接點,能夠將切入點視爲匹配Spring bean上方法的執行。
切入點聲明有兩個部分:
AOP的@AspectJ註釋樣式中,切入點簽名由常規方法定義提供,並使用@Pointcut註釋指示切入點表達式
下面定義了一個切入點anyOldTransfer
@Pointcut("execution(* transfer(..))")// 表達式 private void anyOldTransfer() {}// 簽名
造成@Pointcur註釋值的切入點表達式是常規的切入點表達式。
支持的切入點指示符
Spring AOP支持如下AspectJ切入點指示符(PCD)用於切入點表達式
結合Pointcut表達式
能夠組合切入點表達式,可使用&&,||,!。還能夠按名稱引用切入點表達式
// 若是方法執行鏈接點表示任何公共方法的執行 @Pointcut("execution(public * *(..))") private void anyPublicOperation() {} // 若是方法執行在trading方法中則執行 @Pointcut("within(com.xyz.someapp.trading..*)") private void inTrading() {} // 若是方法執行模塊中的任何公共方法,則匹配 @Pointcut("anyPublicOperation() && inTrading()") private void tradingOperation() {}
從較小的命名組件構建更復雜的切入點表達式。當按名稱引入切入點時,將應用常規Java可見性規則。不會影響切入點匹配
共享公共切入點定義
使用企業應用程序時,一般但願從幾個方面引用應用程序的模塊和特定的操做機。
建議定義一個SystemArchitecture 切面,捕獲常見的切入點表達式。
@Aspect public class SystemArchitecture { /** * 若是方法是在 com.xyz.someapp.web 包中的類型中定義的, * 或者是在該包下的任何子包中定義的, 則鏈接點位於 web 層中。 * */ @Pointcut("within(com.xyz.someapp.web..*)") public void inWebLayer() { } @Pointcut("within(com.xyz.someapp.web..*)") public void inServiceLayer() { } @Pointcut("within(com.xyz.someapp.dap..*)") public void inDataAccessLayer() { } }
能夠在須要切入點表達式的任何位置引用此類方面中定義的切入點。例如,要使用服務層稱爲事務性的,能夠編寫如下內容
<aop:config> <aop:advisor pointcut="com.xyz.someapp.SystemArchitecture.businessService()" advice-ref="tx-advice"/> </aop:config> <tx:advice id="tx-advice"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
例子
Spring AOP用戶可能execution最常使用切入點指示符。執行表達式的格式以下
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
除了ret-type-pattern返回類型模式,名稱模式和參數模式以外的全部部分都是可選擇。返回類型模式肯定方法的返回類型必須是什麼才能匹配鏈接點。
* 最經常使用做返回類型模式。能夠匹配任何返回類型。
僅當方法返回給定類型時,徹底限定類型名稱才匹配。
名稱模式與方法名稱匹配,能夠將 * 通配符用做名稱模式的所有或部分。
若是指定聲明類型模式,請包含尾部 . 以將其鏈接到名稱模式組件。
參數模式稍微複雜一些:
下面顯示了一些常見的切入點表達式
@Pointcut("execution(public * *(..))") //執行任何公共方法
execution(* set*(..)) //執行set開頭的任何方法
execution(* chenxy.spring.AccountService.*(..)) //執行接口定義的任何方法
execution(* com.service.*.*(..)) //執行service包中任何方法
execution(* com.service..*.*(..)) //執行service包及其子包中任何方法
within(com.service.*) //服務包中的任何鏈接點
within(com.service..*) //服務包及其子包中的任何鏈接點
this(com.service.Account) //代理實現接口的任何鏈接點
target(com.service.Account) //目標對象實現接口的任何鏈接點
args(java.io.Serializable) //單個參數的任何鏈接點
// 目標對象具備@Transaction註釋的任何鏈接點
@target(org.springframework.transaction.annotation.Transactional)
// 任何鏈接點,其中目標對象的聲明類型具備@Transactional註釋
@within(org.springframework.transaction.annotation.Transactional)
// 任何鏈接點,其中執行方法具備@Transaction註釋
@annotation(org.springframework.transaction.annotation.Transactional)
@args(com.xyz.security.Classified) // 接受一個參數,而且傳遞範數的運行時類型
編譯期間,AspectJ處理切入點以優化匹配性能。檢查代碼並肯定每一個鏈接點是否匹配。
在第一次遇到切入點聲明時,會將其重寫爲匹配過程的最佳形式。基本上切入點在DNF中重寫,而且切入點的組件被排序。
宣佈建議
可使用@Before註釋在切面中的方法以前聲明
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { // ... }
使用@AfterReturning註釋在執行以後返回
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { // ... }
異常退出執行
@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doRecoveryActions() { // ... }
異常退出執行,並訪問異常內容
@AfterThrowing( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", throwing="ex") public void doRecoveryActions(DataAccessException ex) { // ... }
無論正常異常,最終執行
@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doReleaseLock() { // ... }
圍繞通知
圍繞通知圍繞匹配的方法執行運行。有機會在方法執行以前和以後完成工做。
若是須要線程安全的方式在方法執行以前和以後共享狀態,則一般會使用around通知。
使用@Around註釋聲明around通知。advice方法的第一個參數必須是type ProceedingJoinPoint。
通知主體內,調用底層方法執行proceed()。該方法能夠傳入object[]。
@Around("com.xyz.myapp.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; }
通知參數
Spring提供徹底類型通知,意味着在通知簽名聲明瞭所需的參數,而不是一直使用Object[]數組。
任何通知方式均可以聲明一個類型的參數做爲其第一個參數,JoinPoint接口提供了許多方法
將參數傳遞給建議
要使用參數值可用於通知體,可使用綁定形式args。若是args表達式中使用參數名稱代替類型名稱,則在調用通知時,相應參數的值將做爲參數值傳遞。
假設您要通知執行以Account對象做爲第一個參數的DAO操做,須要訪問通知體中的Account。能夠寫下面的內容
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") public void validateAccount(Account account) { // ... }
args(account,..) 切入點表達式的一部分有兩個目的
它將匹配僅限於那些方法至少接受一個參數的方法執行
聲明一個切入點,Account當它與鏈接點匹配時提供對象值,而後從通知中引入指定的切入點
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") private void accountDataAccessOperation(Account account) {} @Before("accountDataAccessOperation(account)") public void validateAccount(Account account) { // ... }
通知參數和泛型
Spring AOP能夠處理類聲明和方法參數中使用的泛型。假設有一個泛型類型以下
public interface Sample<T> { void sampleGenericMethod(T param); void sampleGenericCollectionMethod(Collection<T> param); }
能夠經過在要攔截方法的參數類型中鍵入advice參數,將方法類型的攔截限制爲某些參數類型
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") public void beforeSampleMethod(MyType param) { // Advice implementation }
此方法不適用於通用集合。沒法按以下方式定義切入點
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") public void beforeSampleMethod(Collection<MyType> param) { // Advice implementation }
爲了完成這項工做,必須檢查集合中的每一個元素,這是不合理的也沒法決定如何處理null通常值。
必須鍵入參數Collection<?>並手動檢測元素的類型
肯定參數名稱
通知調用中的參數綁定依賴切入點表達式中使用的名稱與通知切入點方法簽名中聲明的參數名稱匹配。參數名稱不能經過Java反射得到,所以Spring AOP使用如下策略來肯定參數名稱
若是用戶已明確指定參數名稱,則使用指定的參數名稱。通知和切入點註釋都有一個可選argNames屬性,可使用該屬性指定帶註釋方法的參數名稱。這些參數名稱能夠在運行時使用。
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", argNames="bean,auditable") public void audit(Object bean, Auditable auditable) { AuditCode code = auditable.value(); // ... use code and bean }
簡介使切面可以聲明通知對象實現給定接口,並表明這些對象提供該接口的實現
可使用@DeclareParents註釋進行介紹。批註用於聲明匹配類型具備新父級。假設給定一個名爲interface接口UsageTracked和該接口的實現DefaultUsageTracked,如下切面聲明服務接口的全部實現者也實現了UsageTracked接口
@Aspect public class UsageTracking { @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class) public static UsageTracked mixin; @Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)") public void recordUsage(UsageTracked usageTracked) { usageTracked.incrementUseCount(); } }
要實現的接口由註釋字段的類型肯定。註釋的value屬性@DeclareParents是AspectJ類型模式。任何匹配類型的bean都實現了該UsageTracked接口
服務bean能夠直接用做UsageTracked接口的實現。若是用編程方式訪問,能夠編寫如下內容
UsageTracked usageTracked = (UsageTracked) context.getBean("myService");
實例化模型
應用程序上下文中的每個切面都有一個實例,AspectJ將其稱爲單例實例化模型。可使用備用聲明週期定義切面。Spring支持AspectJ的perthis和pertarget實例化模型
能夠經過perthis在@Aspect註釋中指定子句來聲明切面
@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())") public class MyAspect { private int someState; @Before(com.xyz.myapp.SystemArchitecture.businessService()) public void recordServiceUsage() { // ... } }
perthis子句的做用是爲執行業務服務的每一個惟一服務對象建立一個切面實例。方法實例是在第一次服務對象上調用方法時建立的。當服務對象超出範圍時,該切面超出範圍。在建立切面實例以前,其中沒有任何通知執行。一旦建立了切面實例,其中聲明的通知就會在匹配的鏈接點執行。
因爲併發問題,業務服務的執行有時會失敗。
若是重試該操做,則可能在一下次嘗試時成功。這是明確跨越服務層中的多個服務的要求,所以是經過一個切面實現的理想選擇。
是要使用around建議,以即可以proceed屢次調用
@Aspect @Component public class ConcurrentOperationExecutor implements Ordered { private static final int DEFAULT_MAX_RETRIES = 2; private int maxRetries = DEFAULT_MAX_RETRIES; private int order = 1; public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } @Around("execution(* chenxy.spring.demo.*.*(..))") public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { int numAttempts = 0; Exception lockFailureException; do { numAttempts++; try { return pjp.proceed(); } catch (Exception ex) { lockFailureException = ex; } } while (numAttempts <= this.maxRetries); throw lockFailureException; } }
切面實現了Ordered接口,以便咱們能夠將切面的優先級設置爲高於事務通知。
maxRetried和order均可以Spring配置,主要行動發生在doConcurrentOperation周圍的通知中。目前將重試邏輯應用於每一個businessService()。
會試着繼續訪問,若是失敗了PessimisticLockingFailureException,能夠再次訪問,一直到用盡全部的重試嘗試
相應的Spring配置以下
<aop:config> <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor"> <aop:pointcut id="idempotentOperation" expression="execution(* com.xyz.myapp.service.*.*(..))"/> <aop:around pointcut-ref="idempotentOperation" method="doConcurrentOperation"/> </aop:aspect> </aop:config> <bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor"> <property name="maxRetries" value="3"/> <property name="order" value="100"/> </bean>
#
SpringBoot能夠輕鬆建立能夠運行的獨立應用程序。大多數應用只須要不多的Spring配置。
可使用java -jar或傳統的war部署啓動的Java應用程序
Eclipse安裝插件
Spring Tool Suite (STS) for Eclipse
建立項目
New -> Project -> Spring Starter Project
啓動項目
Run As -> Spring Boot App
import org.springframework.boot.*; import org.springframework.boot.autoconfigure.*; import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration public class Example { @RequestMapping("/") String home() { return "Hello World!"; } public static void main(String[] args) throws Exception { SpringApplication.run(Example.class, args); } }
@RestController:構造性註釋,提醒類是一個Web的Controller
@RequestMapping:提供路由信息,告訴Spring任何帶/路徑的HTTP請求都應該映射到該home方法
@EnableAutoConfiguration:自動配置正在開發Web應用程序並相應設置Spring
main方法,遵循應用程序入口點的Java約定的標準方法。經過調用委託給SpringBoot的類run。先啓動Spring,而後自動配置Tomcat Web服務器。須要Example.class做爲參數傳遞給run方法,以告訴SpringApplication哪一個是主要的Spring組建。
建立可執行jar包
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
添加到pom.xml。運行mvn package
當一個類不包含package聲明時,認爲是默認包。一般不鼓勵使用默認包。建議遵循Java推薦包命名並使用反向域名
Spring Boot默認提供靜態資源目錄位置需置於classpath下,目錄名需符合以下規則:
Spring Boot框架自己並無對工程結構有特別要求,可是按照最佳實踐的工程結構能夠幫助咱們減小可能碰見的坑,尤爲是Spring包掃描機制的存在。
典型示例
myproject:放置應用程序主類,作一些配置掃描等工做
domain:放置實體、數據訪問層
service:邏輯層
web:表示層
示例代碼
@RestController @RequestMapping(value="/users") // 經過這裏配置使下面的映射都在/users下 public class UserController { // 建立線程安全的Map static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>()); @RequestMapping(value="/", method=RequestMethod.GET) public List<User> getUserList() { // 處理"/users/"的GET請求,用來獲取用戶列表 // 還能夠經過@RequestParam從頁面中傳遞參數來進行查詢條件或者翻頁信息的傳遞 List<User> r = new ArrayList<User>(users.values()); return r; } @RequestMapping(value="/", method=RequestMethod.POST) public String postUser(@ModelAttribute User user) { // 處理"/users/"的POST請求,用來建立User // 除了@ModelAttribute綁定參數以外,還能夠經過@RequestParam從頁面中傳遞參數 users.put(user.getId(), user); return "success"; } @RequestMapping(value="/{id}", method=RequestMethod.GET) public User getUser(@PathVariable Long id) { // 處理"/users/{id}"的GET請求,用來獲取url中id值的User信息 // url中的id可經過@PathVariable綁定到函數的參數中 return users.get(id); } @RequestMapping(value="/{id}", method=RequestMethod.PUT) public String putUser(@PathVariable Long id, @ModelAttribute User user) { // 處理"/users/{id}"的PUT請求,用來更新User信息 User u = users.get(id); u.setName(user.getName()); u.setAge(user.getAge()); users.put(id, u); return "success"; } @RequestMapping(value="/{id}", method=RequestMethod.DELETE) public String deleteUser(@PathVariable Long id) { // 處理"/users/{id}"的DELETE請求,用來刪除User users.remove(id); return "success"; } }
下面針對Controller編寫單元測試
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class ApplicationTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new UserController()).build(); } @Test public void testUserController() throws Exception { // 測試UserController RequestBuilder request = null; // 一、get查一下user列表,應該爲空 request = get("/users/"); mvc.perform(request) .andExpect(status().isOk()) .andExpect(content().string(equalTo("[]"))); // 二、post提交一個user request = post("/users/") .param("id", "1") .param("name", "測試大師") .param("age", "20"); mvc.perform(request) .andExpect(content().string(equalTo("success"))); // 三、get獲取user列表,應該有剛纔插入的數據 request = get("/users/"); mvc.perform(request) .andExpect(status().isOk()) .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"測試大師\",\"age\":20}]"))); // 四、put修改id爲1的user request = put("/users/1") .param("name", "測試終極大師") .param("age", "30"); mvc.perform(request) .andExpect(content().string(equalTo("success"))); // 五、get一個id爲1的user request = get("/users/1"); mvc.perform(request) .andExpect(content().string(equalTo("{\"id\":1,\"name\":\"測試終極大師\",\"age\":30}"))); // 六、del刪除id爲1的user request = delete("/users/1"); mvc.perform(request) .andExpect(content().string(equalTo("success"))); // 七、get查一下user列表,應該爲空 request = get("/users/"); mvc.perform(request) .andExpect(status().isOk()) .andExpect(content().string(equalTo("[]"))); } }
#
建議將主應用程序類放在其餘類之上的根包中。該@SpringBootApplication註解放在你的主類,它隱含地定義爲某些項目搜索包。例如正在編寫JPA應用項目,@SpringBootApplication則使用帶註釋的類的包來搜索@Entity項目。還容許組建掃描僅應用於您的項目。
故此經典佈局模式應該是下方的形式
com +- example +- myapplication +- Application.java | +- customer | +- Customer.java | +- CustomerController.java | +- CustomerService.java | +- CustomerRepository.java | +- order +- Order.java +- OrderController.java +- OrderService.java +- OrderRepository.java
配置類:SpringBoot支持基於JAVA的配置,一般建議主要源是單個@Configuration類,定義main方法的類是主要的選擇
導入配置類:@Import註釋能夠用於導入額外的配置類,或者使用@ComponentScan自動獲取全部Spring組件,包括@Configuration類
導入XML配置:從一個@Configuration類開始,使用@ImportResource註釋來加載XML配置文件
自動配置:SpringBoot自動配置嘗試根據您添加的jar依賴項自動配置Spring應用程序。須要經過向其中一個類添加@EnableAutoConfiguration或@SpringBootApplication註釋來自動選擇@Configuration
禁用特定自動配置類:@EnableAutoConfiguration禁用
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
SpringBeans和依賴注入
使用@ComponentScan和使用@Autowired作構造函數注入效果很好
能夠添加@ComponentScan不帶任何參數的代碼。全部應用程序組建如@Component,@Service,@Controller等自動註冊接口
例以下面展現了一個@Service使用構造函數注入來獲取所需RiskAssessor bean的Bean
package com.example.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class DatabaseAccountService implements AccountService { public final RiskAssessor riskAssessor; @Autowired public DatabaseAccountService(RiskAssessor riskAssessor){ this .riskAssessor = riskAssessor; } }
自動配置
@SpringBootApplication:能夠單個註釋來啓用自動配置、組建掃描、額外註冊這三個功能。
@EnableAutoConfiguration:啓用SpringBoot的自動配置機制,根據路徑設置添加bean
@ComponentScan:應用程序所在的程序包上啓用掃描,告訴Spring在包中尋找其餘組件
@Configuration:容許在上下文中註冊額外的bean或導入其餘配置類
package com.example.myapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
//與@Configuration相同@EnableAutoConfiguration @ComponentScan public class Application { public static void main(String [] args){ SpringApplication.run(Application .class,args); } }
SpringBoot將自動處理這些存儲庫,只要它們包含在@SpringBootApplication類的同一個包或子包中。要更好地控制註冊過程,可使用@EnableMongoRepositories註釋
package hello; import java.util.concurrent.atomic.AtomicLong; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GreetingController { private static final String template = "Hello,%s!"; private final AtomicLong counter = new AtomicLong(); @RequestMapping("/greeting") public Greeting greeting( @RequestParam(value = "name", defaultValue = "World") String name) { return new Greeting(counter.incrementAndGet(), String.format(template, name)); } }
@RequestMapping:確保HTTP請求/greeting被映射到greeting()方法,使用@RequestMapping(method=GET) 能夠縮小指定請求映射
@RequestParam:查詢字符串參數的值綁定name到方法name參數中。若是不存在則使用默認值
@RestController:將類標記爲控制器,其中每一個方法都返回一個域對象而不是視圖。
Greeting對象必須轉換爲JSON,因爲Spring的HTTP消息轉化器支持,無需手動執行此轉換,會自動選擇Spring來將Greeting實例轉換爲JSON
@JsonIgnoreProperties:指示應忽略此類型中未綁定的任何屬性
熱插拔
熱部署:指容器在運行狀態下部署或從新部署整個項目。可能會形成session丟失
熱加載:容器狀態在運行的狀況下從新加載改變編譯後的類。這種狀況下內存不會清空,session不會丟失。
SpringBoot應用程序是普通的JAVA應用程序,JVM熱交換是能夠直接使用的。spring-boot-devtools模塊還包含對快速應用程序重啓的支持。
SpringApplication除了一般的Spring Framework事件以外,還會發送一些額外的應用程序事件
應用程序運行時,會按如下順序發送應用程序事件
使用SpringFramework事件發佈機制發送應用程序事件。機制的一部分保存在子上下文中發佈給監聽器的事件也會在上下文中發佈。
ApplicationContextAware若是監聽器是bean,則能夠經過註冊上下文@Autowired
Spring Boot容許外部化配置,能夠在不一樣的環境中使用相同的應用代碼。
可使用屬性、YAML文件,環境變量、命令行參數來外部化配置。屬性能夠經過直接注射到你的bean @Value註釋,經過Spring的訪問Evironment抽象,或者綁定到結構化對象經過@ConfigurationProperties
SpringBoot使用一種很是特殊的PropertySource順序,容許合理的覆蓋值。按如下順序考慮屬性
#
許多已經成爲平常開發人員語言的一部分,包括entity,就是指一個具備惟一標識的持久化對象。value object,也就是VO,你常常據說的,是用來存放數據的,能夠與數據庫表對應,也能夠不對應,有點相似用來傳輸數據的DTO。service,就是指包含業務邏輯的服務。但不該歸類到entity或者value object。
repository,表示一堆entity 的集合就是一個repository。
如何拆解服務呢?
使用什麼樣的方法拆解服務?業界流行1個類=1個服務、1個方法=1個服務、2 Pizza團隊、2周能重寫完成等方法,可是這些都缺少實施基礎。咱們必須從一些軟件設計方法中尋找,面向對象和設計模式適用的問題空間是一個模塊,而函數式編程的理念更多的是在代碼層面的微觀上起做用。
Eric Evans 的《領域驅動設計》這本書對微服務架構有很大借鑑意義,這本書提出了一個能將一個大問題空間拆解分爲領域和實體之間的關係和行爲的技術。目前來講,這是一個最合理的解決拆分問題的方案,透過限界上下文(Bounded Context,下文簡稱爲BC)這個概念,咱們能將實現細節封裝起來,讓BC都可以實現SRP(單一職責)原則。而每一個微服務正是BC在實際世界的物理映射,符合BC思路的微服務互相獨立鬆耦合。
微服務架構是一件好事,逼着你們關注設計軟件的合理性,若是原來在Monolithic中領域分析、面向對象設計作很差,換微服務會把這個問題成倍的放大
以電商中的訂單和商品兩個領域舉例,按照DDD拆解,他們應該是兩個獨立的限界上下文,可是訂單中確定是包含商品的,若是貿然拆爲兩個BC,查詢、調用關係就耦合在一塊兒了,甚至有了麻煩的分佈式事務的問題,這個關聯如何拆解?BC理論認爲在不一樣的BC中,即便是一個術語,他的關注點也不同,在商品BC中,關注的是屬性、規格、詳情等等(實際上商品BC這個領域有價格、庫存、促銷等等,把他做爲單獨一個BC也是不合理的,這裏爲了簡化例子,你們先認爲商品BC就是商品基礎信息), 而在訂單BC中更關注商品的庫存、價格。因此在實際編碼設計中,訂單服務每每將關注的商品名稱、價格等等屬性冗餘在訂單中,這個設計解脫了和商品BC的強關聯,兩個BC能夠獨立提供服務,獨立數據存儲
#