昨天搭建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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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

  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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

修改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);
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

启动后抛异常

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);
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

再次启动,成功,访问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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

修改原来 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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

@EnableAutoConfiguration
@ComponentScan(basePackages={"com.kx.springboot"})
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

  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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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

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

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

添加新的pom依赖

 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

在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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

启动应用,尝试输出日志

访问 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
  • 1
  • 2
  • 3
  • 4
  • 5

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

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

-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
  • 1
  • 2
  • 3

访问 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
  • 1
  • 2
  • 3
  • 4
  • 5

我们发现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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

看一下我们现在的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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

再次运行项目,访问 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在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
  • 1
  • 2
  • 3
  • 4
  • 5

表结构

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

编写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); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在业务层增加对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);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

接下来修改我们的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 "插入成功";
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

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

+----+-------------------+-------------------+
| id | username | password |
+----+-------------------+-------------------+
| 1 | username123寇鑫 | password123寇鑫 |
+----+-------------------+-------------------+
  • 1
  • 2
  • 3
  • 4
  • 5

可以看到,在我们的表中,已经插入了一条数据,并且中文显示正常。但现在每次新加一个接口,都要对应的写一条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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

因为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);
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

新建配置扫描类

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;
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

新建对外暴露接口

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 特别注意,该接口不能被扫描到,否则会出错
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

@Mapper
public interface UserDao extends MyMapper<UserInfo> { }
  • 1
  • 2
  • 3
  • 4
  • 5

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

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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

看到我们在调用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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

访问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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

修改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 + '\'' + '}';
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

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

[
{
"username":"user123寇鑫",
"password":"password123寇鑫"
}, {
"username":"username123寇鑫",
"password":"password123寇鑫"
}
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

得到如下结果,到此已经成功整合了通用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);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

新建定时任务类

@Component
public class SchedulingTest {
private final Logger logger = LoggerFactory.getLogger(getClass()); @Scheduled(cron = "0/5 * * * * ?") // 每5秒执行一次
public void scheduler() {
logger.info(">>>>>>>>>>>>> scheduled test... ");
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

启动测试

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...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

 

springboot 完整企业项目搭建实记的更多相关文章

  1. springboot(一).初识springboot以及基本项目搭建

    初识springboot 以及基本项目搭建 由于新的项目需要搭建后台框架,之前的springmvc架构也使用多次,在我印象中springboot的微服务架构更轻量级更容易搭建,所以想去试试spring ...

  2. SpringBoot入坑-项目搭建

    对于学过三大框架的小童鞋,从今天开始给大家带来一套新的框架学习,相信对于做程序的小童鞋一定有所耳闻,作为下一代java开发框架springboot,减去了繁琐的xml配置,相信用过spring.sta ...

  3. IDEA SpringBoot多模块项目搭建详细过程(转)

    文章转自https://blog.csdn.net/zcf980/article/details/83040029 项目源码: 链接: https://pan.baidu.com/s/1Gp9cY1Q ...

  4. SpringBoot + SpringCloud学习踩坑实记

     踩的坑: 1).springcloud框架中,依赖一直报错,很可能是没有添加springcloud的依赖,或者是依赖的版本号过低.并且springboot也有一个父依赖. 2.springcloud ...

  5. springboot+mybatis+thymeleaf项目搭建及前后端交互

    前言 spring boot简化了spring的开发, 开发人员在开发过程中省去了大量的配置, 方便开发人员后期维护. 使用spring boot可以快速的开发出restful风格微服务架构. 本文将 ...

  6. vue企业项目搭建过程(vue-cli脚手架超详细教程 傻瓜-入门)

    vue作为现在主流的前端框架,有必要学习一下. vue的官方文档还是不错的,开源中文,一个爽字形容. 如果不是实际开发需要vue-cli构建项目,那么可以在加一个爽. 然而要构建的时候发现官方文档还是 ...

  7. springboot+jps+druid项目搭建

    pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...

  8. 完整maven项目搭建

    1. 在eclipse中New选择maven项目,并选择web类型这个百度一下到处有: 2. 写简单的controller示例: 3. 配置web.xml,主要是DispatcherServlet: ...

  9. springboot多模块项目搭建遇到的问题记录

    废话不多说,直接上问题报错与解决方法. 问题报错一:(报错信息看下方代码) 问题原因:'com.company.logistics.service.company.CompanyService' 未找 ...

随机推荐

  1. Discuz常见小问题-如何发布站点公告

    运营-站点公告,可以管理或添加新的公告   如果要添加要把这个终止时间设置的远一点,不然一个月之后就自动消失了   完成之后可以在首页,帖子的常规地方看到这些公告    

  2. PHP如何安装和配置Zend Studio

    1 网上下载该软件,安装之后关闭三个东西 2 新建一个本地的PHP项目 3 展开左侧的资源树,发现多了一个PHP的相关东西 4 右击"基础知识",新建一个PHP文件并输入以下代码 ...

  3. Activity与Service通信

    Activity与Service通信的方式有三种: 继承Binder类 这个方式只有当你的Acitivity和Service处于同一个Application和进程时,才可以用,比如你后台有一个播放背景 ...

  4. Binwalk:后门(固件)分析利器

    http://blog.csdn.net/testing_is_believing/article/details/14091179 Binwalk介绍 Binwalk是一个固件的分析工具,旨在协助研 ...

  5. iOS7 下使用SVPullToRefresh 下拉刷新导航栏位置错误

    iOS7 下使用SVPullToRefresh 下拉刷新导航栏位置错误: 下拉刷新之后,tableview的第一列会跑到导航栏的下面: 修正:添加如下代码 /** * 下拉刷新 增加一个: */ // ...

  6. (step6.1.1)hdu 1879(继续畅通工程——最小生成树、kruscal)

    题目大意:输入一个整数n,表示有n个村庄.在接下来的n(n-1)/2行中,每行有4个整数begin  end  weight  flag.分别表示从begin到end之间可以连通 ,他们之间的费用为w ...

  7. QtGui.QSplitter

    A QtGui.QSplitter lets the user control the size of child widgets by dragging the boundary between t ...

  8. ant design pro (十三)advanced 错误处理

    一.概述 原文地址:https://pro.ant.design/docs/error-cn 二.详细 2.1.页面级报错 2.1.1.应用场景 路由直接引导到报错页面,比如你输入的网址没有匹配到任何 ...

  9. 〖Android〗/system/etc/fallback_fonts.xml

    <?xml version="1.0" encoding="utf-8"?> <!-- Fallback Fonts This file sp ...

  10. sql 百分比

    select [city], bfb=cast(cast(count(*)*100./(select count(*) from [UserBasicInfo]) as decimal(10,0)) ...