背景:

  有需求要将原来的Spring(3.2.6) + Springmvc + Hibernate项目重构为Springboot(1.5.2)项目

描述:

  记录重构过程,以及期间遇到的种种问题和对应的解决方案  

环境:

  原项目: win10 + eclipse + jdk1.8 + mysql5.7

  新项目: win10 + IDEA + jdk1.8 + mysql5.7 + Maven

过程:

  第一步:  新建Maven项目

    IDEA: project > New > Module > Maven (选择 maven-archetype-quickstart 快速创建一个maven项目, 如下图)

    

    点击Next, 自己想一个项目的 groupid(一般为项目域名的倒写) 和 artifactid(项目名) 并填好(如下图)

    

    点击Next, 确认创建信息

    点击Next, 选择项目创建文件夹地址

    点击确认自动创建项目

    项目创建就完成了

    如果发现创建maven项目十分缓慢, 很可能是由于访问maven官方中央仓库网速太差导致的,建议可以修改Maven的settings.xml文件

    将默认的仓库地址改为国内阿里云的地址(http://maven.aliyun.com/nexus/content/groups/public/),(如下图)

    

   第二步: 配置pom.xml

     不多说,上代码,如果对其中某些节点含义不清楚, 可以参考此博文: https://www.cnblogs.com/hafiz/p/5360195.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>

pom.xml

  

   第三步: 创建配置文件, 特别注意:  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只用于处理静态资源

application.yml

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

application-freemarker.yml

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

application-interceptor.yml

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

application-mysql.yml

<?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>

log4j2.xml

server:
port: 8082 # 嵌入server的运行端口
context-path: /yjyboot # 配置项目运行地址
spring:
devtools:
restart:
exclude: classpath:common/**,classpath:templates/**

application-dev.yml

server:
port: 8080
context-path: /yjyboot # 导出war包存放在tomcat后会有一个项目运行地址, 这里配置可以模拟项目地址, 达到IDEA运行与tomcat运行地址相同

application-pro.yml

  第四步: 主类(一般放在根包中)

    

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", //
"yyyy-MM", //
"yyyy-MM-dd HH:mm:ss", //
"yyyy-MM-dd HH:mm", //
}; /**
* 这里将参数格式化成 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);
} }

后台自定义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 = "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);
} }

前台自定义Servlet

      对应的默认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>

自定义Servlet默认配置

      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();
} }

初始化ServletContext

      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 { }

BaseEntity

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);
} }

BaseClass

    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(); }

BaseJpaRepository.java

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); }

BaseService

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;
}
}

BaseServiceImpl

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();
}

Paginable

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;
}
}

Pagination

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;
}
}
}

SimplePage

    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 +
'}');
} }

RedisConfig.java

大概就是这么个样子!!! 嗯

    

    

    

    

SpringBoot webmvc项目导出war包并在外部tomcat运行产生的诸多问题以及解决方案的更多相关文章

  1. springboot-把web项目打成war包部署到外部tomcat

    将打包方式修改为war <packaging>war</packaging> 移除tomcat依赖或者将tomcat依赖scope改为provide 移除tomcat依赖 &l ...

  2. ideal中把项目打成war包,并放在tomcat运行,遇见的问题。。。

    先说下我遇见的问题吧:最近做项目要把项目放在tomcat上运行,用的springboot框架, 在建项目时选择的是  jar包,项目写完要部署打包是,在pom中虽然把包改成了war ,可是每次放入to ...

  3. SpringBoot打包项目成war包,并部署到服务器的tomcat上

    最近遇到项目需要上线部署到服务器,需要讲自己的SpringBoot项目打包成war包,部署到tomcat上. 下面记录下打包部署的过程. (1) 将SpringBoot项目打包成war包, 一.修改打 ...

  4. springboot项目打war包发布到外置tomcat

    第一步:修改pom.xml 1. <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> ...

  5. springboot将项目打成war包

    1. 将项目的打包方式改为war包 <groupId>com.cc</groupId> <artifactId>aaaaaa</artifactId> ...

  6. Springboot项目打成war包,部署到tomcat上,正常启动访问报错404

    前言: 项目介绍,此项目是一个Maven多模块项目,模块项目:all(父模块):util (公用的工具类):dao(实体类.业务类.mapper.mapper.xml):business(业务serv ...

  7. IDEA将项目导出war包方法(详细)

    右上角点击进入配置页面(如图)选择Artifcts 点击绿色的那个+号,选择Web Application:Archive; 设置名称,选择输出路径 下面开始打war包在Build下面选择Build ...

  8. IDEA中web项目打成war包并在本地tomcat部署(超细版)

    准备工作:相关软件及插件IDEA(2021.1.3).tomcat(8.5.50)且在IDEA中调用tomcat运行时没有任何错误的,如何下载安装百度都有详细的介绍,这里就不过多的介绍了,版本不同操作 ...

  9. Spring boot 项目打成war包并在idea中运行

    1. 修改pom文件原来是jar改成<packaging>war</packaging> 2. 在pom文件中添加移除内置tomcat并且添加添加servlet-api的依赖. ...

随机推荐

  1. JS/JQuery 设置input等标签设置和取消只读属性

    <input type="text" id="HouseName" value="" align="left"/& ...

  2. 6-4 The present perfect

    1 Summary The present perfect is an important verb tense in English. It is used to talk about things ...

  3. 【win7】安装开发环境

    1. 通用版主分支合并到v3,并删除data下无用文件或添加data有用文件 2. xampp php7与php5切换 是否可以行? 换phpstudy 默认支持php 32位,而我们要下载支持64的 ...

  4. Azure - Create your first function using Visual Studio

    Azure Functions lets you execute your code in a serverless environment without having to first creat ...

  5. BugKu 这是一张单纯的图片

    http://123.206.87.240:8002/misc/1.jpg FLAG在哪里?? 吐槽一下这图片一点也不单纯 用010 打开后发现最后附着一段意义不明的字符,file命令也识别不出来 题 ...

  6. Magento 2 Plugin - Interceptor - Magento 2插件 - 拦截器-插件开发

    Magento 2 Plugin - Interceptor - Magento 2插件 - 拦截器 Magento 2 Plugin is a technical plugin for your b ...

  7. .Net Core实践1

    实践目标 编写经典的hello world程序.使用.netcore框架,然后运行在linux上. .netcore目前已经是2.1版本了,可以简单的认为是一种跨平台的.net framework,除 ...

  8. django restframework permission

    与 authentication 和 throttling 一起,permission 决定是应该接受还是拒绝访问请求.权限检查总是在视图的最开始处运行,在任何其他代码被允许进行之前.权限检查通常会使 ...

  9. PHP 加解密方法大全

    最近看见一篇文章讲的是PHP的加解密方法,正好也自己学习下,顺便以后有用到的地方也好能快速用上,仅供自己学习和复习,好了不多BB,上代码. 基于这几个函数可逆转的加密为:base64_encode() ...

  10. busybox编译

    sync.c:(.text.sync_main+0x7c): undefined reference to `syncfs'Coreutils—>sync选项去掉 nsenter.c:(.tex ...