springboot 完整企业项目搭建实记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013187139/article/details/68944972

昨天搭建ssm框架时突然想到可以搭建springboot来完美解决配置复杂的问题,今天学习了一下springboot的搭建,在此记录一下搭建的过程和踩过的坑

这里给自己定一个该框架搭建完成的目标,如下
框架要求功能:
- 处理http/json 请求
- 日志记录
- 持久化
- 数据源,事务控制
- 定时任务
- 视图模版

搭建环境:
- 编译器:idea 2016.2.4
- Maven : maven3.0
- JDK: java7
- 系统: mac OS 10.10.4
- 数据库: mysql5.6

2017-03-29
搭建记录

  1. 新建maven应用(不需要web应用)
  2. 在pom中添加以下配置
    <!-- Spring boot 父引用-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
    </parent>

    <!-- Spring boot 核心web-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
  1. 新建controller
@Controller
@EnableAutoConfiguration
public class TestBootController {

    @RequestMapping("/")
    @ResponseBody
    String home() {
    return "hello world";
    }

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

此时的项目目录结构是这样的

项目结构

  1. 启动main函数,可以看到控制台输出,非常简单明了

启动日志

  1. 端口默认是8080,启动log已经写了,访问后如图所示
    访问

  2. 在搭建了基础应用的基础上,想增加service层抽离控制层和业务层代码

//业务层接口
public interface TestInterFace {

    public int testInterFace();

    public User testUser();
}

//业务层接口实现
@Service
public class TestInterFaceImpl implements TestInterFace {
    @Override public int testInterFace() {
    return 0;
    }

    @Override public User testUser() {
    return new User();
    }
}

修改controller层代码

@Controller
@EnableAutoConfiguration
public class TestBootController {
    @Autowired
    private TestInterFace testInterFace;

    @RequestMapping("/num")
    @ResponseBody
    int home() {
    int i = testInterFace.testInterFace();
    return i;
    }
    @RequestMapping("/get")
    @ResponseBody User getUser() {
    return testInterFace.testUser();
    }

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

}

启动后抛异常

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘testBootController’: Unsatisfied dependency expressed through field ‘testInterFace’: No qualifying bean of type [com.kx.springboot.service.TestInterFace] found for dependency [com.kx.springboot.service.TestInterFace]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.kx.springboot.service.TestInterFace] found for dependency [com.kx.springboot.service.TestInterFace]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

相信大家对这种异常非常常见,注入失败,这时候问题来了,按照传统的方式,对注入进行检查。service层已经加了@service的注解,怎么还是注入失败了呢,想查看配置文件,发现springboot没有配置文件,那么他是怎么扫描注解的呢?

百度了一下才知道,springboot默认按照包顺序注入,所以在创建controller时service还没有注入,springboot不需要传统的xml配置扫描包,只需要添加注解@ComponentScan(basePackages={“com.kx.springboot.service”}),代码如下

@Controller
@EnableAutoConfiguration
@ComponentScan(basePackages={"com.kx.springboot.service"})//添加的注解
public class TestBootController {
    @Autowired
    private TestInterFace testInterFace;

    @RequestMapping("/num")
    @ResponseBody
    int home() {
    int i = testInterFace.testInterFace();
    return i;
    }
    @RequestMapping("/get")
    @ResponseBody User getUser() {
    return testInterFace.testUser();
    }

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

}

再次启动,成功,访问http://localhost:8088/get

{“username”:”username寇鑫123”,”password”:”password寇鑫123”}

  1. 这时候,又有一个问题,web应用都是多controller,这种启动方式一次只能启动一个controller,那么,怎么才能启动应用后访问多个controller,查阅springboot官方教程后,修改代码如下

增加 UserController

@Controller
@RequestMapping("user")
public class UserController {
    @Autowired
    private TestInterFace testInterFace;

    @RequestMapping("/get")
    @ResponseBody User getUser() {
    return testInterFace.testUser();
    }
}

修改原来 TestBootController

@Controller
@RequestMapping("test")
public class TestBootController {
    @Autowired
    private TestInterFace testInterFace;

    @RequestMapping("/num")
    @ResponseBody
    int home() {
    int i = testInterFace.testInterFace();
    return i;
    }

    @RequestMapping("/get")
    @ResponseBody User getUser() {
    return testInterFace.testUser();
    }
}

在包的最外层增加新的应用启动入口 —> Application

@EnableAutoConfiguration
@ComponentScan(basePackages={"com.kx.springboot"})
public class Application {
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
}

可以看到,应用启动入口配置了扫描所有包。此时整个项目的结构如图所示
修改后项目结构

  1. 启动应用后访问

http://localhost:8080/test/get

{“username”:”username寇鑫123”,”password”:”password寇鑫123”}

http://localhost:8080/user/get

{“username”:”username寇鑫123”,”password”:”password寇鑫123”}

到此,符合处理http/json 的web框架已经搭建ok了


============2017-3-30 =============

第一步处理http/json已经完成了,现在给我们的框架里加上日志记录的功能

要求:
- 日志按天记录,自动生成当天的记录文件
- 日志分级存储(info,error)

Springboot自带日志,所以我们现在直接在SpringBoot中添加日志

修改 TestController

@Controller
@RequestMapping("test")
public class TestBootController {
//增加日志
    private final Logger log = LoggerFactory.getLogger(TestBootController.class);

    @Autowired
    private TestInterFace testInterFace;

    @RequestMapping("/num")
    @ResponseBody
    int home() {
    int i = testInterFace.testInterFace();
    return i;
    }
    @RequestMapping("/get")
    @ResponseBody User getUser() {
    //打印日志
    log.info("TestBootController getUser info");
    return testInterFace.testUser();
    }
}

只增加了两行代码,现在启动尝试打印日志

访问 http://localhost:8080/test/get

第一次日志

可以看到我们的日志已经打出来了,那么现在又有疑问了,这个日志只是打在控制台了,并没有生成文件,查询后发现默认日志如果要打印生成文件需要添加application.properties,而添加这个只可以打固定名称格式的日志,并不能灵活的配置,所以添加Springboot默认支持的logback作为标准日志输出

添加新的pom依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

在resource下添加logback配置文件logback.xml

首先配置按天生成日志,尝试日志文件打印输出

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/Users/kx/Desktop" />
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/TestSpringBoot.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <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>

启动应用,尝试输出日志

访问 http://localhost:8080/test/get

第二次日志

好吧,首先发现我们的日志不是彩色的了,哭~,不过还好日志打出来了,那么看看我们的文件有没有生成

//生成文件
-rw-r--r--  1 kx  staff   5117  3 30 22:19 TestSpringBoot.log.2017-03-30.log

//info 日志打印效果
2017-03-30 22:21:03.341 [http-nio-8080-exec-3] INFO  com.kx.springboot.controller.TestBootController - TestBootController getUser info

OK,文件也完美的按照我们指定的路径生成了,并且文件命名方式按照配置的日期命名,日志打印的内容也按照配置的内容正确的打印了,现在配置info日志和error日志,区分info和error打出来的日志文件

修改 logback.log 增加新的error appender 修改原来的appender 为 info

<?xml version="1.0" encoding="UTF-8"?>
<!--
    Copyright 2010-2011 The myBatis Team
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/Users/kx/Desktop" />
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--修改原来的 appender 为info-->
    <!-- 按照每天生成日志文件 -->
    <appender name="DAYINFO"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/TestSpringBoot_info.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <!--这里设置日志级别为info-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!--新增加的error appender-->
    <appender name="DAYERROR"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/TestSpringBoot_error.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
         <!--这里设置日志级别为error-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="DAYINFO" />
        <appender-ref ref="DAYERROR" />
    </root>
</configuration>

启动应用后发现已经新增了两个日志文件,命名完全按照配置

-rw-r--r--  1 kx  staff      0  3 30 22:19 TestSpringBoot_error.log.2017-03-30.log
-rw-r--r--  1 kx  staff   5117  3 30 22:19 TestSpringBoot_info.log.2017-03-30.log

访问 http://localhost:8080/test/get

//info 日志打印效果
2017-03-30 22:21:03.341 [http-nio-8080-exec-3] INFO  com.kx.springboot.controller.TestBootController - TestBootController getUser info

//error 日志打印效果
2017-03-30 22:21:03.342 [http-nio-8080-exec-3] ERROR com.kx.springboot.controller.TestBootController - TestBootController getUser error

我们发现error日志和info日志已经按照我们的要求已经打印到对应的文件中了,那么现在我们想解决一下控制台彩色输出的模块,刚好百度到了这里的代码(珍藏)

    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
    <!-- Console 输出设置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

看一下我们现在的logback日志总文件

<?xml version="1.0" encoding="UTF-8"?>
<!--
    Copyright 2010-2011 The myBatis Team
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<configuration>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/Users/kx/Desktop" />


    <!-- 彩色日志 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
    <!-- Console 输出设置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 不用彩色控制台输出 -->
    <!--<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">-->
        <!--<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">-->
            <!--&lt;!&ndash;格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符&ndash;&gt;-->
            <!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>-->
        <!--</encoder>-->
    <!--</appender>-->
    <!-- 按照每天生成日志文件 -->
    <appender name="DAYINFO"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/TestSpringBoot_info.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <appender name="DAYERROR"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/TestSpringBoot_error.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!-- 日志输出级别 -->

    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="DAYERROR" />
        <appender-ref ref="DAYINFO" />
    </root>
</configuration>

再次运行项目,访问 http://localhost:8080/test/get

这里写图片描述

哈哈哈,我们的彩色日志又回来了,而且结构清晰,调试代码的时候简直神器啊

2017-04-01

============整合Mybatis============

在众多orm框架中,我对mybatis最熟悉,所以我采用mybatis进行整合,对我们的orm框架,这里我们也提出几点要求

  • 支持分页
  • curd接口抽象处理
  • 事务控制
  • 多数据源

在查阅了一些资料后,找到目前为止最简单的一种整合mybatis方式,这里贴出原始博客地址

http://blog.didispace.com/springbootmybatis/

参照大神的整合,我们的整合异常的顺利,贴出我们增加的代码

在pom中添加以下依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.21</version>
</dependency>

在application.properties 中增加以下配置

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

表结构

Create Table: CREATE TABLE `userinfo` (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `password` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8

编写dao层代码,新建UserDao 接口

@Mapper
public interface UserDao {
    @Select("SELECT * FROM USERINFO WHERE username = #{username}")
    UserInfo findByName(@Param("username") String username);

    @Insert("INSERT INTO USERINFO(username, password) VALUES(#{username}, #{password})")
    int insert(@Param("username") String name, @Param("password") String password);

}

在业务层增加对dao层接口的引用,修改TestInterFace,TestInterFaceImpl

public interface TestInterFace {

    public int testInterFace();

    public UserInfo testUser();

    public int insertUser(String username,String password);//新增的接口
}


@Service
public class TestInterFaceImpl implements TestInterFace {
    //引入dao层接口
    @Autowired UserDao userDao;
    @Override public int testInterFace() {
    return 0;
    }

    @Override public UserInfo testUser() {
    return new UserInfo();
    }

    //新增的接口实现
    @Override public int insertUser(String username,String password) {
    return userDao.insert(username,password);
    }
}

接下来修改我们的controller,提供对外的接口,修改UserController

@Controller
@RequestMapping("user")
public class UserController {
    @Autowired
    private TestInterFace testInterFace;

    @RequestMapping("/get")
    @ResponseBody UserInfo getUser() {
    return testInterFace.testUser();
    }

    //增加新的对外访问接口
    @RequestMapping("/add")
    @ResponseBody String add() {
        testInterFace.insertUser("username123寇鑫","password123寇鑫");
        return "插入成功";
    }
}

来测试一下我们的新接口,访问 http://localhost:8080/user/add

数据库插入

看到浏览器已经正常返回了,接下来去数据库看看数据有没有实际插入

+----+-------------------+-------------------+
| id | username          | password          |
+----+-------------------+-------------------+
|  1 | username123寇鑫   | password123寇鑫   |
+----+-------------------+-------------------+

可以看到,在我们的表中,已经插入了一条数据,并且中文显示正常。但现在每次新加一个接口,都要对应的写一条sql,这样很麻烦,而且不利于开发,业务方不能专注于业务的开发,所以我们要抽象出来通用的curd接口,并且支付分页。

2017-04-04

mybatis有很多成熟的分页插件以及通用接口插件,这里我们也采用目前较为成熟的方案,不必重复造轮子。
添加pom

 <!--分页插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.2.1</version>
</dependency>
<!--通用Mapper-->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper</artifactId>
    <version>3.3.9</version>
</dependency>

因为springboot 不需要设置xml,所以这里都采用注解和代码的形式注入插件

新建配置类

package com.kx.springboot.dao.mybatis;

import com.github.pagehelper.PageHelper;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.util.Properties;

/**
 * Created by kx on 17/4/2.
 */
public class MyBatisConfig {
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean() {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
//  bean.setDataSource(dataSource());

    bean.setTypeAliasesPackage("com.kx.springboot.bean");

    //分页插件设置
    PageHelper pageHelper = new PageHelper();
    Properties properties = new Properties();
    properties.setProperty("reasonable", "true");
    properties.setProperty("supportMethodsArguments", "true");
    properties.setProperty("returnPageInfo", "check");
    properties.setProperty("params", "count=countSql");
    pageHelper.setProperties(properties);

    //添加分页插件
    bean.setPlugins(new Interceptor[]{pageHelper});

    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    try {
        //基于注解扫描Mapper,不需配置xml路径
        //bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
        return bean.getObject();
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
    }
}

新建配置扫描类

package com.kx.springboot.dao.mybatis;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;

import java.util.Properties;

/**
 * Created by kx on 17/4/2.
 */
@Configuration
//必须在MyBatisConfig注册后再加载MapperScannerConfigurer,否则会报错
@AutoConfigureAfter(MyBatisConfig.class)
public class MyBatisMapperScannerConfig {
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
    MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
    mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
    mapperScannerConfigurer.setBasePackage("com.kx.springboot.dao.mybatis");

    //初始化扫描器的相关配置,这里我们要创建一个Mapper的父类
    Properties properties = new Properties();
    properties.setProperty("mappers", "com.kx.springboot.dao.baseDao.MyMapper");
    properties.setProperty("notEmpty", "false");
    properties.setProperty("IDENTITY", "MYSQL");

    mapperScannerConfigurer.setProperties(properties);

    return mapperScannerConfigurer;
    }
}

新建对外暴露接口

package com.kx.springboot.dao.baseDao;

import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;

/**
 * Created by kx on 17/4/2.
 */
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
    //TODO
    //FIXME 特别注意,该接口不能被扫描到,否则会出错
}

现在,修改我们的dao层实现类,继承我们的通用接口

@Mapper
public interface UserDao extends MyMapper<UserInfo> {

}

在业务层中增加想要调用的方法

public interface TestInterFace {

    public int testInterFace();

    public UserInfo testUser();

    public int insertUser(UserInfo userInfo);

//新增加的方法
    List<UserInfo> selectALL();
}


@Service
public class TestInterFaceImpl implements TestInterFace {
    @Autowired UserDao userDao;
    @Override public int testInterFace() {
    return 0;
    }

    @Override public UserInfo testUser() {
    return new UserInfo();
    }

    @Override public int insertUser(UserInfo userInfo) {
    return userDao.insert(userInfo);
    }

    //新增加的实现
    @Override
    public List<UserInfo> selectALL(){
    return userDao.selectAll();
    }
}

看到我们在调用userDao时已经有了通用的单表查询方法。将新接口暴露给http,修改controller

@Controller
@RequestMapping("user")
public class UserController {
    @Autowired
    private TestInterFace testInterFace;

    @RequestMapping("/get")
    @ResponseBody UserInfo getUser() {
    return testInterFace.testUser();
    }

    @RequestMapping("/add")
    @ResponseBody String add() {
        UserInfo user = new UserInfo();
        user.setUsername("username123寇鑫");
        user.setPassword("password123寇鑫");
        testInterFace.insertUser(user);
        return "插入成功";
    }

//新增的接口方法
    @RequestMapping("/getall")
    @ResponseBody List<UserInfo> getall() {
        return testInterFace.selectALL();
    }
}

访问http://localhost:8080/user/getall

程序抛出异常

### Error querying database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist
### The error may exist in com/kx/springboot/dao/UserDao.java (best guess)
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: SELECT username,password  FROM user_info
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist
; bad SQL grammar []; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist] with root cause

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)

从异常可以看出来,我们的表和我们查询数据库的字段没有映射起来,查询资料后发现原因是通用插件的问题,默认的字段中有下划线,我们需要手动指定映射

修改userinfo类

package com.kx.springboot.bean;

import javax.persistence.Column;
import javax.persistence.Table;

/**
 * Created by kx on 17/3/29.
 */
 //增加注解声明表名
@Table(name = "userinfo")
public class UserInfo {
    //增加注解声明字段名
    @Column(name = "username")
    private String username = "username寇鑫123";
    @Column(name = "password")
    private String password = "password寇鑫123";

    public String getUsername() {
    return username;
    }

    public void setUsername(String username) {
    this.username = username;
    }

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    @Override public String toString() {
    return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}';
    }
}

再次启动访问 http://loclhost:8080/user/getall

[
    {
        "username":"user123寇鑫",
        "password":"password123寇鑫"
    },

    {
        "username":"username123寇鑫",
        "password":"password123寇鑫"
    }
]

得到如下结果,到此已经成功整合了通用mapper插件,现在加入定时任务测试
springboot 定时任务的启动和配置要简单很多,只需要增加一个注解即可

修改Application.java 启动类

@EnableAutoConfiguration
@ComponentScan(basePackages={"com.kx.springboot"})
@EnableScheduling//增加支持定时任务的注解
public class Application {
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
}

新建定时任务类

@Component
public class SchedulingTest {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Scheduled(cron = "0/5 * * * * ?") // 每5秒执行一次
    public void scheduler() {
    logger.info(">>>>>>>>>>>>> scheduled test... ");
    }
}

启动测试

2017-04-05 00:19:00.002  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 
2017-04-05 00:19:05.000  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 
2017-04-05 00:19:10.000  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 
2017-04-05 00:19:15.000  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 
2017-04-05 00:19:20.001  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 
2017-04-05 00:19:25.002  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 
2017-04-05 00:19:30.003  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 
2017-04-05 00:19:35.003  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 

可以看到我们的定时任务执行结果没有问题,接下来要在框架中增加多数据源以及事务控制。

阅读更多
换一批

没有更多推荐了,返回首页