背景:html
有需求要將原來的Spring(3.2.6) + Springmvc + Hibernate項目重構爲Springboot(1.5.2)項目java
描述:mysql
記錄重構過程,以及期間遇到的種種問題和對應的解決方案 web
環境:redis
原項目: win10 + eclipse + jdk1.8 + mysql5.7spring
新項目: win10 + IDEA + jdk1.8 + mysql5.7 + Mavensql
過程:數據庫
第一步: 新建Maven項目apache
IDEA: project > New > Module > Maven (選擇 maven-archetype-quickstart 快速建立一個maven項目, 以下圖)json
點擊Next, 本身想一個項目的 groupid(通常爲項目域名的倒寫) 和 artifactid(項目名) 並填好(以下圖)
點擊Next, 確認建立信息
點擊Next, 選擇項目建立文件夾地址
點擊確認自動建立項目
項目建立就完成了
若是發現建立maven項目十分緩慢, 極可能是因爲訪問maven官方中央倉庫網速太差致使的,建議能夠修改Maven的settings.xml文件
將默認的倉庫地址改成國內阿里雲的地址(http://maven.aliyun.com/nexus/content/groups/public/),(以下圖)
第二步: 配置pom.xml
很少說,上代碼,若是對其中某些節點含義不清楚, 能夠參考此博文: http://www.javashuo.com/article/p-ckzkvhfg-gz.html
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.yjy.test</groupId> <version>1.0-SNAPSHOT</version> <artifactId>yjyboot-${project.version}</artifactId> <name>yjyboot</name> <packaging>war</packaging> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <log4j2.level>debug</log4j2.level> <log4j2.root.path>/logs/${project.name}</log4j2.root.path> <log4j2.error.path>/logs/${project.name}-error</log4j2.error.path> <log4j2.package.path>/logs/${project.name}-kk</log4j2.package.path> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!--https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-web --> <!-- 若是沒有此 log4j-web 導出的war將不能打印日誌到文件!!! --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-web</artifactId> <version>2.7</version> </dependency> <!-- freemarker --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-- 下面兩個引入爲了操做數據庫 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Json包 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.16</version> </dependency> <!-- 爲了監控數據庫 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.25</version> </dependency> <!-- commons --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> <!-- httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5.3</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-httpclient/commons-httpclient --> <dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <!-- 兼容log4j --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-1.2-api</artifactId> <version>2.8.2</version> </dependency> <!-- Redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/nl.bitwalker/UserAgentUtils --> <dependency> <groupId>nl.bitwalker</groupId> <artifactId>UserAgentUtils</artifactId> <version>1.2.4</version> </dependency> <!-- 打war包時加入此項 告訴spring-boot tomcat相關jar包用外部的 不要打進去 IDEA運行時須要將此依賴註釋掉, 不然會沒法運行 --> <!--<dependency>--> <!--<groupId>org.springframework.boot</groupId>--> <!--<artifactId>spring-boot-starter-tomcat</artifactId>--> <!--<scope>provided</scope>--> <!--</dependency>--> </dependencies> <build> <finalName>${project.name}</finalName> <directory>target</directory> <sourceDirectory>src/main/java</sourceDirectory> <testSourceDirectory>src/test/java</testSourceDirectory> <outputDirectory>target</outputDirectory> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/*</include> </includes> </resource> <!-- 將自定義的Servlet(extends DispatcherServlet)默認xml配置文件打包至WEB-INF下 不然外部tomcat沒法處理此Servlet --> <resource> <directory>src/main/extraConfig</directory> <targetPath>${build.finalName}/WEB-INF/</targetPath> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>build-info</goal> </goals> </execution> </executions> </plugin> <!--spring-boot爲了保護application.yml和application.properties,修改了默認的佔位符${...}爲@...@--> <!--爲了spring boot的yml和properties文件可以使用maven變量替換,使用${}佔位符--> <plugin> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>utf-8</encoding> <useDefaultDelimiters>true</useDefaultDelimiters> </configuration> </plugin> </plugins> </build> </project>
第三步: 建立配置文件, 特別注意: log4j2.xml 只能放在resources目錄下, 不然導出的war包,配置的log4j2將不起做用!!!!
spring:
profiles:
active: dev # 激活的配置文件
include: freemarker,mysql,redis,interceptor # 加載其餘配置文件
mvc:
favicon:
enabled: true
debug: true # 是否啓用debug
server:
servlet-path: /common # 全部接口請求都交由自定義的Servlet處理了, 因此默認的servlet只用於處理靜態資源
spring:
freemarker:
enabled: true # 是否啓用freemarker
cache: false # 是否啓用緩存
prefix: # 模板文件前綴
suffix: .ftl # 模板文件後綴
charset: UTF-8 # 模板文件編碼
template-loader-path: classpath:templates/ # 模板文件目錄
check-template-location: true # 是否檢查模板目錄是否存在
content-type: text/html # 模板類型
request-context-attribute: req # RequestContext 引用
settings: # 更多配置
number_format: '0.##' #數字格式化, 保留兩位小數
allow-request-override: false # 是否容許 request 屬性覆蓋 controller 屬性
allow-session-override: false # 是否容許 session 屬性覆蓋 controller 屬性
expose-request-attributes: false # 設置在與模板合併以前,是否應該將全部HttpRequest屬性添加到模型中。
expose-session-attributes: false # 設置在與模板合併以前,是否應該將全部HttpSession屬性添加到模型中。
expose-spring-macro-helpers: true # 設置是否公開一個請求上下文,以供Spring的宏庫使用,名稱爲「springMacroRequestContext」
prefer-file-system-access: true # 更喜歡文件系統訪問模板加載。文件系統訪問支持對模板更改進行熱檢測。
# view-names: # whitelist of view names that can be resolved
front:
login:
excludeUrls: # 這裏配置的前臺登入驗證的白名單
- /hello.sv
- /hello2.sv
- /index.jtk
- /autho.jtk
- /code.jtk
- /checkLogin.jtk
- /checkUser.jtk
- /test.jtk
- /wxPay/notify.jtk
- /api/list.jtk
- /api/deposit.jtk
- /config/wechat.jtk
back:
login:
excludeUrls: # 這裏配置的後臺登入驗證的白名單
- /login.do
- /logout.do
- /game/scores.do
- /game/dayScore.do
spring: datasource: # 數據庫訪問配置 # 主數據源,默認的 type: com.alibaba.druid.pool.DruidDataSource dbUrl: jdbc:mysql://localhost:3306/hotpot?useUnicode=true&characterEncoding=utf-8&useSSL=false username: yjy password: yyyyyy driverClassName: com.mysql.jdbc.Driver # 下面爲鏈接池的補充設置,應用到上面全部數據源中 # 初始化大小,最小,最大 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 # 配置獲取鏈接等待超時的時間 timeBetweenEvictionRunsMillis: 60000 # 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒 minEvictableIdleTimeMillis: 300000 # 配置一個鏈接在池中最小生存的時間,單位是毫秒 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 打開PSCache,而且指定每一個鏈接上PSCache的大小 maxPoolPreparedStatementPerConnectionSize: 20 filters: stat,wall,log4j # 配置監控統計攔截的filters,去掉後監控界面sql沒法統計,'wall'用於防火牆 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 經過connectProperties屬性來打開mergeSql功能;慢SQL記錄 useGlobalDataSourceStat: true # 合併多個DruidDataSource的監控數據 #JPA Configuration: jpa: database: MYSQL show-sql: true # Show or not log for each sql query generate-ddl: true # Hibernate ddl auto (create, create-drop, update) hibernate: ddl-auto: update naming: strategy: org.hibernate.cfg.ImprovedNamingStrategy properties: hibernate: dialect: org.hibernate.dialect.MySQL5Dialect
<?xml version="1.0" encoding="UTF-8"?> <!--日誌級別以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration後面的status,這個用於設置log4j2自身內部的信息輸出,能夠不設置,當設置成trace時,你會看到log4j2內部各類詳細輸出--> <!--monitorInterval:Log4j可以自動檢測修改配置 文件和從新配置自己,設置間隔秒數--> <configuration status="INFO" monitorInterval="30"> <!--先定義全部的appender--> <appenders> <!--這個輸出控制檯的配置--> <console name="Console" target="SYSTEM_OUT"> <!--輸出日誌的格式--> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <!--控制檯只輸出level及以上級別的信息(onMatch),其餘的直接拒絕(onMismatch)--> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> </console> <!--文件會打印出全部信息,這個log每次運行程序會自動清空,由append屬性決定,這個也挺有用的,適合臨時測試用--> <File name="CurrentLog" fileName="logs/current.log" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </File> <!-- 這個會打印出全部的info及如下級別的信息,每次大小超過size, 則這size大小的日誌會自動存入按年份-月份創建的文件夾下面並進行壓縮,做爲存檔--> <RollingFile name="RollingFileInfo" fileName="F:/logs/info.log" filePattern="F:/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"> <!--控制檯只輸出level及以上級別的信息(onMatch),其餘的直接拒絕(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="50MB"/> </Policies> </RollingFile> <RollingFile name="RollingFileWarn" fileName="F:/logs/warn.log" filePattern="F:/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="30MB"/> </Policies> <!-- DefaultRolloverStrategy屬性如不設置,則默認爲最多同一文件夾下7個文件,這裏設置了20 --> <DefaultRolloverStrategy max="20"/> </RollingFile> <RollingFile name="RollingFileError" fileName="F:/logs/error.log" filePattern="F:/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="20MB"/> </Policies> </RollingFile> </appenders> <!--而後定義logger,只有定義了logger並引入的appender,appender纔會生效--> <loggers> <!--過濾掉spring和mybatis的一些無用的DEBUG信息--> <logger name="org.springframework" level="INFO"/> <logger name="org.springframework.boot.autoconfigure.logging" level="INFO"/> <logger name="org.springframework.boot.logging" level="INFO"/> <logger name="org.mybatis" level="INFO"/> <logger name="org.hibernate" level="INFO"/> <logger name="druid.sql" level="INFO"/> <root level="info"> <appender-ref ref="Console"/> <appender-ref ref="CurrentLog"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>
server:
port: 8082 # 嵌入server的運行端口
context-path: /yjyboot # 配置項目運行地址
spring:
devtools:
restart:
exclude: classpath:common/**,classpath:templates/**
server:
port: 8080
context-path: /yjyboot # 導出war包存放在tomcat後會有一個項目運行地址, 這裏配置能夠模擬項目地址, 達到IDEA運行與tomcat運行地址相同
第四步: 主類(通常放在根包中)
package com.yjy.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * SpringBoot 啓動入口 * * @Author yjy * @Date 2018-04-17 12:43 */ //@SpringBootApplication = (@Configuration, @EnableAutoConfiguration, @ComponentScan) @Configuration @EnableAutoConfiguration @ComponentScan @EntityScan("com.yjy.test.game.entity") // 掃描實體類 @ServletComponentScan(basePackages = "com.yjy.test") // 掃描自定義Servlet @PropertySource(value = { // 導入配置 "classpath:/config/application.yml", }) public class Application extends SpringBootServletInitializer { // IDEA運行時 運行此函數 public static void main(String[] args) { SpringApplication.run(Application.class, args); } // 導出war在外部tomcat使用時, 不能使用main函數運行, 須要配置此項 @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(Application.class); } }
第五步: 添加配置類(按本身的須要添加, 無特別說明的狀況下, 配置類能夠存在任意包內, 只需知足包級別不高於Application.java所在的包就能夠
固然也能夠經過配置掃描包註解來自定義, 默認掃描主類所在包如下的全部包)
1: 全局跨域配置類(放在與Application.java同目錄下)
package com.yjy.test; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 跨域配置 * * @Author yjy * @Date 2018-04-26 15:55 */ @Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.setAllowCredentials(true); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); return new CorsFilter(source); } }
2: Date參數的格式化( 請求中符合格式的字符串參數可使用Date類型接收參數, 好比請求參數 ?addTime=20180101, Controller層可使用 func(Date addTime); 接收, 不然會報400錯誤)
package com.yjy.test; import org.apache.commons.lang3.StringUtils; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; import java.text.ParseException; import java.text.SimpleDateFormat; /** * Date參數格式化 * * 嘗試格式化java.util.Date類型的參數 * * @Author yjy * @Date 2018-04-27 9:58 */ @Component public class DateConverterConfig implements Converter<String, java.util.Date> { private static final String[] formats = new String[] { "yyyy-MM-dd", // 0 "yyyy-MM", // 1 "yyyy-MM-dd HH:mm:ss", // 2 "yyyy-MM-dd HH:mm", // 3 }; /** * 這裏將參數格式化成 java.sql.Date 爲了方便後面用來拼接sql * @param param 日期格式的字符串 * @return java.sql.Date */ @Override public java.sql.Date convert(String param) { if (StringUtils.isBlank(param)) { return null; } param = param.trim(); if (param.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) { return parseDate(param, formats[0]); } if (param.matches("^\\d{4}-\\d{1,2}$")) { return parseDate(param, formats[1]); } if (param.matches("^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}$")) { return parseDate(param, formats[2]); } if (param.matches("^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}$")) { return parseDate(param, formats[3]); } throw new IllegalArgumentException("Invalid date param '" + param + "'"); } /** * 格式化日期 * @param dateStr 日期字符串 * @param format 格式 * @return 日期 */ private java.sql.Date parseDate(String dateStr, String format) { java.sql.Date date = null; try { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); java.util.Date dates = simpleDateFormat.parse(dateStr); date = new java.sql.Date(dates.getTime()); } catch (ParseException e) { e.printStackTrace(); } return date; } }
3: 自定義指定請求前綴的Servlet(一個前臺, 一個後臺)
package com.yjy.test.game.web.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 後臺servlet * 須要添加對應的 *-servlet.xml * * @Author yjy * @Date 2018-04-23 16:26 */ @WebServlet(name = "backServlet", urlPatterns = {"/manager/admin/*"}) public class CustomBackServlet extends DispatcherServlet { private static final Logger log = LoggerFactory.getLogger(CustomBackServlet.class); @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("backServlet doService..."); super.doService(request, response); } }
package com.yjy.test.game.web.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 前臺servlet * 須要添加對應的 *-servlet.xml * * @Author yjy * @Date 2018-04-23 16:26 */ @WebServlet(name = "frontServlet", urlPatterns = {"/*"}) public class CustomFrontServlet extends DispatcherServlet { private static final Logger log = LoggerFactory.getLogger(CustomFrontServlet.class); @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("frontServlet doService..."); super.doService(request, response); } }
對應的默認xml文件, 打包war的時候須要, 看pom.xml中相應配置, 不然到外部tomcat運行時, 會報找不到對應的配置文件的錯誤
xml內容都是同樣的
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 此文件用於項目導出war包在外部tomcat運行時檢測, 若是沒有此文件, 則自定義Servlet沒法訪問 --> </beans>
4: 由於上面兩個自定義的Servlet繼承自DispatcherServlet, 不容許重寫init()方法, 因此若是須要自定義初始化ServletContext, 則必須本身寫一個Servlet繼承HttpServlet,( 此Servlet不須要配置相應的xml文件)
package com.yjy.test.game.web.servlet; import com.yjy.test.game.service.OptionItemService; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; /** * 自定義初始化 ServletContext * * WebServlet中 urlPatterns必須填寫, 不然不會加載此Servlet, 同時須要配置 loadOnStartup = 1 * * @Author yjy * @Date 2018-05-02 11:47 */ @WebServlet(urlPatterns = "", loadOnStartup = 1) public class DictServlet extends HttpServlet { private OptionItemService optionItemService; public void setOptionItemService(OptionItemService optionItemService) { this.optionItemService = optionItemService; } public void init() throws ServletException { System.out.println("DictServlet init.............................."); WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext()); setOptionItemService(wac.getBean (OptionItemService.class)); optionItemService.getAllFieldName(); // init something... // 例子: 設置Servlet全局屬性 this.getServletContext().setAttribute("appName", "項目名"); super.init(); } }
5: 靜態資源請求配置
package com.yjy.test.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * 自定義WebMvcConfigurerAdapter配置 * * @Author yjy * @Date 2018-04-23 11:40 */ @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { private static final Logger log = LoggerFactory.getLogger(WebMvcConfig.class); /** * 靜態資源請求配置 * @param registry 資源處理註冊器 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("addResourceHandlers..........................."); registry.addResourceHandler("/**").addResourceLocations("classpath:/common/"); super.addResourceHandlers(registry); } }
6: tomcat上傳配置
package com.yjy.test.config; import org.springframework.boot.web.servlet.MultipartConfigFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.MultipartConfigElement; /** * 配置tomcat上傳限制 * * @Author yjy * @Date 2018-04-24 14:38 */ @Configuration public class MultipartConfig { /** * 配置tomcat上傳限制 * @return 配置 */ @Bean public MultipartConfigElement multipartConfigElement(){ MultipartConfigFactory factory = new MultipartConfigFactory(); factory.setMaxFileSize("50MB"); factory.setMaxRequestSize("10MB"); return factory.createMultipartConfig(); } }
7: 先後臺登入攔截器, 以及相應配置類
package com.yjy.test.game.web.interceptor; import com.yjy.test.game.entity.Config; import com.yjy.test.game.entity.User; import com.yjy.test.game.service.ConfigService; import com.yjy.test.game.util.FrontUtils; import com.yjy.test.game.web.ErrorCode; import com.yjy.test.util.UnicodeUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.util.UrlPathHelper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; /** * 前臺登入攔截器 * * @Author yjy * @Date 2018-04-24 15:03 */ // 這裏導入前綴爲 front.login 的配置參數 @ConfigurationProperties(prefix = "front.login") public class FrontLoginInterceptor extends HandlerInterceptorAdapter { private static final Logger log = LoggerFactory.getLogger(FrontLoginInterceptor.class); // 例外 private List<String> excludeUrls = new ArrayList<>(); private ConfigService configService; @Autowired public void setConfigService(ConfigService configService) { this.configService = configService; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("FrontLoginInterceptor > excludeUrls: {}", excludeUrls); String uri = getURI(request); if (exclude(uri)) { return true; } try { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); User user = FrontUtils.getCurrentUser(request); if (null == user) { Enumeration s = request.getHeaderNames(); String requestType = request.getHeader("X-Requested-With"); if (requestType != null && requestType.equals("XMLHttpRequest")) { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.getOutputStream().print("{\"status\":0,\"info\":\"" + UnicodeUtil.toEncodedUnicode( "登陸超時,請從新登陸", false) + "\", \"data\":null, \"code\": \"" + ErrorCode.ER_NOT_LOGIN + "\"}" ); return false; } Config config = configService.findThisConfig(); String path = null; if(null != config){ path = StringUtils.isNotBlank(request.getContextPath()) ? request.getContextPath():""; } String reLogin = "/autho.jtk"; if(StringUtils.isNotBlank(path) && path.length() > 1) { reLogin = path + reLogin; } response.sendRedirect(reLogin); return false; } } catch (Exception e) { log.error("檢查前臺登陸參數出錯", e); } return super.preHandle(request, response, handler); } private boolean exclude(String uri) { if (excludeUrls != null) { for (String exc : excludeUrls) { if (exc.equals(uri)) { return true; } } } return false; } /** * 得到第三個路徑分隔符的位置 * * @param request * @throws IllegalStateException * 訪問路徑錯誤,沒有三(四)個'/' */ private static String getURI(HttpServletRequest request) throws IllegalStateException { UrlPathHelper helper = new UrlPathHelper(); String uri = helper.getOriginatingRequestUri(request); return uri; } public List<String> getExcludeUrls() { return excludeUrls; } public void setExcludeUrls(List<String> excludeUrls) { this.excludeUrls = excludeUrls; } public ConfigService getConfigService() { return configService; } }
package com.yjy.test.game.web.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.yjy.test.game.entity.Admin; import com.yjy.test.game.entity.Config; import com.yjy.test.game.service.ConfigService; import com.yjy.test.game.util.BackUtils; import com.yjy.test.game.util.UnicodeUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.util.UrlPathHelper; import java.util.ArrayList; import java.util.List; /** * 後臺上下文登陸檢測 * * @author wdy * @version :2016年2月29日 下午6:22:56 */ // 這裏導入前綴爲 back.login 的配置參數 @ConfigurationProperties(prefix = "back.login") public class AdminLoginInterceptor extends HandlerInterceptorAdapter { private static final Logger log = LoggerFactory.getLogger(AdminLoginInterceptor.class); // 例外 private List<String> excludeUrls = new ArrayList<>(); private ConfigService configService; @Autowired public void setConfigService(ConfigService configService) { this.configService = configService; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = getURI(request); if (exclude(uri)) { return true; } try { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); Admin user = BackUtils.getCurrentUser(request); if (null == user) { String requestType = request.getHeader("X-Requested-With"); if (requestType != null && requestType.equals("XMLHttpRequest")) { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.getOutputStream().print("{\"status\":2, \"code\":\"login\", \"info\":\"" + UnicodeUtil.toEncodedUnicode("登陸超時,請從新登陸", false) + "\", \"data\":null}"); return false; } Config config = configService.findThisConfig(); String path = null; if (null != config) { path = StringUtils.isNotBlank(request.getContextPath()) ? request.getContextPath() : ""; } String reLogin = "/manager/admin/login.do"; if (StringUtils.isNotBlank(path) && path.length() > 1) { reLogin = path + reLogin; } response.sendRedirect(reLogin); return false; } } catch (Exception e) { log.error("檢查後臺登陸參數出錯", e); } return super.preHandle(request, response, handler); } private boolean exclude(String uri) { if (excludeUrls != null) { for (String exc : excludeUrls) { if (exc.equals(uri)) { return true; } } } return false; } /** * 得到第三個路徑分隔符的位置 * * @param request * @throws IllegalStateException 訪問路徑錯誤,沒有三(四)個'/' */ private static String getURI(HttpServletRequest request) throws IllegalStateException { UrlPathHelper helper = new UrlPathHelper(); String uri = helper.getOriginatingRequestUri(request); String ctxPath = helper.getOriginatingContextPath(request); int start = 0, i = 0, count = 2; if (!StringUtils.isBlank(ctxPath)) { count++; } while (i < count && start != -1) { start = uri.indexOf('/', start + 1); i++; } if (start <= 0) { throw new IllegalStateException( "admin access path not like '/manager/admin/...' pattern: " + uri); } return uri.substring(start); } public List<String> getExcludeUrls() { return excludeUrls; } public void setExcludeUrls(List<String> excludeUrls) { this.excludeUrls = excludeUrls; } public ConfigService getConfigService() { return configService; } }
package com.yjy.test.game.web.config; import com.yjy.test.game.web.interceptor.AdminLoginInterceptor; import com.yjy.test.game.web.interceptor.FrontLoginInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * 自定義WebMvcConfigurerAdapter配置 * * @Author yjy * @Date 2018-04-23 11:40 */ @Configuration public class GameWebMvcConfig extends WebMvcConfigurerAdapter { private static final Logger log = LoggerFactory.getLogger(GameWebMvcConfig.class); /** * 攔截器配置 * @param registry 攔截器註冊器 */ @Override public void addInterceptors(InterceptorRegistry registry) { log.info("addInterceptors1...................."); registry.addInterceptor(getFrontLoginInterceptor()) .addPathPatterns("*.jtk", "/*.jtk", "/*/*.jtk", "/*/*/*.jtk"); registry.addInterceptor(getAdminLoginInterceptor()) .addPathPatterns("*.do", "/*.do", "/*/*.do", "/*/*/*.do"); super.addInterceptors(registry); } @Bean AdminLoginInterceptor getAdminLoginInterceptor() { return new AdminLoginInterceptor(); } @Bean FrontLoginInterceptor getFrontLoginInterceptor() { return new FrontLoginInterceptor(); } }
8: 添加過濾器
package com.yjy.test.game.web.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 記錄請求執行時間 */ @WebFilter(urlPatterns = "/*") public class ProcessTimeFilter implements Filter { protected final Logger log = LoggerFactory.getLogger(ProcessTimeFilter.class); /** * 請求執行開始時間 */ public static final String START_TIME = "_start_time"; public void destroy() { } public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; long time = System.currentTimeMillis(); log.info("process start at {} for uri: {}", time, request.getRequestURI()); request.setAttribute(START_TIME, time); chain.doFilter(request, response); time = System.currentTimeMillis() - time; log.info("process in {} ms: {}", time, request.getRequestURI()); } public void init(FilterConfig arg0) throws ServletException { log.info("CustomFilter: ProcessTimeFilter init...."); } }
第六步: 遷移原項目源碼 幾個遇到問題的點:
1: hibernate -> hibernate + JPA
原來的 *-hbm.xml 映射方式所有須要改爲註解的方式, 實體類註解子以下:
package com.yjy.test.game.entity.club; import com.yjy.test.base.BaseEntity; import javax.persistence.*; import java.math.BigInteger; import java.util.Date; /** * 俱樂部消息表 * * @author yjy * Created on 2017年12月6日 上午9:34:07 */ @Entity @Table(name = "cg_club_message") public class ClubMessage extends BaseEntity { private static final long serialVersionUID = -1353909238958898740L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // id private Long receiveId; // 消息接收人 private Long sendId; // 消息發送人 private Long clubId; // 俱樂部id private Long clubUserId; // 相關成員id private Integer type; // 類型 private Integer status; // 已操做/已讀狀態 private Integer result; // 申請結果 private String remark; // 備註 private Integer isDelete; // 是否刪除 private Date addTime; // 建立時間 private Date updateTime; // 更新時間 @ManyToOne @JoinColumn(name = "clubId", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT )) private ClubUser clubUser; // 非持久化字段 @Transient private String nickName; // 暱稱 @Transient private String headImg; // 頭像 @Transient private String userCode; // 用戶code @Transient private String clubName; // 俱樂部名稱 public ClubMessage() { } public ClubMessage(Long sendId, Long receiveId, Long clubId, Long clubUserId, Integer type) { this.sendId = sendId; this.receiveId = receiveId; this.clubId = clubId; this.clubUserId = clubUserId; this.type = type; this.init(); } private void init() { this.isDelete = NO; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getReceiveId() { return receiveId; } public void setReceiveId(Long receiveId) { this.receiveId = receiveId; } public Long getSendId() { return sendId; } public String getNickName() { return nickName; } public Long getClubId() { return clubId; } public Integer getIsDelete() { return isDelete; } public void setIsDelete(Integer isDelete) { this.isDelete = isDelete; } public void setClubId(Long clubId) { this.clubId = clubId; } public void setNickName(String nickName) { this.nickName = nickName; } public String getHeadImg() { return headImg; } public void setHeadImg(String headImg) { this.headImg = headImg; } public String getUserCode() { return userCode; } public void setUserCode(String userCode) { this.userCode = userCode; } public String getClubName() { return clubName; } public void setClubName(String clubName) { this.clubName = clubName; } public void setSendId(Long sendId) { this.sendId = sendId; } public Long getClubUserId() { return clubUserId; } public void setClubUserId(Long clubUserId) { this.clubUserId = clubUserId; } public ClubUser getClubUser() { return clubUser; } public void setClubUser(ClubUser clubUser) { this.clubUser = clubUser; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public Integer getResult() { return result; } public void setResult(Integer result) { this.result = result; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public Date getAddTime() { return addTime; } public void setAddTime(Date addTime) { this.addTime = addTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } @Override public String toString() { return "ClubMessageDao [id=" + id + ", receiveId=" + receiveId + ", sendId=" + sendId + ", clubId=" + clubId + ", clubUserId=" + clubUserId + ", type=" + type + ", status=" + status + ", result=" + result + ", remark=" + remark + ", isDelete=" + isDelete + ", addTime=" + addTime + ", updateTime=" + updateTime + ", nickName=" + nickName + ", headImg=" + headImg + ", userCode=" + userCode + ", clubName=" + clubName + "]"; } }
package com.yjy.test.base; import java.io.Serializable; /** * 實體類父類 */ public class BaseEntity extends BaseClass implements Serializable { }
package com.yjy.test.base; import org.apache.commons.lang3.StringUtils; public abstract class BaseClass { protected static final int YES = 1; protected static final int NO = 0; /** * 驗證字符串 * @param s 字符串 * @return 是否爲空 */ protected static boolean isBlank(String s) { return StringUtils.isBlank(s); } /** * 驗證字符串 * @param s 字符串 * @return 是否不爲空 */ protected static boolean notBlank(String s) { return StringUtils.isNotBlank(s); } /** * 驗證字符串 * @param s 字符串 * @return 是否數字 */ protected static boolean isNumber(String s) { return StringUtils.isNumeric(s); } }
2: 重寫Base層(代碼以下),注意: 原來BaseDaoImpl中的 sessionFactory 沒有了, 就是說不能經過getSession().createSQLQuery(sql) 的方式獲取SQLQuery了, 須要經過em.createNativeQuery(sql).unwrap(SQLQuery.class); 來得到SQLQuery, em在BaseServiceImpl中已經注入, 經過這種方式能夠兼容以前的代碼
package com.yjy.test.base; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.NoRepositoryBean; import java.io.Serializable; /** * BaseJpaRepository * * @Author yjy * @Date 2018-04-25 15:55 */ @NoRepositoryBean public interface BaseJpaRepository<T extends BaseEntity, L extends Serializable> extends JpaRepository<T, L> { public T findTopByOrderByIdDesc(); }
package com.yjy.test.base; import com.yjy.test.util.hibernate.Pagination; import org.springframework.data.domain.Sort; import java.io.Serializable; import java.util.List; public interface BaseService<T extends BaseEntity, L extends Serializable> { /** * 保存對象 * * @param entity 實體對象 * @return 操做信息 */ T save(T entity); T update(T entity); void delete(T entity); /** * 根據ID刪除記錄 * * @param id 記錄ID */ void deleteById(L id); /** * 根據ID數組刪除記錄,當發生異常時,操做終止並回滾 * * @param ids 記錄ID數組 * @return 刪除的對象 */ void deleteById(L[] ids); /** * 保存並刷新對象,避免many-to-one屬性不完整 * * @param entity */ T saveAndRefresh(T entity); /** * 經過ID查找對象 * * @param id 記錄的ID * @return 實體對象 */ T findById(L id); T load(L id); T findByProperty(String property, Object value); List<T> findListByProperty(String property, Object value); /** * 根據屬性查找 * @param propertyName 屬性 * @param value 值 * @param anywhere 是否模糊匹配 * @return */ List<T> findListByProperty(String propertyName, Object value, boolean anywhere); /** * 查找全部對象 * * @return 對象列表 */ List<T> findAll(); /** * 分頁查詢 * @param pageNo 頁號 * @param pageSize 條數 * @param orders 排序規則 * @return 分頁列表 */ Pagination findAllPage(int pageNo, int pageSize, Sort.Order... orders); List<T> findList(T entity, Sort.Order... orders); List<T> findList(T entity, int pageNo, int pageSize, Sort.Order... orders); Pagination findListPage(T entity, int pageNo, int pageSize, Sort.Order... orders); T findLast(); T findFirst(T entity, Sort.Order... orders); long findAllCount(); long findCount(T entity); }
package com.yjy.test.base; import com.yjy.test.util.hibernate.Finder; import com.yjy.test.util.hibernate.Pagination; import org.hibernate.Query; import org.springframework.data.domain.*; import org.springframework.data.jpa.repository.JpaRepository; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.List; // 這裏不能加@Service註解, 不然會沒法獲取泛型T的Class public class BaseServiceImpl<T extends BaseEntity, L extends Serializable> extends BaseClass implements BaseService<T, L> { //@Autowired和@PersistenceContext註解任取一 @PersistenceContext protected EntityManager em; @Override public T save(T entity) { return dao.save(entity); } @Override public T update(T entity) { return dao.saveAndFlush(entity); } @Override public void delete(T entity) { dao.delete(entity); } @Override public void deleteById(L id) { dao.delete(id); } @Override public void deleteById(L[] ids) { if (ids != null) { for (L id : ids) { dao.delete(id); } } } @Override public T saveAndRefresh(T entity) { return dao.saveAndFlush(entity); } @Override public T findById(L id) { return dao.findOne(id); } @Override public T load(L id) { return dao.getOne(id); } @Override public T findByProperty(String property, Object value) { List<T> list = findListByProperty(property, value); return list != null ? list.get(0) : null; } @Override public List<T> findListByProperty(String property, Object value) { return findListByProperty(property, value, false); } @Override public List<T> findListByProperty(String property, Object value, boolean anywhere) { CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); CriteriaQuery<T> query = criteriaBuilder.createQuery(getPersistentClass()); Root<T> root = query.from(getPersistentClass()); Predicate predicate; if (anywhere) predicate = criteriaBuilder.like(root.get(property), "%" + value.toString() + "%"); else predicate = criteriaBuilder.equal(root.get(property), value); query.where(predicate); return em.createQuery(query).getResultList(); } @Override public List<T> findAll() { return dao.findAll(); } @Override public Pagination findAllPage(int pageNo, int pageSize, Sort.Order... orders) { Sort sort = orders != null && orders.length > 0 ? new Sort(orders) : null; Pageable pageable = new PageRequest(pageNo - 1, pageSize, sort); Page<T> page = dao.findAll(pageable); Pagination pagination = new Pagination(pageNo, pageSize, (int) page.getTotalElements()); pagination.setList(page.getContent()); return pagination; } @Override public List<T> findList(T entity, Sort.Order... orders) { Example<T> example = Example.of(entity); if (orders != null && orders.length > 0) return dao.findAll(example, new Sort(orders)); else return dao.findAll(example); } @Override @SuppressWarnings("unchecked") public List<T> findList(T entity, int pageNo, int pageSize, Sort.Order... orders) { Pagination pagination = findListPage(entity, pageNo, pageSize, orders); if (pagination != null) { return (List<T>)pagination.getList(); } return new ArrayList<>(); } @Override public Pagination findListPage(T entity, int pageNo, int pageSize, Sort.Order... orders) { Example<T> example = Example.of(entity); Sort sort = orders != null && orders.length > 0 ? new Sort(orders) : null; Pageable pageable = new PageRequest(pageNo - 1, pageSize, sort); Page<T> page = dao.findAll(example, pageable); Pagination pagination = new Pagination(pageNo, pageSize, (int) page.getTotalElements()); pagination.setList(page.getContent()); return pagination; } @Override public T findLast() { return dao.findTopByOrderByIdDesc(); } @Override public T findFirst(T entity, Sort.Order... orders) { List<T> list = findList(entity, 1, 1, orders); if (!list.isEmpty()) { return list.get(0); } return null; } @Override public long findAllCount() { return dao.count(); } @Override public long findCount(T entity) { Example<T> example = Example.of(entity); return dao.count(example); } @SuppressWarnings("rawtypes") protected Pagination find(Finder finder, int pageNo, int pageSize) { int totalCount = countQueryResult(finder); Pagination p = new Pagination(pageNo, pageSize, totalCount); if (totalCount < 1) { p.setList(new ArrayList()); return p; } Query query = em.createQuery(finder.getOrigHql()).unwrap(Query.class); finder.setParamsToQuery(query); query.setFirstResult(p.getFirstResult()); query.setMaxResults(p.getPageSize()); List list = query.list(); p.setList(list); return p; } /** * 經過count查詢得到本次查詢所能得到的對象總數. * * @param finder * @return */ protected int countQueryResult(Finder finder) { Query query = em.createQuery(finder.getRowCountHql()).unwrap(Query.class); finder.setParamsToQuery(query); return ((Number) query.iterate().next()).intValue(); } /*************************************************************************/ private Class<T> persistentClass; @SuppressWarnings("unchecked") public BaseServiceImpl() { ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); this.persistentClass = (Class<T>) parameterizedType.getActualTypeArguments()[0]; } private BaseJpaRepository<T, L> dao; public void setDao(BaseJpaRepository<T, L> dao) { this.dao = dao; } protected BaseJpaRepository<T, L> getDao() { return this.dao; } private Class<T> getPersistentClass() { return persistentClass; } public void setPersistentClass(Class<T> persistentClass) { this.persistentClass = persistentClass; } }
package com.yjy.test.util.hibernate; /** * 分頁接口 */ public interface Paginable { /** * 總記錄數 * * @return */ int getTotalCount(); /** * 總頁數 * * @return */ int getTotalPage(); /** * 每頁記錄數 * * @return */ int getPageSize(); /** * 當前頁號 * * @return */ int getPageNo(); /** * 是否第一頁 * * @return */ boolean isFirstPage(); /** * 是否最後一頁 * * @return */ boolean isLastPage(); /** * 返回下頁的頁號 */ int getNextPage(); /** * 返回上頁的頁號 */ int getPrePage(); }
package com.yjy.test.util.hibernate; import java.util.List; /** * 列表分頁。包含list屬性。 */ @SuppressWarnings("serial") public class Pagination extends SimplePage implements java.io.Serializable, Paginable { public Pagination() { } /** * 構造器 * * @param pageNo * 頁碼 * @param pageSize * 每頁幾條數據 * @param totalCount * 總共幾條數據 */ public Pagination(int pageNo, int pageSize, int totalCount) { super(pageNo, pageSize, totalCount); } /** * 構造器 * * @param pageNo * 頁碼 * @param pageSize * 每頁幾條數據 * @param totalCount * 總共幾條數據 * @param list * 分頁內容 */ public Pagination(int pageNo, int pageSize, int totalCount, List<?> list) { super(pageNo, pageSize, totalCount); this.list = list; } /** * 第一條數據位置 * * @return */ public int getFirstResult() { return (pageNo - 1) * pageSize; } /** * 當前頁的數據 */ private List<?> list; /** * 得到分頁內容 * * @return */ public List<?> getList() { return list; } /** * 設置分頁內容 * * @param list */ @SuppressWarnings("rawtypes") public void setList(List list) { this.list = list; } }
package com.yjy.test.util.hibernate; /** * 簡單分頁類 */ public class SimplePage implements Paginable { public static final int DEF_COUNT = 20; /** * 檢查頁碼 checkPageNo * * @param pageNo * @return if pageNo==null or pageNo<1 then return 1 else return pageNo */ public static int cpn(Integer pageNo) { return (pageNo == null || pageNo < 1) ? 1 : pageNo; } /** * 檢查每頁條數 * @author yjy * Created on 2017年12月5日 下午2:39:23 * @param pageSize 條數 * @return 矯正值 */ public static int cps(Integer pageSize) { return (pageSize == null || pageSize < 1) ? DEF_COUNT : pageSize; } /** * 根據頁號和條數計算起始下標 * @author yjy * Created on 2017年12月6日 上午9:36:17 * @param pageNo 頁號 * @param pageSize 條數 * @return 起始下標 return (pageNo - 1) * pageSize */ public static int getStart(Integer pageNo, Integer pageSize) { return (cpn(pageNo) - 1) * cps(pageSize); } public SimplePage() { } /** * 構造器 * * @param pageNo * 頁碼 * @param pageSize * 每頁幾條數據 * @param totalCount * 總共幾條數據 */ public SimplePage(int pageNo, int pageSize, int totalCount) { setTotalCount(totalCount); setPageSize(pageSize); setPageNo(pageNo); adjustPageNo(); } /** * 調整頁碼,使不超過最大頁數 */ public void adjustPageNo() { if (pageNo == 1) { return; } int tp = getTotalPage(); if (pageNo > tp) { pageNo = tp; } } /** * 得到頁碼 */ public int getPageNo() { return pageNo; } /** * 每頁幾條數據 */ public int getPageSize() { return pageSize; } /** * 總共幾條數據 */ public int getTotalCount() { return totalCount; } /** * 總共幾頁 */ public int getTotalPage() { int totalPage = totalCount / pageSize; if (totalPage == 0 || totalCount % pageSize != 0) { totalPage++; } return totalPage; } /** * 是否第一頁 */ public boolean isFirstPage() { return pageNo <= 1; } /** * 是否最後一頁 */ public boolean isLastPage() { return pageNo >= getTotalPage(); } /** * 下一頁頁碼 */ public int getNextPage() { if (isLastPage()) { return pageNo; } else { return pageNo + 1; } } /** * 上一頁頁碼 */ public int getPrePage() { if (isFirstPage()) { return pageNo; } else { return pageNo - 1; } } protected int totalCount = 0; protected int pageSize = 20; protected int pageNo = 1; /** * if totalCount<0 then totalCount=0 * * @param totalCount */ public void setTotalCount(int totalCount) { if (totalCount < 0) { this.totalCount = 0; } else { this.totalCount = totalCount; } } /** * if pageSize< 1 then pageSize=DEF_COUNT * * @param pageSize */ public void setPageSize(int pageSize) { if (pageSize < 1) { this.pageSize = DEF_COUNT; } else { this.pageSize = pageSize; } } /** * if pageNo < 1 then pageNo=1 * * @param pageNo */ public void setPageNo(int pageNo) { if (pageNo < 1) { this.pageNo = 1; } else { this.pageNo = pageNo; } } }
3: 使用 mvn package 打包項目時, 須要配置主類, 不然若是項目中存在多個類有main函數時, 打包會報錯, 配置以下:
4: 有一個比較實用的點, 就是能夠經過@Value(property)註解將配置綁定至靜態變量中, 下面是Redis靜態配置類的例子:
package com.yjy.test.game.redis; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotNull; @Component @Validated public class RedisConfig { @NotNull public static String addr; //Redis服務器IP @NotNull public static int port; //Redis的端口號 public static String auth; //訪問密碼 @NotNull public static int maxActive = 10; // 可用鏈接實例的最大數目,默認值爲8;若是賦值爲-1,則表示不限制; // 若是pool已經分配了maxActive個jedis實例,則此時pool的狀態爲exhausted(耗盡)。 @NotNull public static int maxIdle = 200; //控制一個pool最多有多少個狀態爲idle(空閒的)的jedis實例,默認值也是8。 @NotNull public static int timeOut = 2000; //鏈接的超時時間 @NotNull public static int maxWait = 10000; //等待可用鏈接的最大時間,單位毫秒,默認值爲-1,表示永不超時。若是超過等待時間,則直接拋出JedisConnectionException; @NotNull public static boolean testOnBorrow = true; //在borrow一個jedis實例時,是否提早進行validate操做;若是爲true,則獲得的jedis實例均是可用的; @NotNull public static boolean testOnReturn = true; //在return一個jedis實例時,是否提早進行validate操做. @Value("${redis.addr}") public void setAddr(String addr) { this.addr = addr; } @Value("${redis.port}") public void setPort(int port) { this.port = port; } @Value("${redis.auth}") public void setAuth(String auth) { this.auth = auth; } @Value("${redis.maxActive}") public void setMaxActive(int maxActive) { this.maxActive = maxActive; } @Value("${redis.maxIdle}") public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } @Value("${redis.timeOut}") public void setTimeOut(int timeOut) { this.timeOut = timeOut; } @Value("${redis.maxWait}") public void setMaxWait(int maxWait) { this.maxWait = maxWait; } @Value("${redis.testOnBorrow}") public void setTestOnBorrow(boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } @Value("${redis.testOnReturn}") public void setTestOnReturn(boolean testOnReturn) { this.testOnReturn = testOnReturn; } public static void printAllConfig() { System.out.println("RedisConfig{" + "addr='" + addr + '\'' + ", port=" + port + ", auth='" + auth + '\'' + ", maxActive=" + maxActive + ", maxIdle=" + maxIdle + ", timeOut=" + timeOut + ", maxWait=" + maxWait + ", testOnBorrow=" + testOnBorrow + ", testOnReturn=" + testOnReturn + '}'); } }
大概就是這麼個樣子!!! 嗯