Springboot2快速集成

  1. 通過(guò)一個(gè)實(shí)例快速搭建SSM。
  2. 通過(guò)實(shí)例來(lái)看看各個(gè)環(huán)節(jié)的最佳實(shí)踐。
  3. 如何使用業(yè)務(wù)異常。
  4. 如何設(shè)計(jì)一個(gè)對(duì)前端友好的接口。
  5. 通過(guò)單元測(cè)試使你的工作更加輕松和安全。
  6. 簡(jiǎn)單聊聊代碼規(guī)范。
  7. 利用CI/CD來(lái)幫助你處理繁雜的重復(fù)性工作。

源碼地址 https://github.com/bestaone/Mybatis4Springboot


Spring5、springboot2近況


  • spring5

    最大的亮點(diǎn)是 Spring webflux。Spring webflux 是一個(gè)新的非堵塞函數(shù)式 Reactive Web 框架,可以用來(lái)建立異步的,非阻塞,事件驅(qū)動(dòng)的服務(wù),并且擴(kuò)展性非常好。

  • springboot

    集成了大量常用的第三方庫(kù)配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot應(yīng)用中這些第三方庫(kù)幾乎可以零配置的開箱即用(out-of-the-box),大部分的Spring Boot應(yīng)用都只需要非常少量的配置代碼,開發(fā)者能夠更加專注于業(yè)務(wù)邏輯。


一分鐘helloworld看看新姿勢(shì)


  • 創(chuàng)建文件

    新建項(xiàng)目Demo

    創(chuàng)建文件 pom.xml

    src/main/java、src/main/resources、src/test/java

    創(chuàng)建包 hello


pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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.caad.springboot.test</groupId>
    <artifactId>Demo</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.M7</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>http://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>


創(chuàng)建啟動(dòng)類 SampleController.java

package hello;

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@Controller
@EnableAutoConfiguration
public class SampleController {

    @RequestMapping("/")
    @ResponseBody
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SampleController.class, args);
    }
    
}
  • 運(yùn)行
mvn install
mvn spring-boot:run
  • 測(cè)試
http://localhost:8080/


標(biāo)準(zhǔn)的三層模型


  • 目錄結(jié)構(gòu)
- com.caad.springboot.test
    - controller
        - UserController.java
    - service
        - UserService.java
    - dao
        - UserDao.java
    - domain
        - enums
            - GenderType.java
        - User.java
    - Application.java

為什么service不使用interface了

  • 代碼實(shí)現(xiàn)
public class User {

    private Long id;
    private String name;
    private GenderType gender;
    private Date createTime;

}

public enum GenderType {

    MALE,
    FEMALE,
    UNKNOW,
    OTHER;

}
@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/find/{id}")
    public User find(@PathVariable("id") Long id) {
        User user = userService.findById(id);
        return user;
    }

}
@Service
public class UserService {

    @Autowired
    private UserDao dao;

    public User findById(Long id) {
        return dao.findById(id);
    }

}
@Repository
public class UserDao {

    public User findById(Long id) {
        User user = new User();
        user.setId(123L);
        user.setName("test");
        user.setGender(GenderType.UNKNOW);
        user.setCreateTime(new Date());
        return user;
    }

}


集成mybatis


  • 添加依賴maven依賴
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- springboot整合mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>
  • 添加springboot配置文件 application.yml
server.port: 8888

spring.datasource:
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://172.16.2.154:3307/aqs_test?useUnicode=true&characterEncoding=utf-8
  username: 
  password: r5rD6a8NBnWP9NGs

mybatis:
  config-locations: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
  type-aliases-package: com.caad.springboot.test.domain

  • 添加mybatis配置文件 mybatis-config.xml
<configuration>

    <!-- 全局參數(shù) -->
    <settings>
        <!-- 使全局的映射器啟用或禁用緩存。 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 全局啟用或禁用延遲加載。當(dāng)禁用時(shí),所有關(guān)聯(lián)對(duì)象都會(huì)即時(shí)加載。 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 當(dāng)啟用時(shí),有延遲加載屬性的對(duì)象在被調(diào)用時(shí)將會(huì)完全加載任意屬性。否則,每種屬性將會(huì)按需要加載。 -->
        <setting name="aggressiveLazyLoading" value="true"/>
        <!-- 是否允許單條sql 返回多個(gè)數(shù)據(jù)集  (取決于驅(qū)動(dòng)的兼容性) default:true -->
        <setting name="multipleResultSetsEnabled" value="true"/>
        <!-- 是否可以使用列的別名 (取決于驅(qū)動(dòng)的兼容性) default:true -->
        <setting name="useColumnLabel" value="true"/>
        <!-- 允許JDBC 生成主鍵。需要驅(qū)動(dòng)器支持。如果設(shè)為了true,這個(gè)設(shè)置將強(qiáng)制使用被生成的主鍵,有一些驅(qū)動(dòng)器不兼容不過(guò)仍然可以執(zhí)行。  default:false  -->
        <setting name="useGeneratedKeys" value="true"/>
        <!-- 指定 MyBatis 如何自動(dòng)映射 數(shù)據(jù)基表的列 NONE:不隱射 PARTIAL:部分  FULL:全部  -->
        <setting name="autoMappingBehavior" value="PARTIAL"/>
        <!-- 這是默認(rèn)的執(zhí)行類型  (SIMPLE: 簡(jiǎn)單; REUSE: 執(zhí)行器可能重復(fù)使用prepared statements語(yǔ)句;BATCH: 執(zhí)行器可以重復(fù)執(zhí)行語(yǔ)句和批量更新)  -->
        <!-- 對(duì)于批量更新操作緩存SQL以提高性能 BATCH,SIMPLE -->
        <setting name="defaultExecutorType" value="BATCH"/>
        <!-- 數(shù)據(jù)庫(kù)超過(guò)25000秒仍未響應(yīng)則超時(shí) -->
        <setting name="defaultStatementTimeout" value="25000" />
        <!-- 使用駝峰命名法轉(zhuǎn)換字段。 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 設(shè)置本地緩存范圍 session:就會(huì)有數(shù)據(jù)的共享  statement:語(yǔ)句范圍 (這樣就不會(huì)有數(shù)據(jù)的共享 ) defalut:session -->
        <setting name="localCacheScope" value="SESSION"/>
        <!-- 設(shè)置但JDBC類型為空時(shí),某些驅(qū)動(dòng)程序 要指定值,default:OTHER,插入空值時(shí)不需要指定類型 -->
        <setting name="jdbcTypeForNull" value="NULL"/>
        <!-- 設(shè)置關(guān)聯(lián)對(duì)象加載的形態(tài),此處為按需加載字段(加載字段由SQL指 定),不會(huì)加載關(guān)聯(lián)表的所有字段,以提高性能 -->
        <setting name="aggressiveLazyLoading" value="false" />
    </settings>

</configuration>
  • 修改DAO注解
@Mapper
public interface UserDao {

    @Select("SELECT id, name, gender, createTime FROM User where id=#{id}")
    public User findById(Long id);

}
  • 初始化數(shù)據(jù)庫(kù)
CREATE TABLE `User` (
  `id` bigint(20) NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `gender` varchar(20) DEFAULT NULL,
  `createTime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • CRUD
@Insert("INSERT INTO User(id,name,gender,createTime) VALUES(#{id}, #{name}, #{gender}, #{createTime})")
void insert(User user);

@Delete("DELETE FROM User WHERE id = #{id}")
void delete(Long id);

@Update("UPDATE User SET name=#{name},gender=#{gender},createTime=#{createTime} WHERE id =#{id}")
void update(User user);

@Select("SELECT id, name, gender, createTime FROM User")
List<User> findAll();
@Service
public class UserService {

    @Autowired
    private UserDao dao;

    public User findById(Long id) {
        return dao.findById(id);
    }

    public User save(User user){
        if(user==null) return null;
        if(user.getId()==null){
            dao.insert(user);
        }else {
            dao.update(user);
        }
        return user;
    }

    public void remove(Long id){
        dao.delete(id);
    }

    public List<User> findAll(){
        return dao.findAll();
    }

}
  • 添加單元測(cè)試依賴
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>
  • 編寫測(cè)試類
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserServiceTest {

    @Autowired
    public UserService service;

    @Test
    public void CRUDTest() {

        //CREATE
        User o = new User();
        o.setCreateTime(new Date());
        o.setName("CRUDTest");
        service.save(o);
        Assert.assertNotNull(o.getId());

        //READ
        o = service.findById(o.getId());
        Assert.assertNotNull(o.getId());

        //UPDATE
        o.setName("CRUDTest1");
        service.save(o);
        o = service.findById(o.getId());
        Assert.assertTrue(o.getName().equals("CRUDTest1"));

        //DELETE
        service.remove(o.getId());
        o = service.findById(o.getId());
        Assert.assertNull(o);

    }
    
}
  • 引入主鍵生成器 IdGenerator

  • XML方式實(shí)現(xiàn)DAO接口

List<User> findByName(String name);

  • 添加mapper文件(文件名需要和接口名一致)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.caad.springboot.test.dao.UserDao" >

    <resultMap id="BaseResultMap" type="User" >
        <id column="id" property="id" jdbcType="BIGINT" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="gender" property="gender" javaType="GenderType" />
        <result column="createTime" property="createTime" jdbcType="TIMESTAMP"/>
    </resultMap>

    <sql id="Base_Column_List" >
        id, name, gender, createTime
    </sql>

    <select id="findByName" resultMap="BaseResultMap" >
        SELECT <include refid="Base_Column_List" /> FROM User WHERE name = #{name}
    </select>

</mapper>
  • 添加logback.xml配置,查看sql
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">

    <!--定義日志文件的存儲(chǔ)地址 勿在 LogBack 的配置中使用相對(duì)路徑-->
    <property name="LOG_HOME" value="./logs" />
    <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息,%n是換行符-->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
    <!--<property name="LOG_PATTERN" value="%ip [web] %d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } -&#45;&#45; [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>-->
    <property name="LOG_FILE" value="./logs/app.log"/>

    <!-- 用于打印 Spring 在啟動(dòng)的時(shí)候初始化各個(gè) Bean 的信息 -->
    <!-- level:用來(lái)設(shè)置打印級(jí)別,大小寫無(wú)關(guān)(最常用的幾種):DEBUG, INFO, WARN, ERROR -->
    <!--<logger name="org.springframework.web" level="DEBUG"/>-->
    <logger name="com.caad.springboot.test.mapper" level="DEBUG" />
    <logger name="com.caad.springboot" level="DEBUG"/>

    <!-- 控制臺(tái)輸出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%i.zip</fileNamePattern>
            <minIndex>1</minIndex>
            <maxIndex>10</maxIndex>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 日志輸出級(jí)別 -->
    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    </root>

</configuration>
  • 添加測(cè)試代碼
List<User> list = service.findByName(o.getName());
Assert.assertNotNull(list.size()>0);
  • 幾次失誤,導(dǎo)致了臟數(shù)據(jù),映入測(cè)試回滾
@Transactional
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserServiceTest {
  • 添加controller端findAll接口
@RequestMapping(value = "/getAll")
public List<User> getAll() {
    return userService.findAll();
}
  • 引入分頁(yè)插件
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.3</version>
</dependency>
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="helperDialect" value="mysql"/>
        <property name="reasonable" value="true"/>
        <property name="supportMethodsArguments" value="true"/>
        <property name="autoRuntimeDialect" value="true"/>
        <property name="params" value="count=countSql"/>
    </plugin>
</plugins>
@RequestMapping(value = "/pageAll")
public PageInfo<User> pageAll() {
    PageHelper.startPage(1, 5);
    List<User> list = userService.findAll();
    return new PageInfo<User>(list);
}


如何寫一個(gè)對(duì)用戶友好的接口


  • 問(wèn)題

    用戶有哪些,測(cè)試人員、開發(fā)人員、瀏覽器、調(diào)試工具、客戶端程序等

    開發(fā)人員拿到?jīng)]有統(tǒng)一格式的數(shù)據(jù),沒辦法分層處理

    為了方便框架解析、分層控制,有必要規(guī)范輸入輸出格式

  • 事例 getUser
{
    "id": 4354523,
    "name":"張三"
}

{
    "errorCode":-10000,
    "message":"未登錄"
}

{
    "bizCode":-1,
    "message":"所查用戶不存在"
}
{
    "code": 1,
    "message":"",
    "data":{
        "id": 4354523,
        "name":"張三"
    }
}

{
    "code": -10000,
    "message":"未登錄",
    "data":{
    }
}

{
    "code": -1,
    "message":"所查用戶不存在",
    "data":{
    }
}
  • 引入ViewData
public class ViewData<T> implements Serializable{

    private static final long serialVersionUID = 7408790903212368997L;

    private Integer code = 1;

    private String message;

    private T data;

    public ViewData(){}

    public ViewData(T obj) {
        this.data = obj;
    }

    public ViewData(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public ViewData(Integer code, String message, T obj) {
        this.code = code;
        this.message = message;
        this.data = obj;
    }

    public static <T> ViewData<T> ok() {
        return new ViewData<>();
    }

    public static <T> ViewData<T> ok(T obj) {
        return new ViewData<>(obj);
    }

    public static <T> ViewData<T> error(String msg) {
        return new ViewData<>(-1, msg);
    }

    public static <T> ViewData<T> error(Integer code, String msg) {
        return new ViewData<>(code, msg);
    }

}
  • 對(duì)輸出數(shù)據(jù)進(jìn)行格式化
package com.caad.springboot.test.api.resp;

public class UserResp {

    private Long id;
    private String username;
    private Date createTime;
    private GenderType gender;

}
@RequestMapping(value = "/find/{id}")
public ViewData<UserResp> find(@PathVariable("id") Long id) {
    User user = userService.findById(id);
    UserResp resp = new UserResp();
    resp.setCreateTime(user.getCreateTime());
    resp.setGender(user.getGender());
    resp.setUsername(user.getName());
    resp.setId(user.getId());
    return ViewData.ok(resp);
}
  • 使用@RequestBody對(duì)輸入?yún)?shù)格式化
package com.caad.springboot.test.api.requ;

public class UserRequ {

    private Long id;
    private String name;
    private String gender;

    public UserRequ() { }

    public UserRequ(Long id, String name, String gender) {
        this.id = id;
        this.name = name;
        this.gender = gender;
    }

}

@RequestMapping(value = "/update")
public ViewData<User> update(@RequestBody UserRequ userRequ) {
    User user = userService.findById(userRequ.getId());
    user.setName(userRequ.getName());
    user.setGender(GenderType.valueOf(userRequ.getGender()));
    userService.save(user);
    return ViewData.ok(user);
}
http://localhost:8080/user/update
{
    "id":1,
    "name":"hi boy",
    "gender":"MALE"
}

  • 自定義數(shù)據(jù)轉(zhuǎn)換
@Component
public class JsonDataSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if(o==null) return;
        if(o instanceof Long){
            Long data = (Long) o;
            jsonGenerator.writeString(data.toString());
        } if(o instanceof Date){
            Date date = (Date) o;
            jsonGenerator.writeString(date.getTime() + "");
        }
    }

}

要裝換的字段使用 @JsonSerialize(using = JsonDataSerializer.class)

處理空字段 @JsonInclude(Include.NON_NULL)

  • 使用map傳參
@RequestMapping(value = "/findByName")
public ViewData<List<User>> findByName(@RequestBody Map<String,String> params) {
    List<User> list = userService.findByName(params.get("name"));
    return ViewData.ok(list);
}
http://localhost:8080/user/findByName
{
    "name":"ZHANG"
}
  • 異常處理
public User addUser(User user) throws DataDuplicateException {
    List<User> list = dao.findByName(user.getName());
    if(list!=null && list.size()>0){
        throw new DataDuplicateException("用戶已經(jīng)存在");
    }
    return this.save(user);
}
package com.caad.springboot.test.common.exception;

public class DataDuplicateException extends RuntimeException {

    public DataDuplicateException() {
        super();
    }

    public DataDuplicateException(String message) {
        super(message);
    }

}
@RequestMapping(value = "/add")
public ViewData<User> add(@RequestBody UserRequ userRequ) {
    if(userRequ.getName()==null) return ViewData.error("用戶名未填寫");
    if(userRequ.getGender()==null) return ViewData.error("性別未填寫");
    User user = new User();
    user.setCreateTime(new Date());
    user.setGender(GenderType.valueOf(userRequ.getGender()));
    user.setName(userRequ.getName());
    try {
        userService.addUser(user);
    }catch (DataDuplicateException e){
        return ViewData.error(-1, e.getMessage());
    }
    return ViewData.ok(user);
}
http://localhost:8080/user/add
{
    "name":"testsa2",
    "gender":"MALE"
}
  • 刪除User接口
@RequestMapping(value = "/remove/{id}")
public ViewData remove(@PathVariable("id") Long id) {
    User user = new User();
    user.setId(id);
    user.setCreateTime(new Date());
    user.setGender(GenderType.UNKNOW);
    user.setName("test");
    userService.remove(user.getId());
    return ViewData.ok();
}

測(cè)試 http://localhost:8080/user/remove/1


mock模擬接口黑盒測(cè)試


  • 創(chuàng)建測(cè)試類UserControllerTest.java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserControllerTest{

    protected static final String SUCESS_CODE = "\"code\":1";

    @Autowired
    protected ObjectMapper objectMapper;

    protected MockMvc mvc;

    @Autowired
    UserController userController ;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(userController).build();
    }

    @Test
    public void testHelloController() throws Exception {

        //增加
        UserRequ requ = new UserRequ();
        requ.setName("01234567890123456789");
        requ.setGender("MALE");

        byte[] content = objectMapper.writeValueAsBytes(requ);

        RequestBuilder request = post("/user/add").accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(content);
        MvcResult result = mvc.perform(request).andExpect(status().isOk()).andExpect(content().string(containsString(SUCESS_CODE))).andReturn();
        Integer status = result.getResponse().getStatus();
        Assert.assertTrue("正確", status == 200);

        String json = result.getResponse().getContentAsString();
        JavaType javaType = getCollectionType(ViewData.class, User.class);

        ViewData vd = objectMapper.readValue(json, javaType);
        Assert.assertTrue("出現(xiàn)業(yè)務(wù)異常", vd.getCode()==1);

    }

    private JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

}
  • 將測(cè)試代碼進(jìn)行封裝,減少重復(fù)勞動(dòng)
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

public class ControllerSupperTest {

    protected static final String SUCESS_CODE = "\"code\":1";

    @Autowired
    protected ObjectMapper objectMapper;

    protected MockMvc mvc;

    protected ViewData<?> doPost(String uri, Object requ, Class... elementClasses) throws Exception {

        byte[] content = "{}".getBytes();
        if(requ!=null) content = objectMapper.writeValueAsBytes(requ);

        RequestBuilder request = post(uri).accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(content);
        //.andDo(MockMvcResultHandlers.print())
        MvcResult result = mvc.perform(request).andExpect(status().isOk()).andExpect(content().string(containsString(SUCESS_CODE))).andReturn();
        Integer status = result.getResponse().getStatus();
        Assert.assertTrue("正確", status == 200);

        String json = result.getResponse().getContentAsString();
        JavaType javaType = getCollectionType(ViewData.class, elementClasses);

        ViewData vd = objectMapper.readValue(json, javaType);
        Assert.assertTrue("出現(xiàn)業(yè)務(wù)異常", vd.getCode()==1);

        return vd;
    }

    private JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

}
  • 調(diào)整測(cè)試類 UserControllerTest.java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserControllerTest extends ControllerSupperTest{

    @Autowired
    UserController userController ;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(userController).build();
    }

    @Test
    public void testHelloController() throws Exception {

        User user = null;

        //增加
        {
            UserRequ requ = new UserRequ();
            requ.setName("01234567890123456789");
            requ.setGender("MALE");

            ViewData<User> data = (ViewData<User>) this.doPost("/user/add", requ, User.class);
            Assert.assertTrue(data.getCode()==1);
            user = data.getData();
        }

    }

}
  • 完善測(cè)試方法
//修改
{
    UserRequ requ = new UserRequ();
    requ.setId(user.getId());
    requ.setName("testHelloController");
    requ.setGender("FEMALE");

    ViewData<?> data = this.doPost("/user/update", requ, User.class);
    Assert.assertTrue(data.getCode()==1);
}

//查詢,主鍵
{
    ViewData<?> data = this.doPost("/user/find/" + user.getId(), null, UserResp.class);
    Assert.assertTrue(data.getCode()==1);
}

//查詢,name
{
    Map<String, String> map = new HashMap<>();
    map.put("name", "testHelloController");

    ViewData<?> data = this.doPost("/user/findByName", map, List.class);
    Assert.assertTrue(data.getCode()==1);
}

//查找,所有
{
    ViewData<?> data = this.doPost("/user/getAll", null, List.class);
    Assert.assertTrue(data.getCode()==1);
}

//查找,分頁(yè)
{
    ViewData<?> data = this.doPost("/user/pageAll",null, PageInfo.class);
    Assert.assertTrue(data.getCode()==1);
}

//刪除
{
    ViewData<?> data = this.doPost("/user/remove/" + user.getId(), null, PageInfo.class);
    Assert.assertTrue(data.getCode()==1);
}


抽取抽象邏輯,封裝通用代碼,控制框架行為


  • 抽取父類
public abstract class GenericService<T extends BaseEntity> {

    @Autowired
    LongIdGenerator idGenerator;

    protected abstract GenericDao<T> getDao();

    public T findById(Long id) {
        return getDao().findById(id);
    }

    public T save(T entity){
        if(entity==null) return null;
        if(entity.getId()==null){
            entity.setId(idGenerator.generate());
            getDao().insert(entity);
        }else {
            getDao().update(entity);
        }
        return entity;
    }

    public void remove(Long id){
        getDao().delete(id);
    }

    public List<T> findAll(){
        return getDao().findAll();
    }

}

public interface GenericDao<T extends BaseEntity> {

    public T findById(Long id);

    void insert(T entity);

    void delete(Long id);

    void update(T entity);

    List<T> findAll();

}
public abstract class BaseEntity {

    public abstract Long getId();

    public abstract void setId(Long id);

}
  • 使用泛型主鍵
public abstract class BaseEntity<PK extends Serializable> {

    public abstract PK getId();

    public abstract void setId(PK id);

}

public interface GenericDao<T extends BaseEntity, PK extends Serializable> {

    public T findById(PK id);

    void insert(T entity);

    void delete(PK id);

    void update(T entity);

    List<T> findAll();

}
public interface GenericDao<T extends BaseEntity, PK extends Serializable> {

    public T findById(PK id);

    void insert(T entity);

    void delete(PK id);

    void update(T entity);

    List<T> findAll();

}

持續(xù)集成


  • 添加多環(huán)境配置
spring.profiles.active: '@profile.active@'
server.port: 8888

mybatis:
  config-locations: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
  type-aliases-package: com.caad.springboot.test.domain

---
spring:
  profiles: native
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://172.16.2.154:3307/aqs_test?useUnicode=true&characterEncoding=utf-8
    username: aqs
    password: r5rD6a8NBnWP9NGs

---
spring:
  profiles: dev
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://sql10.freemysqlhosting.net:3306/sql10210303?useUnicode=true&characterEncoding=utf-8
    username: sql10210303
    password: mWsVRVGwXD
---
  • 修改pom.xm
<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <profile.active>dev</profile.active>
        </properties>
    </profile>
    <profile>
        <id>native</id>
        <properties>
            <profile.active>native</profile.active>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
</profiles>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <resources>
            <resource>
                <directory>src/main/resources/</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </configuration>
</plugin>

jenkins腳本

cd /root/.jenkins/workspace/test-1
mvn clean install -P dev
count=`ps -ef | grep Mybatis4Springboot | grep -v grep | wc -l`
if [ $count -gt 0 ];then
ps -ef | grep Mybatis4Springboot | grep -v grep | grep -v PID | awk '{print $2}' | xargs kill -9
fi
rm -rf /home/microservice/Mybatis4Springboot.jar
cp -rf ./target/Mybatis4Springboot-1.0.0-SNAPSHOT.jar /home/microservice/Mybatis4Springboot.jar

BUILD_ID=dontKillMe

nohup java -jar /home/microservice/Mybatis4Springboot.jar -Xmx512m -Xss256k >/dev/null &

其他資源


本文源碼地址 https://github.com/bestaone/Mybatis4Springboot

阿里代碼規(guī)范插件 https://github.com/alibaba/p3c

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,253評(píng)論 6 342
  • 算了,金子就這么悠悠蕩蕩地晃著吧! 草稞里,金子的大鼻頭探了過(guò)去,我定睛一看,頓時(shí)魂飛天外! “蛇!”大喊著飛也似...
    湖畔風(fēng)聲閱讀 278評(píng)論 0 0
  • 畢業(yè)季,總是最難的時(shí)候。 一姑娘跟我訴苦,說(shuō)工作難找,兩情相悅的工作更不好找,說(shuō)家里人對(duì)她抱著很大的期望,在大城市...
    南溪姑娘閱讀 323評(píng)論 1 2

友情鏈接更多精彩內(nèi)容