Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 409|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-5-16 17:33:28 | 显示全部楼层 |阅读模式

    背景:

      有需求要将原来的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", // 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);
        }
    
    }
    后台自定义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

     

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

     

     

        

        

        

        

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-3 09:56 , Processed in 0.075583 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表