美文网首页SpringBoot
Springboot2快速集成

Springboot2快速集成

作者: 码道功臣 | 来源:发表于2018-11-20 16:38 被阅读21次
    1. 通过一个实例快速搭建SSM。
    2. 通过实例来看看各个环节的最佳实践。
    3. 如何使用业务异常。
    4. 如何设计一个对前端友好的接口。
    5. 通过单元测试使你的工作更加轻松和安全。
    6. 简单聊聊代码规范。
    7. 利用CI/CD来帮助你处理繁杂的重复性工作。

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

    Spring5、springboot2近况


    • spring5

      最大的亮点是 Spring webflux。Spring webflux 是一个新的非堵塞函数式 Reactive Web 框架,可以用来建立异步的,非阻塞,事件驱动的服务,并且扩展性非常好。

    • springboot

      集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。

    一分钟helloworld看看新姿势


    • 创建文件

      新建项目Demo

      创建文件 pom.xml

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

      创建包 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>
    

    创建启动类 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);
        }
        
    }
    
    • 运行
    mvn install
    mvn spring-boot:run
    
    • 测试
    http://localhost:8080/
    

    标准的三层模型


    • 目录结构
    - com.caad.springboot.test
        - controller
            - UserController.java
        - service
            - UserService.java
        - dao
            - UserDao.java
        - domain
            - enums
                - GenderType.java
            - User.java
        - Application.java
    

    为什么service不使用interface了

    • 代码实现
    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>
    
        <!-- 全局参数 -->
        <settings>
            <!-- 使全局的映射器启用或禁用缓存。 -->
            <setting name="cacheEnabled" value="true"/>
            <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
            <setting name="lazyLoadingEnabled" value="true"/>
            <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->
            <setting name="aggressiveLazyLoading" value="true"/>
            <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->
            <setting name="multipleResultSetsEnabled" value="true"/>
            <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
            <setting name="useColumnLabel" value="true"/>
            <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->
            <setting name="useGeneratedKeys" value="true"/>
            <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->
            <setting name="autoMappingBehavior" value="PARTIAL"/>
            <!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->
            <!-- 对于批量更新操作缓存SQL以提高性能 BATCH,SIMPLE -->
            <setting name="defaultExecutorType" value="BATCH"/>
            <!-- 数据库超过25000秒仍未响应则超时 -->
            <setting name="defaultStatementTimeout" value="25000" />
            <!-- 使用驼峰命名法转换字段。 -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
            <setting name="localCacheScope" value="SESSION"/>
            <!-- 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型 -->
            <setting name="jdbcTypeForNull" value="NULL"/>
            <!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 -->
            <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);
    
    }
    
    • 初始化数据库
    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();
        }
    
    }
    
    • 添加单元测试依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    
    • 编写测试类
    @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方式实现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">
    
        <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
        <property name="LOG_HOME" value="./logs" />
        <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%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 在启动的时候初始化各个 Bean 的信息 -->
        <!-- level:用来设置打印级别,大小写无关(最常用的几种):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"/>
    
        <!-- 控制台输出 -->
        <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>
    
        <!-- 日志输出级别 -->
        <root level="INFO">
            <appender-ref ref="STDOUT" />
            <appender-ref ref="FILE" />
        </root>
    
    </configuration>
    
    • 添加测试代码
    List<User> list = service.findByName(o.getName());
    Assert.assertNotNull(list.size()>0);
    
    • 几次失误,导致了脏数据,映入测试回滚
    @Transactional
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = Application.class)
    public class UserServiceTest {
    
    • 添加controller端findAll接口
    @RequestMapping(value = "/getAll")
    public List<User> getAll() {
        return userService.findAll();
    }
    
    • 引入分页插件
    <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);
    }
    

    如何写一个对用户友好的接口


    • 问题

      用户有哪些,测试人员、开发人员、浏览器、调试工具、客户端程序等

      开发人员拿到没有统一格式的数据,没办法分层处理

      为了方便框架解析、分层控制,有必要规范输入输出格式

    • 事例 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);
        }
    
    }
    
    • 对输出数据进行格式化
    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对输入参数格式化
    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"
    }
    
    
    • 自定义数据转换
    @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("用户已经存在");
        }
        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();
    }
    

    测试 http://localhost:8080/user/remove/1

    mock模拟接口黑盒测试


    • 创建测试类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("出现业务异常", vd.getCode()==1);
    
        }
    
        private JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
            return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
        }
    
    }
    
    • 将测试代码进行封装,减少重复劳动
    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("出现业务异常", vd.getCode()==1);
    
            return vd;
        }
    
        private JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
            return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
        }
    
    }
    
    • 调整测试类 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();
            }
    
        }
    
    }
    
    • 完善测试方法
    //修改
    {
        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);
    }
    
    //查找,分页
    {
        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();
    
    }
    

    持续集成


    • 添加多环境配置
    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

    阿里代码规范插件 https://github.com/alibaba/p3c

    相关文章

      网友评论

        本文标题:Springboot2快速集成

        本文链接:https://www.haomeiwen.com/subject/uiexqqtx.html