springboot 完整企業項目搭建實記


 

昨天搭建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

可以看到我們的定時任務執行結果沒有問題,接下來要在框架中增加多數據源以及事務控制。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM