1. Spring Boot + Spring MVC + Spring Core + MyBatis + Druid整合案例

1. 在pom.xml中引入需要的依赖

  1. <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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  2. <modelVersion>4.0.0</modelVersion>
  3. <groupId>com.example</groupId>
  4. <artifactId>springboot-demo01</artifactId>
  5. <version>1.0.0-SNAPSHOT</version>
  6. <name>springboot-demo01</name>
  7. <!-- 继承spring boot的父工程 -->
  8. <!-- spring boot父工程直接约束了常用依赖的版本 -->
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>1.5.9.RELEASE</version>
  13. </parent>
  14. <dependencies>
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter-web</artifactId>
  18. </dependency>
  19. <!-- 引入mybatis-spring-boot-starter依赖,用于mybatis与spring boot整合 -->
  20. <dependency>
  21. <groupId>org.mybatis.spring.boot</groupId>
  22. <artifactId>mybatis-spring-boot-starter</artifactId>
  23. <version>1.3.1</version>
  24. </dependency>
  25. <!-- 引入mysql驱动依赖 -->
  26. <dependency>
  27. <groupId>mysql</groupId>
  28. <artifactId>mysql-connector-java</artifactId>
  29. <scope>runtime</scope>
  30. </dependency>
  31. <!-- 引入spring-boot-starter-data-jpa依赖 -->
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-data-jpa</artifactId>
  35. </dependency>
  36. <!-- 引入阿里的druid连接池依赖 -->
  37. <dependency>
  38. <groupId>com.alibaba</groupId>
  39. <artifactId>druid</artifactId>
  40. <version>1.1.6</version>
  41. </dependency>
  42. </dependencies>
  43. <!-- 引入一个spring boot插件,可以支持我们打包程序 -->
  44. <!-- 打包时是需要将所有依赖的第三方jar包都打进来的,spring boot这个插件可以支持 -->
  45. <build>
  46. <plugins>
  47. <plugin>
  48. <groupId>org.springframework.boot</groupId>
  49. <artifactId>spring-boot-maven-plugin</artifactId>
  50. </plugin>
  51. </plugins>
  52. </build>
  53. </project>

2. 在application.properties配置文件中配置druid连接池

  1. # 驱动配置
  2. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  3. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/oa?useUnicode=true&characterEncoding=utf-8
  4. spring.datasource.username=root
  5. spring.datasource.password=root
  6. spring.datasource.driverClassName=com.mysql.jdbc.Driver
  7. # 连接池的配置信息
  8. # 连接池初始大小
  9. spring.datasource.initialSize=5
  10. # 连接池最小空闲连接数量
  11. spring.datasource.minIdle=5
  12. # 连接池最大活跃连接数量
  13. spring.datasource.maxActive=20
  14. spring.datasource.maxWait=60000
  15. spring.datasource.timeBetweenEvictionRunsMillis=60000
  16. spring.datasource.minEvictableIdleTimeMillis=300000
  17. spring.datasource.validationQuery=SELECT 1 FROM DUAL
  18. spring.datasource.testWhileIdle=true
  19. spring.datasource.testOnBorrow=false
  20. spring.datasource.testOnReturn=false
  21. spring.datasource.poolPreparedStatements=true
  22. spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
  23. spring.datasource.filters=stat,wall,log4j
  24. spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

3. 编写Druid连接池配置管理类

  1. package com.example.springboot.config;
  2. // 使用@Configuration标注,表示这是一个配置管理类
  3. @Configuration
  4. public class DruidDBConfig {
  5. // 因为spring boot是默认开启了资源过滤的
  6. // 所以这里的配置,都会自动从application.properties配置文件中加载出来,设置到这个@Configuration类中
  7. @Value("${spring.datasource.url}")
  8. private String dbUrl;
  9. @Value("${spring.datasource.username}")
  10. private String username;
  11. @Value("${spring.datasource.password}")
  12. private String password;
  13. @Value("${spring.datasource.driverClassName}")
  14. private String driverClassName;
  15. @Value("${spring.datasource.initialSize}")
  16. private int initialSize;
  17. @Value("${spring.datasource.minIdle}")
  18. private int minIdle;
  19. @Value("${spring.datasource.maxActive}")
  20. private int maxActive;
  21. @Value("${spring.datasource.maxWait}")
  22. private int maxWait;
  23. @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
  24. private int timeBetweenEvictionRunsMillis;
  25. @Value("${spring.datasource.minEvictableIdleTimeMillis}")
  26. private int minEvictableIdleTimeMillis;
  27. @Value("${spring.datasource.validationQuery}")
  28. private String validationQuery;
  29. @Value("${spring.datasource.testWhileIdle}")
  30. private boolean testWhileIdle;
  31. @Value("${spring.datasource.testOnBorrow}")
  32. private boolean testOnBorrow;
  33. @Value("${spring.datasource.testOnReturn}")
  34. private boolean testOnReturn;
  35. @Value("${spring.datasource.poolPreparedStatements}")
  36. private boolean poolPreparedStatements;
  37. @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
  38. private int maxPoolPreparedStatementPerConnectionSize;
  39. @Value("${spring.datasource.filters}")
  40. private String filters;
  41. @Value("{spring.datasource.connectionProperties}")
  42. private String connectionProperties;
  43. // 在这个配置类中,直接基于配置信息,创建出了一个bean
  44. // 这个bean就是一个DataSource
  45. // 这个DataSource bean就会被纳入spring容器的管理范围之内
  46. @Bean
  47. @Primary
  48. public DataSource dataSource() {
  49. // 这里就是用外部加载进来的配置信息,创建出来一个Druid连接池
  50. DruidDataSource datasource = new DruidDataSource();
  51. datasource.setUrl(this.dbUrl);
  52. datasource.setUsername(username);
  53. datasource.setPassword(password);
  54. datasource.setDriverClassName(driverClassName);
  55. //configuration
  56. datasource.setInitialSize(initialSize);
  57. datasource.setMinIdle(minIdle);
  58. datasource.setMaxActive(maxActive);
  59. datasource.setMaxWait(maxWait);
  60. datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
  61. datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
  62. datasource.setValidationQuery(validationQuery);
  63. datasource.setTestWhileIdle(testWhileIdle);
  64. datasource.setTestOnBorrow(testOnBorrow);
  65. datasource.setTestOnReturn(testOnReturn);
  66. datasource.setPoolPreparedStatements(poolPreparedStatements);
  67. datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
  68. try {
  69. datasource.setFilters(filters);
  70. } catch (SQLException e) {
  71. e.printStackTrace();
  72. }
  73. datasource.setConnectionProperties(connectionProperties);
  74. return (DataSource) datasource;
  75. }
  76. }

编写这个类之后,还需要在Application类中导入这个配置管理类

@SpringBootApplication
// 使用@Import就可以将其他的配置管理类导入进来
@Import(DruidDBConfig.class)
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

4. 编写Domain类

package com.example.springboot.domain;

public class User {
    private Long id;
    private String name;
    private Integer age;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

5. 编写Mapper类

package com.example.springboot.mapper;

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM user")
    List<User> listUsers();

    @Select("SELECT * FROM user WHERE user_id = #{userId}")
    User getUserById(@Param("userId") Long userId);

    @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
    void saveUser(User user);

    @Update("UPDATE user SET name=#{name}, age=#{age} WHERE user_id=#{userId}")
    void updateUser(User user);

    @Delete("DELETE FROM user WHERE user_id=#{userId}")
    void removeUser(@Param("userId") Long userId);
}

6. 编写Dao

package com.example.springboot.dao;

public interface UserDao {
    List<User> listUsers();
    User getUserById(Long userId);
    void saveUser(User user);
    void updateUser(User user);
    void removeUser(Long userId);
}
package com.example.springboot.dao.impl;

@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> listUsers() {
        return userMapper.listUsers();
    }

    @Override
    public User getUserById(Long userId) {
        return userMapper.getUserById(userId);
    }

    @Override
    public void saveUser(User user) {
        userMapper.saveUser(user);
    }

    @Override
    public void updateUser(User user) {
        userMapper.updateUser(user);
    }

    @Override
    public void removeUser(Long userId) {
        userMapper.removeUser(userId);
    }
}

7. 编写Service

package com.example.springboot.service;

public interface UserService {
    List<User> listUsers();
    User getUserById(Long userId);
    void saveUser(User user);
    void updateUser(User user);
    void removeUser(Long userId);
}
package com.example.springboot.service.impl;

@Service
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao;

    @Override
    public List<User> listUsers() {
        return userDao.listUsers();
    }

    @Override
    public User getUserById(Long userId) {
        return userDao.getUserById(userId);
    }

    @Override
    public void saveUser(User user) {
        userDao.saveUser(user);
    }

    @Override
    public void updateUser(User user) {
        userDao.updateUser(user);
    }

    @Override
    public void removeUser(Long userId) {
        userDao.removeUser(userId);
    }

}

8. 编写controller

package com.example.springboot.controller;

// 这里直接在类级别给一个/user的映射,就是代表用户管理的请求都会到这里来
@RequestMapping(value="/user")
public class UserController {
    @Autowired
    private UserService userService;

    // GET请求代表着是查询数据
    @RequestMapping(value="/", method=RequestMethod.GET)
    public List<User> listUsers() {
        return userService.listUsers();
    }

    // GET请求+{id}代表的是查询某个用户
    @RequestMapping(value="/{userId}", method=RequestMethod.GET) 
    public User getUser(@PathVariable("userId") Long userId) { 
        return userService.getUserById(userId);
    }

    // POST请求代表着是新增数据
    @RequestMapping(value="/", method=RequestMethod.POST) 
    public String saveUser(User user) { 
        userService.saveUser(user); 
        return "success"; 
    }

    // PUT请求代表的是更新
    @RequestMapping(value="/{userId}", method=RequestMethod.PUT) 
    public String updateUser(@PathVariable("userId") Long userId, User user) {
        user.setId(userId);
        userService.updateUser(user);
        return "success"; 
    } 

    // DELETE请求代表的是删除
    @RequestMapping(value="/{userId}", method=RequestMethod.DELETE) 
    public String deleteUser(@PathVariable("userId") Long userId) { 
        userService.removeUser(userId);
        return "success"; 
    }
}

9. 启动程序

启动程序,然后浏览器访问:http://localhost:8080/employee/1

此时会在浏览器中看到,将id=1的员工信息查询了出来,显示到了浏览器中

curl命令的使用,通过curl命令,可以替代浏览器去进行RESTful接口的一个测试,git命令行里面,就可以直接使用curl命令

curl -XPOST URL -d "param1=value1&param2=value2"
curl -XGET URL
curl -XDELETE URL
curl -XPUT URL
curl -XPUT URL -H 'Content-Type:application/json' -d'
{
    "param1":"value1",
    "param2":"value2"
}

10. 测试

在spring boot做restful接口的时候,PUT请求和DELETE请求是有问题的,因为spring mvc对RESTful接口的支持会有一点问题

所以在做开发的时候,一般对于PUT,POST,请求,他们的请求参数,都不是用表单直接提交的,一般要求是用ajax走content-type是application/json的类型,通过json类型发送请求参数过来
image.png

2. 梳理一下此时的几个框架整合的思路

(1)系统启动的时候,首先会去扫描DruidDBConfig类,这就可以将外部的druid连接池配置加载进来,同时初始化出来一个druid连接池的DataSource bean

(2)mybatis-spring-boot-starter接着开始干活儿,发现了一个DataSource bean,就会将其注入SqlSessionFactory,再创建SqlSessionTemplate,接着扫描Mapper接口,将SqlSessionTemplate注入每个Mapper,然后将Mapper放入spring容器中来管理

(3)spring boot的@ComponantScan注解开始干活儿,自动扫描所有的bean,依次初始化实例以及注入依赖,EmployeeServiceImpl(将EmployeeMapper注入其中),EmployeeCongtroller(将EmployeeServiceImpl注入其中)

(4)此时浏览器发送请求,会先由controlller处理,接着调用service,再调用mapper。mapper底层会基于druid连接池访问到数据库,进行数据操作

至此,最常规的几个框架整合和使用,就完成了。

3. Demo中涉及的知识

1. spring-boot-starter-parent的作用

(1)默认的编译级别是 JDK 1.6
(2)指定了 UTF-8 为 source coding
(3)有一个 声明,对 spring boot 可以集成的大部分流行第三方依赖,都指定了对应的可以互相兼容的版本
(4)开启了资源过滤器,支持对应资源文件中占位符的替换
(5)指定了多个插件:exec、surefire、git commit id、shade,用来支持运行、测试、版本控制、打包

2. 不直接继承spring-boot-starter-parent

如果要继承自己的 parent 工程,而不是直接继承 spring-boot-starter-parent,那么可以考虑使用 import 方式,将 spring-boot-start-parent 中的 dependency management 引入进来。但是这样的话,就没法用到 plugin management 了。

<dependencyManagement>
     <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.9.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3. 自己指定JDK版本

<properties>
    <java.version>1.8</java.version>
</properties>

4. 使用spring boot的打包插件

spring-boot-starter-parent 指定了 pluginManagement,配置好了一系列的插件,其中一个就是用于打成可执行 jar 包的 shade 插件。如果我们要使用的话,需要自己手动声明这个插件,然后通过 mvn pakcage 命令就可以将工程打包成可以执行的 jar 包了。

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

5. spring-boot-starter-*

spring-boot 提供了一些列的 starter 类依赖,跟流行的常用项目进行整合,比如mybatis、redis、mongodb、elasticsearch,等等。我们只要声明对应的 spring-boot-starter-*,就可以直接使用对应版本的依赖,多个依赖的版本都是兼容的。可能就是通过 application.properties,注解,少量的配置,就可以快速整合进来一个技术开始使用。

6. 基于spring boot进行开发需要遵守的约定规则

一般建议基于 spring boot 来开发的时候,按照 spring boot 约定的规则来设置我们的包结构、代码布局,比如下面这样,这样 spring boot 在按照约定搜索各种注解的时候,一般不会出现什么问题。

com
 +- example
     +- springboot
         +- Application.java
         |
         +- domain
         |   +- User.java
         +- dao
         |   +- UserDao.java
         |
         +- service
         |   +- UserService.java
         |
         +- controller
             +- UserController.java

用于启动的类一般会按照下面这样来写

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

7. Configuration类

spring boot 的一个核心思想,就是尽量消除掉各种复杂的 xml 配置文件,所以一般建议如果要进行一些配置,可以采用 Configuration 类,在 Java 类中做一些配置。

而且通常比较好的实践是,就将用来放 main 方法的 Application 类,作为 Configuration 类,所以一般会给 Application 类加@Configuration 注解。

如果不想将所有的配置都放在 Application 类中,也可以使用 @Import 注解将其他的 Configuration 类引入进来,或者是依靠@ComponentScan 注解自动扫描其他的 Configuration 类。

即使一定要使用一个 xml 配置文件,建议是用 @ImportResource 来导入一个 xml 配置文件。

8. Auto Configuration

(1)什么是Auto Configuration

Auto Configuration是spring boot中非常核心的一个功能,因为spring boot的一个核心思想,就是尽可能减少我们要配置的东西,尽量才有约定规则自动完成一些配置。这个Auto Configuration功能,就会根据我们引入的依赖,来推测我们做什么事情,然后自动给我们完成需要的一些配置。举个例子,如果依赖了HSQLDB,那么spring boot会基于HSQLDB自动配置和创建一个内存数据库。

(2)如何启用Auto Configuration

我们给@Configuration类,增加了@EnableAutoConfiguration注解之后,就会开启Auto Configuration功能,而且一般推荐使用使用@EnableAutoConfiguration开启Auto Configuration功能。

9. 与Spring核心框架进行整合使用

spring boot 可以无缝与 spring 框架进行整合使用,一般就是在 Application 类上加 @ComponentScan 注解,启用自动扫描所有的 spring bean,同时搭配好 @Autowired 注解来进行自动装配。只要按照上面的那个约定,将 Application 类放在最顶层的包结构中,那么使用了 @ComponentScan 之后,就可以自动扫描和搜索到所有的 spring bean,包括了:@Component、@Contorller、@Service、@Repository

10. @SpringBootApplication

大多数的spring boot项目,都会在main类上标注:@Configuration、@EnableAutoConfiguration、@ComponantScan,因为这个实在是太过于常见了,所以spring boot提供了一个@SpringBootApplication注解,这个注解就相当于是上面3个注解的综合。所以实际上在开发的时候,一般就会在main类上加一个@SpringBootApplication注解

11. 启动spring boot程序

spring boot的另外一个核心思想,就是尽量简化各种应用的开发。

比如最常见的web应用程序,如果使用传统的方式来开发,需要安装和配置tomcat/jetty服务器,然后还需要将web程序打包后放入tomcat中才可以运行。或者是需要将eclipse等ide工具配置支持tomcat,才能在eclipse中直接启动web程序。

而在spring boot中,直接基于了内置的tomcat/jetty容器,可以在写好代码之后,一键启动web程序,然后就可以在浏览器中访问了。对于本地开发、测试以及debug个调试,都是非常的方便的

(1)在IDE中启动spring boot程序

直接运行main方法即可,默认会绑定在8080端口上

(2)在命令行执行jar包来启动spring boot程序

使用mvn package将应用程序打成一个可以执行的jar包之后,就可以在命令行使用jar -jar命令来启动了,scp命令,自行百度,拷贝到线上的服务器

$ java -jar target/myproject-0.0.1-SNAPSHOT.jar

同样在启动spring boot应用程序时可以传递一些参数

$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8082,suspend=n \
-jar target/springboot-demo-1.0.0.jar

(3)在命令行直接启动spring boot程序
也可以使用spring-boot插件的run goal来直接在命令行基于代码启动spring boot程序,几乎很少用

$ mvn spring-boot:run