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能够独立提供服务,独立数据存储

 

 

 

#

相关文章
相关标签/搜索