Java學習筆記

Java學習筆記

安裝

安裝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

 

Maven使用

新建項目: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下的一個開源項目,提供一個健壯、易用的鏈接池,提供監控功能,方便易用,便於發現泄露的狀況

Maven

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>
View Code

使用<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>
View Code

使用<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>
View Code

建立項目

若是須要在某個地方建立項目,須要建立一個目錄了並在該目錄中啓動一個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將執行序列中每一個階段

  1. 驗證
  2. 產生來源
  3. 流程源
  4. 生成資源
  5. 流程資源
  6. 編譯

打包完成後可使用java命令進行測試

java -cp target/maven-app-1.0-SNAPSHOT.jar com.mvndemo.app.App

Maven階段

默認生命週期階段

  • validate: 驗證項目是否正確而且全部必要的信息均可用
  • compile:編譯項目的源代碼
  • test:使用合適的單元測試框架測試編譯的源代碼
  • package:獲取已編譯的代碼並將其打包爲可分發的格式,例如JAR
  • integration-test:將程序包處理並部署到能夠運行集成測試的環境中
  • verify:運行任何檢查以驗證包是否有效並符合質量標準
  • install:將軟件包安裝到本地存儲庫
  • deploy:在集成或發佈環境中完成,將最終包賦值到遠程存儲庫
  • clean:清楚先前構建建立的工做
  • site:爲該項目生成站點文檔

階段實際上映射到基本目標。每一個階段執行的具體目標取決於項目的包裝類型

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打包

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語法學習

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];

動態初始化時,系統回根據數據類型進行分配初始值。

  • 整數類型:0
  • 浮點類型:0.0
  • 字符類型:'\u000'
  • 布爾類型:false
  • 引用類型:null

注意:不要同時使用靜態和動態初始化。

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();
    }

JUnit

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 的註解。

  • @Test (expected = Exception.class)       表示預期會拋出Exception.class 的異常
  • @Ignore 含義是「某些方法還沒有完成,暫不參與這次測試」。這樣的話測試結果就會提示你有幾個測試被忽略,而不是失敗。一旦你完成了相應函數,只須要把@Ignore註解刪去,就能夠進行正常的測試。
  • @Test(timeout=100)       表示預期方法執行不會超過 100 毫秒,控制死循環
  • @Before 表示該方法在每個測試方法以前運行,可使用該方法進行初始化之類的操做
  • @After 表示該方法在每個測試方法以後運行,可使用該方法進行釋放資源,回收內存之類的操
  • @BeforeClass  表示該方法只執行一次,而且在全部方法以前執行。通常可使用該方法進行數據庫鏈接操做,注意該註解運用在靜態方法。
  • @AfterClass    表示該方法只執行一次,而且在全部方法以後執行。通常可使用該方法進行數據庫鏈接關閉操做,注意該註解運用在靜態方法。

下面簡單介紹一下上邊用到的靜態類 junit.framework.Assert。該類主要包含七個方法:

  • assertEquals() 方法,用來查看對象中存的值是不是期待的值,與字符串比較中使用的 equals() 方法相似;
  • assertFalse() 和 assertTrue() 方法,用來查看變量是是否爲 false 或 true,若是 assertFalse() 查看的變量的值是 false 則測試成功,若是是 true 則失敗,assertTrue() 與之相反。
  • assertSame() 和 assertNotSame() 方法,用來比較兩個對象的引用是否相等和不相等,相似於經過「==」和「!=」比較兩個對象;
  • assertNull() 和 assertNotNull() 方法,用來查看對象是否爲空和不爲空。

Mock對象

@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());

#

Spring Framework

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容器

Spring IOC容器和Bean簡介

Ioc(控制反轉)也稱依賴注入(DI)。能夠經過構造函數參數,工廠方法參數或工廠方法構造或返回的對象實例上設置的屬性來定義它們的依賴關係。而後在建立bean時注入這些依賴項。

這個過程基本上是經過使用類的直接構造或服務定位器模式之類的機制來控制其依賴關係的實例化或位置的逆。

org.springframework.beans|context包是Spring框架的Ioc容器的基礎。

ApplicationContext是一個子界面的BeanFactory

  • 更容易與Spring的AOP功能集成
  • 消息資源處理
  • 活動出版
  • 特定WebApplicationContext與應用程序層的上下文,例如在Web應用程序中使用的上下文

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,是使用體工給容器的配置元數據建立的。

命名 Beans

每一個bean都有一個或多個標識符。必須是惟一的。

可使用id屬性,name屬性來定義bean標識符。若是不提供name或id,會自動生成。可是若是要使用ref元素,則必須體工。

命名約定小字母開頭,駝峯。accountManger,accountService

引入別名:<alias />

<alias name="fromName" alias="toName"/>

實例化 Beans

class屬性中是實例化的對象類型。能夠經過如下Class兩種方式之一使用該屬性

  • 經過反向調用其構造函數直接建立bean的狀況下指定要構造的bean類
  • 經過static調用工廠方法

使用構造函數實例化

須要一個默認空構造函數。使用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>

必須在啓用調試標誌的狀況下編譯代碼,以即可以從構造函數中查找參數名稱。

基於Setter的依賴注入

調用無參數構造函數或無參數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

能夠標記延遲初始化,在第一次請求時建立

<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的自動裝配。

  • no:無自動裝配。必須由ref元素定義。
  • byName:屬性名自動裝配。屬性同名的bean
  • byType:屬性類型相同的bean
  • constructor:構造參數相同屬性

須要在屬性上面添加@Autowired,接口實現須要添加@Component

Bean範圍

建立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:分別表明表示層、數據層、邏輯層

JSR 330

Spring3.0開始,提供依賴注入支持。可使用javax.inject

<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>

用@Inject 替換 @Autowired。一樣能夠在字段級別、方法級別、構造參數級別注入。

Java的容器配置

@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便捷實例化 

能夠在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界面中一些最重要的方法是:

  • getInputStream:找到並打開資源
  • exists:返回boolean指示此資源是否以物理形式存在
  • isOpen:返回一個boolean指示是否具備打開流的句柄。若是true不能讀取屢次
  • getDescription:返回此資源的描述,用於處理資源時的錯誤輸出

其餘方法容許獲取資源的實際URL或File對象

內置資源實現

UrlResource

包裝java.net.URL,能夠用於訪問URL的任務對象。例如文件、HTTP、FTP。全部URL都具備標準化String表示。

ClassPathResource

表示從類路徑獲取的資源。使用線程上下文類加載器,給定的類加載器或給定的類來加載資源。

FileSystemResource

支持做爲一個File和一個解決方案URL

ServletContextResource

用於ServletContext解釋相關Web應用根目錄的相對路徑。支持流訪問和URL訪問

ResourceLoader

接口能夠返回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");

#

Spring Expression Language

簡稱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);

每一個符號運算符能夠指定純字母等價運算符

  • lt (<)
  • gt (>)
  • le (<=)
  • ge (>=)
  • eq (==)
  • ne (!=)
  • div (/)
  • mod (%)
  • not (!)

文本運算符不區分大小寫

SpEL支持如下邏輯運算符

  • and
  • or
  • not

Spring進行面向切面編程

面向切面編程(AOP)經過提供另外一種思考程序結構的方式來補充面向對象編程(OOP)。

OOP中模塊化的關鍵單元是類,AOP中模塊化單元是切面。切面實現了跨越多種類型和對象的關注點的模塊化。能夠進行跨越服務關聯,將影響了多個類的公共行爲封裝到一個可重用模塊。

Spring的一個關鍵組件是AOP框架。雖然Spring Ioc容器不依賴於AOP,可是提供了很是強大的中間件解決方案。

AOP在Spring Framework中用於

  • 提供聲明性企業服務,做爲EJB聲明性服務的替代品。
  • 讓用戶實現自定義切面,補充他們使用AOP的OOP

AOP概念

介紹一些AOP概念和術語

  • 切面(aspect):跨越多個類別的關注點的模塊化。事務管理是企業Java應用程序中橫切關注點的一個很好的例子。Spring AOP中,切面是經過常規類或使用@Aspect註釋的常規類來實現
  • 鏈接點(join point):程序執行期間的一個點,例如執行方法、處理異常。鏈接點始終表示方法執行。加入點值得就是被攔截到的方法。
  • 通知(advice):某個切面在特定鏈接點採起的操做,攔截到鏈接點以後要執行的代碼。不一樣類型的包括「around」,"before","after",許多AOP框架將建議建模爲攔截器並在鏈接點周圍維護一系列攔截器
  • 切入點(pointcut):匹配鏈接點、通知與切入點表達式相關聯,並在鏈接點匹配的任何鏈接點外運行。
  • 簡介(introduction):表明類型聲明其餘方法或字段,AOP容許您向任何建議的對象引入新接口。例如可使用Introduction使bean實現IsModified接口,以簡化緩存
  • 目標對象:由一個或多個方面建議的對象。Spring AOP是運行時代理實現的,所以該對象始終是代理對象
  • AOP代理:由AOP框架建立的對象,用於實現切面契約。AOP代理是JDK動態代理或CGLIB代理
  • 織入(weave):將切面與其餘應用程序類型或對象連接以建立通知對象。能夠在編譯時,加載時間或運行時完成。與其餘純Java AOP框架同樣,Spring AOP在運行時執行編織。

Spring AOP包括如下類型的建議

  • Before advice:在鏈接點以前運行但沒法阻止執行流程進入鏈接點的通知
  • After returning advice:在鏈接點正常完成後運行的通知
  • After throwing advice:若是方法經過拋出異常退出,執行的通知
  • After advice:不管鏈接點退出的方式是什麼,都要執行
  • Around advice:圍繞鏈接點的通知。例如方法調用,就是最有效的通知。around通知能夠在方法調用以前和以後執行自定義行爲。還負責選擇是繼續鏈接點仍是通知返回本身的返回值或拋出異常來快速建議的方法執行

圍繞通知是最普通的通知,因爲Spring AOP提供了全方位的通知類型,所以建議使用能夠實現所需行爲的最具體的通知類型。

例如只須要使用方法的返回值更新緩存,那麼最好實現返回後的通知而不是圍繞的通知,通過能夠完成一樣的事情,可是使用最具體的通知類型能夠提供更簡單的編程模型,減小BUG的可能性。

由切入點匹配的鏈接點的概念是AOP的關鍵,將其與僅提供攔截的舊技術區分開來。切入點使得通知能夠獨立於面向對象的層次結構進行定向。

能夠將一個提供聲明性事務管理的通知應用於跨越多個對象的一組方法

Spring AOP功能和目標

Spring AOP是用Java實現的。不須要特殊的編譯過程。Spring AOP不須要控制類加載器層次結構,所以適合在servlet容器或應用程序服務器中使用。

Spring AOP目前僅支持方法執行鏈接點。雖然能夠在不破壞核心Spring AOP的狀況下添加對字段攔截的支持,但未實現字段攔截。若是須要通知字段訪問和更新鏈接點,考慮使用AspectJ

Spring框架的核心原則在於非侵入性,不該該被迫在您的業務或模型中引入特定於框架的類和接口。SpringFramework確實爲您提供了將Spring Framework特定的依賴項引入代碼庫選項。爲您提供此類選項的基本原理是,在某些狀況下,這種方式閱讀或編寫某些特定功能可能更容器。

AOP代理

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)用於切入點表達式

  • execution:用於匹配方法執行鏈接點。使用Spring AOP時使用的主要切入點指示符
  • within:限制匹配某些類型中的鏈接點
  • this:限制於鏈接點的匹配,其中bean引用是給定類型的實例
  • target:限制匹配鏈接點,其中目標對象是給定類型的實例
  • args:限制於鏈接點匹配,其中參數是給定類型的實例
  • @target: 限制於鏈接點的匹配,執行對象的類具備給定類型的註釋
  • @args:限制於鏈接點的匹配,其中傳遞的實際參數的運行時類型具備給定類型的註釋
  • @within:限制匹配到具備給定註釋的類型中的鏈接點
  • @annotation:限制鏈接點的匹配,其中鏈接點的主題具備給定的註釋

結合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返回類型模式,名稱模式和參數模式以外的全部部分都是可選擇。返回類型模式肯定方法的返回類型必須是什麼才能匹配鏈接點。

* 最經常使用做返回類型模式。能夠匹配任何返回類型。

僅當方法返回給定類型時,徹底限定類型名稱才匹配。

名稱模式與方法名稱匹配,能夠將 * 通配符用做名稱模式的所有或部分。

若是指定聲明類型模式,請包含尾部 . 以將其鏈接到名稱模式組件。

參數模式稍微複雜一些:

  • () 匹配不帶參數的方法,
  • (..)匹配任何數量參數,
  • (*) 匹配任何類型的一個參數的方法,
  • (*,String) 匹配一個帶有兩個參數的方法

下面顯示了一些常見的切入點表達式

@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接口提供了許多方法

  • getArgs():返回方法參數
  • getThis():返回代理對象
  • getTarget():返回目標對象
  • getSignature():返回正在通知的方法的描述
  • toString():打印通知方法的有用說明

將參數傳遞給建議

要使用參數值可用於通知體,可使用綁定形式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子句的做用是爲執行業務服務的每一個惟一服務對象建立一個切面實例。方法實例是在第一次服務對象上調用方法時建立的。當服務對象超出範圍時,該切面超出範圍。在建立切面實例以前,其中沒有任何通知執行。一旦建立了切面實例,其中聲明的通知就會在匹配的鏈接點執行。

AOP實例

因爲併發問題,業務服務的執行有時會失敗。

若是重試該操做,則可能在一下次嘗試時成功。這是明確跨越服務層中的多個服務的要求,所以是經過一個切面實現的理想選擇。

是要使用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>

 

 

#

Spring Boot

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下,目錄名需符合以下規則:

  • /static
  • /public
  • /resources
  • /META-INF/resources

工程結構

Spring Boot框架自己並無對工程結構有特別要求,可是按照最佳實踐的工程結構能夠幫助咱們減小可能碰見的坑,尤爲是Spring包掃描機制的存在。

典型示例

myproject:放置應用程序主類,作一些配置掃描等工做

domain:放置實體、數據訪問層

service:邏輯層

web:表示層

RESTful API與單元測試

示例代碼

@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註釋

RESTful Web服務

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事件以外,還會發送一些額外的應用程序事件

應用程序運行時,會按如下順序發送應用程序事件

  1. ApplicationStartingEvent 開始啓動,但在除了註冊監聽器和初始化程序以外的任何處理以前。
  2. ApplicationEvironmentPreparedEvent spring boot 對應Enviroment已經準備完畢,但此時上下文context尚未建立。
  3. ApplicationPreparedEvent spring boot上下文context建立完成,但此時spring中的bean是沒有徹底加載完成的。
  4. ApplicationStartedEvent 上下文刷新後發送,在應用程序和命令行調用以前
  5. ApplicationFailedEvent 啓動異常時發送

使用SpringFramework事件發佈機制發送應用程序事件。機制的一部分保存在子上下文中發佈給監聽器的事件也會在上下文中發佈。

ApplicationContextAware若是監聽器是bean,則能夠經過註冊上下文@Autowired

外部配置

Spring Boot容許外部化配置,能夠在不一樣的環境中使用相同的應用代碼。

可使用屬性、YAML文件,環境變量、命令行參數來外部化配置。屬性能夠經過直接注射到你的bean @Value註釋,經過Spring的訪問Evironment抽象,或者綁定到結構化對象經過@ConfigurationProperties

SpringBoot使用一種很是特殊的PropertySource順序,容許合理的覆蓋值。按如下順序考慮屬性

  1. Devtools主目錄上的全局設置屬性
  2. @TestPropertySource測試上的註釋
  3. properties屬性測試。可用@SpringBootTest的測試註釋
  4. 命令行參數
  5. 來自SPRING_APPLICATION_JSON的屬性
  6. ServletConfig init參數
  7. ServletContext init參數
  8. JNDI屬性,來自java:comp/env
  9. Java系統屬性(System.getProperties())
  10. OS環境變量
  11. RandomValuePropertySource,擁有性能random.*
  12. 特定於配置文件的應用程序屬性在打包的jar以外

 

 

 

 

#

許多已經成爲平常開發人員語言的一部分,包括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能夠獨立提供服務,獨立數據存儲

 

 

 

#

相關文章
相關標籤/搜索