2022-12-08 SSM项目转springboot整合jsp

目录

1.添加springboot相关pom依赖

2.Springboot整合jsp

2.1.使用打jar包方式执行

2.2.打war包执行

3.多数据源xml文件配置提取

3.1.数据源bean提取

3.2.创建数据源bean

3.3.创建相关配置bean

4.Spring全局事务配置类提取

5.SpringAop的xml开发转为注解开发

6.SpringMVC相关xml配置提取

6.1 视图文件路径配置(视图解析器)

6.2 拦截器注册

6.3 文件上传限制

6.4 全局异常处理

7.web.xml配置提取

7.1 全局应用上下文配置

7.2 全局上下文参数配置

7.3 session超时时间

7.4 字符过滤

7.5 filter注册

7.6 listener注册

7.7 前端控制器配置

7.8 开启后缀匹配

8.springboot集成log4j2日志

8.1 pom依赖

8.2 log4j2.xml文件

9.定时任务

9.1 Quartz 定时任务

9.2 Springboot整合Quartz

9.3 对于quartz定时任务job中不能注入bean对象处理

10.全局属性配置文件提取

10.1 自定义全局yml配置

10.2 注入配置类

11.自定义yml配置文件读取加载

11.1.创建yaml工厂类

11.2.在配置类上指定使用的配置文件

12.集成swagger2

13.配置文件切换

13.1 系统的yml配置文件切换

13.2 properties配置文件自动切换实现


1.添加springboot相关pom依赖

注意:springboot6.x+版本更改了匹配策略ant_path_matcherpath_pattern_parser,并且关闭了依赖循环

如需依赖循环需要

spring:
  main:
    allow-circular-references: true #依赖循环
  mvc:
    pathmatch:
      use-suffix-pattern: true #后缀匹配
      matching-strategy: ant_path_matcher #匹配策略

2.Springboot整合jsp

2.1.使用打jar包方式执行

1.添加tomcat编译jsp的依赖

      
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
        

2.添加springboot的maven插件

注意:springboot整合jsp时,插件版本必须是 1.4.2.RELEASE 时才能直接使用jar包方式运行


   
      org.springframework.boot
      spring-boot-maven-plugin
      
      1.4.2.RELEASE
   

3.指定jsp编译文件输出位置


            
                src/main/java
                
                    **/*.*
                
            
            
                src/main/resources
                
                    **/*.*
                
            
​
            
            
                
                src/main/webapp
                
                META-INF/resources
                
                    **/**
                
                false
            
​
        

4.运行

直接启动或使用 java -jar xxx.jar命令

2.2.打war包执行

1.指定打包后的文件名称


   
   xxx

2.指定jsp编译目录



   
      src/main/webapp
      META-INF/resources
      
         **/*.*
      
   
​
   
   
      src/main/java
      
         **/*.xml
      
   
​
   
   
      src/main/resources
      
         **/*.*
      
   

3.执行打包是war


war

4.主启动类继承SpringBootServletInitializer

/**
 * SpringBootServletInitializer: 继承这个类, 才能使用独立tomcat服务器
 */
@SpringBootApplication
public class JspApplication  extends SpringBootServletInitializer  {
​
   public static void main(String[] args) {
      SpringApplication.run(JspApplication.class, args);
   }
​
   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
      return builder.sources(JspApplication.class);
   }
}

5.部署war

把war放到tomcat等服务器的发布目录中。 tomcat为例, myboot.war放到tomcat/webapps目录。

3.多数据源xml文件配置提取

3.1.数据源bean提取

使用druid数据源,将数据源bean提取到yml配置文件

spring:
  datasource:
    #使用Druid数据源连接池
    type: com.alibaba.druid.pool.DruidDataSource
    db1:
      jdbc-url: jdbc:oracle:thin:@xx.xxx.xx.xx:1521:ORCL
      username: xxx
      password: xxx
      driverClassName: oracle.jdbc.driver.OracleDriver
      # 下面为连接池的补充设置
      druid:
        initialSize: 20   #初始化连接个数
        minIdle: 10       #最小空闲连接个数
        maxActive: 100    #最大连接个数
        maxWait: 60000    #获取连接时最大等待时间,单位毫秒。
        timeBetweenEvictionRunsMillis: 10000  #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        minEvictableIdleTimeMillis: 10000     #配置一个连接在池中最小生存的时间,单位是毫秒
        validationQuery: SELECT x #用来检测连接是否有效的sql,要求是一个查询语句。
    db2:
      jdbc-url: jdbc:oracle:thin:@xx.xxx.xx.xx:1521:ORCL
      username: xxx
      password: xxx
      driverClassName: oracle.jdbc.driver.OracleDriver
      # 下面为连接池的补充设置
      druid:
        initialSize: 20   #初始化连接个数
        minIdle: 10       #最小空闲连接个数
        maxActive: 100    #最大连接个数
        maxWait: 60000    #获取连接时最大等待时间,单位毫秒。
        timeBetweenEvictionRunsMillis: 10000  #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        minEvictableIdleTimeMillis: 10000     #配置一个连接在池中最小生存的时间,单位是毫秒
        validationQuery: SELECT x #用来检测连接是否有效的sql,要求是一个查询语句。
#自定义配置文件
apply:
  mybatis:
    mapperLocations: classpath:mapping/**/*.xml
    configLocation:  classpath:config/sql-map-config.xml

3.2.创建数据源bean

编写配置类,使用数据源builder自动创建对应数据源

3.3.创建相关配置bean

由于使用多数据源,所以使用自定义的sqlsessionFactory和sqlsession配置,排除自动配置

注意事项:多数据源时需要使用@Primary指定主数据源,否则spring会不知道先创建那个数据源的bean,会造成依赖循环错误

完整配置类代码

/**
 * 数据源配置
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "apply.mybatis")
@MapperScan(basePackages = "com.ws.*.mapper",sqlSessionFactoryRef = "sqlSessionFactory",sqlSessionTemplateRef = "sqlSessionTemplate")
public class DataSourceConfig {


    private String mapperLocations; //mapper路径地址

    private String configLocation; //mybatis配置文件地址


    /**
     * 数据源构建
     * @return
     */
    @Primary
    @Bean(name = "dataSource_1")
    @ConfigurationProperties("spring.datasource.db1")
    public DataSource db1DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSource_2")
    @ConfigurationProperties("spring.datasource.db2")
    public DataSource db2DataSource() {
        return DataSourceBuilder.create().build();
    }


    /**
     * 动态数据源
     * @param dataSource1
     * @param dataSource2
     * @return
     */
    @Bean(name = "dataSource")
    public DynamicDataSource dataSource(@Qualifier("dataSource_1")DataSource dataSource1,
                                        @Qualifier("dataSource_2")DataSource dataSource2){
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map map = new HashMap<>();
        map.put("dataSource_1",dataSource1);
        map.put("dataSource_2",dataSource2);
        dynamicDataSource.setTargetDataSources(map);
        dynamicDataSource.setDefaultTargetDataSource(dataSource1);
        return dynamicDataSource;
    }


    /**
     * sqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DynamicDataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(configLocation));
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        return bean.getObject();
    }


    /**
     * sqlSession
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "sqlSessionTemplate")
    @Scope("prototype")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        SqlSessionTemplate bean = new SqlSessionTemplate(sqlSessionFactory);
        return bean;
    }

    /**
     *  数据源事务管理
     */
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dataSource") DynamicDataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

排除数据源springboot的自动配置

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

4.Spring全局事务配置类提取

全局自定义配置抽取

apply:
    #全局事务配置项
  transaction:
    #切点表达式
    aopPointctExpression: (execution(* com.li.*.service..*(..))) || (execution(* com.li.*.**.service..*(..)))
    #操作方法数组
    saveMethodPrefix:
      - '*insert*'
      - '*add*'
      - '*update*'
      - '*save*'
      - '*delete*'
    #读取方法数组
    readMethodPrefix:
      - '*select*'
      - '*get*'
      - '*find*'
      - '*query*'

全局事务切面

import lombok.Data;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局事务配置切面
 */

@Data
@Aspect
@Order(2)
@Configuration
@ConfigurationProperties(prefix = "apply.transaction")
public class SpringTxAspectConfig {


    /** 定义切点表达式 **/
    private String aopPointctExpression;

    /** 操作方法数组*/
    private String[] saveMethodPrefix;

    /** 读取方法数组*/
    private String[] readMethodPrefix;

    @Resource
    private PlatformTransactionManager transactionManager;


    /**
     * 增强(事务)的属性的配置
     * @return TransactionInterceptor
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Bean
    public TransactionInterceptor txAdvice() {
        NameMatchTransactionAttributeSource txAttributeS = new NameMatchTransactionAttributeSource();
        RuleBasedTransactionAttribute requiredAttr = new RuleBasedTransactionAttribute();
        // 设置事务传播 ,PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中
        requiredAttr.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 抛出异常后执行切点回滚
        requiredAttr.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        //设置只读
        requiredAttr.setReadOnly(false);
        RuleBasedTransactionAttribute supportsAttr = new RuleBasedTransactionAttribute();
        //设置传播规则, PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
        supportsAttr.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
        // 只读事务,不做更新操作
        supportsAttr.setReadOnly(true);
        //切入方法
        Map txMethod = new HashMap();
        for (String prefix : saveMethodPrefix) {
            txMethod.put(prefix, requiredAttr);
        }
        for (String prefix : readMethodPrefix) {
            txMethod.put(prefix, supportsAttr);
        }
        txAttributeS.setNameMap(txMethod);
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, txAttributeS);
        return txAdvice;
    }

    /**
     * AOP配置定义切面和切点的信息
     */
    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(aopPointctExpression);//设置切点表达式
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}

注意:需在数据源配置类中向事务管理器注入动态数据源

5.SpringAop的xml开发转为注解开发

6.SpringMVC相关xml配置提取

6.1 视图文件路径配置(视图解析器)

spring:
  mvc:
    #springmvc视图解析路径
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp

6.2 拦截器注册

/**
 * 权限控制拦截器注册
 */
@Configuration
public class AccessInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 1.  registry.addInterceptor(new LoginInterceptor())       注册登录拦截器
        // 2.  addPathPatterns("/**")                                拦截器拦截所有地址
        // 3.  excludePathPatterns(excludes)                         指定不被拦截的路径
         registry.addInterceptor(new AccessControlInterceptor());
    }
}

6.3 文件上传限制

配置类配置

 @Bean
    public MultipartConfigElement multipartConfigElement(){
        MultipartConfigFactory factory = new MultipartConfigFactory();
        // 上传单个文件大小设置
        factory.setMaxFileSize(DataSize.ofMegabytes(104857600));
        // factory.setMaxFileSize("20M");
        // 上传多个文件总共大小设置
        //factory.setMaxRequestSize(DataSize.ofGigabytes(2L));
        return factory.createMultipartConfig();
    }

yml配置

spring:
   servlet:
   	 multipart:
       enabled: true #是否启用http上传处理
       max-request-size: 104857600 #最大请求文件的大小
       max-file-size: 20MB #设置单个文件最大长度
       file-size-threshold: 20MB #当文件达到多少时进行磁盘写入

6.4 全局异常处理

未测试

/**
     * SpringMvc异常解析器
     */
    @Bean
    public SimpleMappingExceptionResolver exceptionResolver(){
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        simpleMappingExceptionResolver.setDefaultErrorView("/common/failure");
        Properties properties = new Properties();
        properties.setProperty("java.sql.SQLException","/common/failure");
        properties.setProperty("java.lang.RuntimeException","/common/failure");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }

springboot全局异常处理

/**
 * 全局异常处理
 */
@ControllerAdvice
public class AdminExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(AdminExceptionHandler.class);


    @ExceptionHandler(value = SQLException.class)
    public String javaExceptionHandler(SQLException ex) {
        logger.error("捕获到SQLException异常{}",ex);
        return "/common/failure";
    }

    @ExceptionHandler(value = RuntimeException.class)
    public String javaExceptionHandler(RuntimeException ex) {
        logger.error("捕获到RuntimeExceptionException异常{}",ex);
        return "/common/failure";
    }

}

7.web.xml配置提取

7.1 全局应用上下文配置

server:
  servlet:
     # 应用上下文路径
    context-path: /xxx

7.2 全局上下文参数配置

server:
  servlet:
    context-parameters:
      log4jRefreshInterval: 6000

7.3 session超时时间

server:
  #session超时时间
  servlet:
    session:
      timeout: 30m

7.4 字符过滤

   /**
     * 字符过滤
     */
    @Bean
    public FilterRegistrationBean characterFilterRegistrationBean(){
        FilterRegistrationBean bean  = new FilterRegistrationBean<>();
        //使用框架中的过滤器类
        CharacterEncodingFilter filter  = new CharacterEncodingFilter();
        //指定使用的编码方式
        filter.setEncoding("utf-8");
        //指定request , response都使用encoding的值
        filter.setForceEncoding(true);
        bean.setFilter( filter);//字符过滤
        bean.addUrlPatterns("/*");
        return bean;
    }

关闭springboot默认的字符过滤,否则不生效

server:
    encoding:
      enabled: false

使用系统字符过滤

server:
  servlet:
    #开启系统字符过滤
    encoding:
      enabled: true
      #强制request,response都使用charset属性的值
      force: true
      #指定使用的编码方式
      charset: utf-8

7.5 filter注册

/**
 * 过滤器注册配置
 *
 */
@Configuration
public class WebFilterConfig {
​
    /**
     * 过滤器注册
     * session过期跳登录页
     */
    @Bean
    public FilterRegistrationBean basicFilterRegistrationBean(){
        FilterRegistrationBean bean  = new FilterRegistrationBean<>();
        bean.setFilter( new BasicFilter());
        bean.addUrlPatterns("*.do","*.action");
        return bean;
    }
}

7.6 listener注册

    /**
     * 注册listener
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public ServletListenerRegistrationBean listenerRegister() {
        ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean();
        srb.setListener(new RequestContextListener());
        return srb;
    }

7.7 前端控制器配置

springboot2.6.x以上该后缀匹配失效

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
​
/**
 * 前端控制器配置
 */
public class MyWebAppInitializer implements WebApplicationInitializer {
​
    @Override
    public void onStartup(ServletContext container) {
        XmlWebApplicationContext appContext = new XmlWebApplicationContext();
      /*  appContext.setConfigLocation("classpath:spring-mvc.xml");*/
        DispatcherServlet dispatcherServlet = new DispatcherServlet(appContext);
        dispatcherServlet.setDispatchOptionsRequest(true);
        ServletRegistration.Dynamic dispatcher =
                container.addServlet("dispatcher", dispatcherServlet);
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("*.action","*.do");
    }
}
 spring:
   mvc:
    servlet:
      load-on-startup: 1 #servlet容器启动优先级

springboot2.6.x以上该后缀匹配失效解决方案

加过滤器,保证过滤器链中该过滤器先执行,需处理静态资源访问问题,使用a字母开头命名,确保连音顺序第一个执行

@Slf4j
@WebFilter(urlPatterns = "/*")
public class AServletFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String uri = request.getRequestURI();
        if(!uri.contains(".")){
            log.info("请求路径:{}","http://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath());
            response.sendRedirect("http://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath()+"/common/failure.action");
            return;
        }
        //判断是否是静态资源目录
        if(uri.contains("/resource/")){
            //静态资源直接放行
            filterChain.doFilter(request,servletResponse);
            return;
        }

        //非静态资源判断后缀
        String endStr = uri.substring(uri.lastIndexOf("."));
        if(endStr.contains(".do")||endStr.contains(".action")){
            //静态资源直接放行
            filterChain.doFilter(request,servletResponse);
            return;
        }
        response.sendRedirect("http://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath()+"/common/failure.action");
    }

    @Override
    public void destroy() {

    }
}

7.8 开启后缀匹配

springboot2.6.x以下版本

import com.ws.sys.core.web.interceptor.AccessControlInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
​
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
​
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        //开启路径后缀匹配
        configurer.setUseRegisteredSuffixPatternMatch(true);
    }
​
}

springboot2.6.x以上版本,需修改匹配策略

但是不加后缀时此时也可以正常访问,并且此时前端控制器的后缀路径配置失效

spring:
  main:
    allow-circular-references: true #依赖循环
  mvc:
    pathmatch:
      use-suffix-pattern: true #后缀匹配
      matching-strategy: ant_path_matcher #匹配策略

8.springboot集成log4j2日志

8.1 pom依赖


   
    
        org.springframework.boot
        spring-boot-starter-web
        
            
                org.springframework.boot
                spring-boot-starter-logging
            
        
           
         
        
            org.springframework.boot
            spring-boot-starter-log4j2
        

8.2 log4j2.xml文件





    
​
    
    
        
        
        
        
        
        
    
​
    
​
        
            
            
            
            
        
​
        
        
            
        
​
        
        
            
            
            
            
                
                
                
            
            
            
        
​
        
        
            
            
            
            
                
                
                
            
            
            
        
​
        
        
            
            
            
            
                
                
                
            
            
            
        
​
    
​
    
    
    
​
        
    
        
        
        
            
        
​
        
            
            
            
            
            
        
    

9.定时任务

9.1 Quartz 定时任务

1.依赖

   
        
            org.quartz-scheduler
            quartz
            2.2.1
        
        
        
            org.quartz-scheduler
            quartz-jobs
            2.2.1
        

2.普通方法定时任务配置

/**
 * Quartz配置(方法测试任务配置)
 */
@Configuration
public class QuartzConfig  {
​
   /**
     * 要调用的工作类
     */
    @Bean
    public Task quartzDay(){
        return new Task();
    }
​
​
    // 配置中设定了
    // ① targetMethod: 指定需要定时执行scheduleInfoAction中的simpleJobTest()方法
    // ② concurrent:对于相同的JobDetail,当指定多个Trigger时, 很可能第一个job完成之前,
    // 第二个job就开始了。指定concurrent设为false,多个job不会并发运行,第二个job将不会在第一个job完成之前开始。
    // ③ cronExpression:0/10 * * * * ?表示每10秒执行一次,具体可参考附表。
    // ④ triggers:通过再添加其他的ref元素可在list中放置多个触发器。 scheduleInfoAction中的simpleJobTest()方法
    @Bean(name = "daymailtask")
    public MethodInvokingJobDetailFactoryBean detailFactoryBean(@Qualifier("quartzDay") Object quartzDay) {
        MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean();
        bean.setTargetObject(quartzDay);
        bean.setTargetMethod("xxx");//调用方法名
        bean.setConcurrent(false);
        return bean;
    }
    @Bean(name = "daymailtaskB")
    public MethodInvokingJobDetailFactoryBean detailFactoryBean2(@Qualifier("quartzDay") Object quartzDay) {
        MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean();
        bean.setTargetObject(quartzDay);
        bean.setTargetMethod("xxx");
        bean.setConcurrent(false);
        return bean;
    }
​
​
    @Bean(name = "doDayMailTime")
    public CronTriggerFactoryBean cronTriggerBean(@Qualifier("daymailtask") MethodInvokingJobDetailFactoryBean daymailtask) {
        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
        tigger.setJobDetail(daymailtask.getObject());
        tigger.setCronExpression("0/10 * * * * ?");// 十秒一次
        return tigger;
​
    }
​
    @Bean(name = "doDayMailTimeB")
    public CronTriggerFactoryBean cronTriggerBean2(@Qualifier("daymailtaskB") MethodInvokingJobDetailFactoryBean daymailtaskB) {
        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
        tigger.setJobDetail(daymailtaskB.getObject());
        tigger.setCronExpression("0/10 * * * * ?");// 十秒一次
        return tigger;
​
    }
​
    @Bean
    @Lazy(false)
    public SchedulerFactoryBean schedulerFactory(Trigger[] cronTriggerBean) throws IOException {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();
        System.err.println(cronTriggerBean[0]);
        bean.setTriggers(cronTriggerBean);
        return bean;
    }
​
    @Bean
    public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
        return schedulerFactoryBean.getScheduler();
    }
}

3.连接数据库整合自定义配置文件

#Main Scheduler Settings
#配置集群时,quartz调度器的id,由于配置集群时,只有一个调度器,必须保证每个服务器该值都相同,可以不用修改,只要每个ams都一样就行 
org.quartz.scheduler.instanceName=quartzScheduler_ym
org.quartz.scheduler.rmi.export=false
org.quartz.scheduler.rmi.proxy=false
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
#集群中每台服务器自己的id,AUTO表示自动生成,无需修改 
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer=true
org.quartz.scheduler.skipUpdateCheck=true
#一次性取出的任务数,默认值是1,适合负载均衡,但不适合大量的短时任务
org.quartz.scheduler.batchTriggerAcquisitionMaxCount=25
​
#Configure ThreadPool
#quartz线程池的实现类,无需修改 
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool 
#quartz线程池中线程数,可根据任务数量和负载度来调整 
org.quartz.threadPool.threadCount=25
#quartz线程优先级 
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
​
#Configure JobStore
org.quartz.jobStore.acquireTriggersWithinLock=true
#表示如果某个任务到达执行时间,而此时线程池中没有可用线程时,任务等待的最大时间,如果等待时间超过下面配置的值(毫秒),本次就不在执行,而等待下一次执行时间的到来,可根据任务量和负责程度来调整 
org.quartz.jobStore.misfireThreshold=60000
#实现集群时,任务的存储实现方式,org.quartz.impl.jdbcjobstore.JobStoreTX表示数据库存储,无需修改 
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
#连接数据库数据源名称,与下面配置中org.quartz.dataSource.myDS的myDS一致即可,可以无需修改 
org.quartz.jobStore.dataSource=myDS
#quartz存储任务相关数据的表的前缀,无需修改 
org.quartz.jobStore.tablePrefix=QRTZ_
#是否启用集群,启用,改为true,注意:启用集群后,必须配置下面的数据源,否则quartz调度器会初始化失败 
org.quartz.jobStore.isClustered=true
#集群中服务器相互检测间隔,每台服务器都会按照下面配置的时间间隔往服务器中更新自己的状态,如果某台服务器超过以下时间没有checkin,调度器就会认为该台服务器已经down掉,不会再分配任务给该台服务器
org.quartz.jobStore.clusterCheckinInterval=10000
​
#Configure DataSources
org.quartz.dataSource.myDS.driver=oracle.jdbc.driver.OracleDriver
​
​
org.quartz.dataSource.myDS.URL=jdbc:oracle:thin:@xxxx:1521:ORCL
org.quartz.dataSource.myDS.user=xxx
org.quartz.dataSource.myDS.password=xxx
#配置连接数据库连接池大小,一般为上面配置的线程池的2倍 
org.quartz.dataSource.myDS.maxConnections=50
#org.quartz.dataSource.myDS.validationQuery=select 1 from dual

工具类

监听spring容器初始化,加载属性配置文件,此时添加完定时任务后,需执行调度器的stater方法进行执行。

​
@Component
public class QuartzManager implements ApplicationListener {
    @Autowired
	private StdSchedulerFactory schedulerFactory ;
    private static final String CONFIG_FILE = "quartz-job.properties";
    private Logger logger = LoggerFactory.getLogger(QuartzManager.class);
​
    /**
     * @Description:添加一个定时任务
     * @param:jobName:任务名
     * @param:jobGroupName:任务组名
     * @param:triggerName:触发器名
     * @param:triggerGroupName:触发器组名
     * @param:jobClass:任务
     * @param:cron:时间设置
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass,
            String cron, String id) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            // 任务名,任务组,任务执行类
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
            // 参数
            JobDataMap jobDataMap = jobDetail.getJobDataMap();
            jobDataMap.put("jkConfigId", id);
            // 触发器
            TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
            // 触发器名,触发器组
            triggerBuilder.withIdentity(triggerName, triggerGroupName);
            triggerBuilder.startNow();
            // 触发器时间设定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
            // 创建Trigger对象
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();
            // 调度容器设置JobDetail和Trigger
            sched.scheduleJob(jobDetail, trigger);
            // 启动
            if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * @Description:添加一个定时任务
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     * @param jobClass
     * @param cron
     * @param dataMap
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass,
            String cron, Map dataMap) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            // 任务名,任务组,任务执行类
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
            // 参数
            JobDataMap jobDataMap = jobDetail.getJobDataMap();
            jobDataMap.putAll(dataMap);//传递的所有参数
            // 触发器
            TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
            // 触发器名,触发器组
            triggerBuilder.withIdentity(triggerName, triggerGroupName);
            triggerBuilder.startNow();
            // 触发器时间设定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
            // 创建Trigger对象
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();
            // 调度容器设置JobDetail和Trigger
            sched.scheduleJob(jobDetail, trigger);
            // 启动
            if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
​
    /**
     * @Description:修改一个任务的触发时间
     * @param:jobName
     * @param:jobGroupName
     * @param:triggerName:触发器名
     * @param:triggerGroupName:触发器组名
     * @param:cron:时间设置
     */
    public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName,
            String cron) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                /** 方式一:调用 rescheduleJob 开始 */
                // 触发器
                TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
                // 触发器名,触发组名
                triggerBuilder.startNow();
                // 触发器时间设定
                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
                // 创建Trigger对象
                trigger = (CronTrigger) triggerBuilder.build();
                // 方式一:修改一个任务的触发时间
                sched.rescheduleJob(triggerKey, trigger);
                /** 方式一:调用rescheduleJob 结束 */
​
                /** 方式二:先删除,然后再创建一个新的Job */
                // JobDetail jobDetail =
                // sched.getJobDetail(JobKey.jobKey(jobName,jobGroupName));
                // Class jobClass = jobDetail.getJobClass();
                // removeJob(jobName,jobGroupName,triggerName,triggerGroupName);
                // addJob(jobName,jobGroupName,triggerName,triggerGroupName,jobClass,cron);
                /** 方式二:先删除,然后再创建一个新的job */
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
​
    /**
     * 触发状态为运行的任务
     * 
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     * @param cron
     */
    public void triggerRunningJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName,
            String cron) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            sched.triggerJob(jobKey);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
​
    /**
     * @Description:移除一个任务
     * @param:jobName
     * @param:jobGroupName
     * @param:triggerName
     * @param:triggerGroupName
     */
    public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            sched.pauseTrigger(triggerKey);// 停止触发器
            sched.unscheduleJob(triggerKey);// 移除触发器
            sched.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
​
    /**
     * @Description:启动所有定时任务
     */
    public void startJobs() {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            sched.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
​
    /**
     * @Description:关闭所有定时任务
     */
    public void shutdownJobs() {
        try {
            Scheduler sched = schedulerFactory.getScheduler();
            if (!sched.isShutdown()) {
                sched.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
​
    private void init() {
        try {

            schedulerFactory                .initialize(Thread.currentThread().getContextClassLoader().getResource(CONFIG_FILE).getFile());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {

            if (null == event.getApplicationContext().getParent()) {
                init();
           
        }
    }
}

9.2 Springboot整合Quartz

1.依赖

  
            org.springframework.boot
            spring-boot-starter-quartz
        

2.分布式quartz的yml配置

 #定时任务
  quartz:
    # 将任务等保存化到数据库
    job-store-type: jdbc
    # 程序结束时会等待quartz相关的内容结束
    wait-for-jobs-to-complete-on-shutdown: true
    # QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录
    overwrite-existing-jobs: true
    # 这里居然是个map,搞得智能提示都没有
    properties:
      org:
        quartz:
          # scheduler相关
          scheduler:
            # scheduler的实例名
            instanceName: scheduler
            instanceId: AUTO
          # 持久化相关
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            # 表示数据库中相关表是QRTZ_开头的
            tablePrefix: QRTZ_
            useProperties: false
          # 线程池相关
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            # 线程数
            threadCount: 10
            # 线程优先级
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true

3.编写核心任务方法

public class QuartzTestJob extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        String userName = (String) context.getJobDetail().getJobDataMap().get("username");
        System.out.println("userName:" + userName);
    }
}

4.配置注册

@Configuration
public class TaskConfig {
​
    @Bean("helloJob")
    public JobDetail helloJobDetail() {
        return JobBuilder.newJob(QuartzTestJob.class)
                .withIdentity("DateTimeJob")
                .usingJobData("username", "Hello Quartz")
                .storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
                .build();
    }
​
    @Bean
    public Trigger printTimeJobTrigger() {
        // 每秒执行一次
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
        return TriggerBuilder.newTrigger()
                //.forJob(helloJobDetail())
                .withIdentity("quartzTaskService")
                .withSchedule(cronScheduleBuilder)
                .build();
    }
}

9.3 对于quartz定时任务job中不能注入bean对象处理

注意事项:

  1. Quartz的定时任务的job类中实现的StatefulJob接口已过时,在开发时尽量使用 实现Job接口或者继承QuartzJobBean类的方式进行执行类的实现。

  2. 由于Quartz的job类中无法直接注入spring容器的bean对象,会报空指针异常,所以在config包下配置了自定义的quartz的工厂来将spring的bean进行注入,然后在调度器工厂的bean中进行设置,即可以在job类中对bean进行注入,但在操作时需要注入自己配置的工厂bean对象。

1.自定义工厂配置类

@Component
public class CustomJobFactory extends AdaptableJobFactory {
​
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;
​
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
​
}

2.使用配置类注入自定义工厂

/**
 * QuartZ定时任务配置
 */
@Configuration
public class QuartzConfig {
​
    @Autowired
    private CustomJobFactory customJobFactory;
​
    @SneakyThrows
    @Bean("schedulerFactory")
    public StdSchedulerFactory scheduler(){
        StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 自定义 JobFactory 使得在 Quartz Job 中可以使用 @Autowired
        scheduler.setJobFactory(customJobFactory);
        scheduler.start();
        return schedulerFactory;
    }
​
}

3.在操作工具类初始化时中注入自定义的调度器

	@Autowired
	private StdSchedulerFactory schedulerFactory ;

			schedulerFactory		.initialize(Thread.currentThread().getContextClassLoader().getResource(CONFIG_FILE).getFile());

10.全局属性配置文件提取

10.1 自定义全局yml配置

#自定义全局配置
apply:
  appRoot: xxx
  datePatterns: yyyy-MM-dd,yyyy-MM-dd HH:mm:ss,yyyy-MM-dd HH:mm,yyyyMMdd,yyyyMMddmm,yyyyMMddHHmmss
  sysUserLoginLogEnabled: true

10.2 注入配置类

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
​
/**
 * 自定义的参数yml配置
 */
@Configuration
@ConfigurationProperties("apply")
@Data
public class ApplicationConfig {
​
    private String datePatterns;
​
    private boolean sysUserLoginLogEnabled;
​
    private static String appRoot;
​
​
    public static String getAppRoot(){
        return ApplicationConfig.appRoot;
    }
}

11.自定义yml配置文件读取加载

11.1.创建yaml工厂类

import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
​
import java.io.IOException;
import java.util.List;
​
​
public class YamlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null) {
            return super.createPropertySource(name, resource);
        }
        List> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
        return sources.get(0);
    }
}

11.2.在配置类上指定使用的配置文件

@PropertySource(value = {"classpath:customize-context.yml"}, factory = YamlPropertySourceFactory.class)
@ConfigurationProperties(prefix = "apply.transaction" 

12.集成swagger2

1. 依赖

     
        
            io.springfox
            springfox-boot-starter
            3.0.0
        
        
        
            com.github.xiaoymin
            swagger-bootstrap-ui
             1.9.0
        

2.自定义yml配置

apply:
  swagger:
    groupName: xx
    title: 数据xx
    enable: false
    description: API-接口说明
    basePath: com.li
    version: 1.0.01

3.配置类

@Configuration
@EnableOpenApi
@EnableSwaggerBootstrapUI
@ConfigurationProperties(prefix = "apply.swagger")
public class SwaggerAutoConfiguration {
​
​
    private static  String basePath;
​
    /**
     * 是否开启swagger配置,生产环境需关闭
     */
    private boolean enable;
    private String title;
    private String description;
    private String version;
    private String groupName;
​
    @Value("${server.address}")
    private String address;
    @Value("${server.servlet.context-path}")
    private String contextPath;
    @Value("${server.port}")
    private String port;
​
​
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .groupName(groupName)
                //是否开启 (true 开启  false隐藏。生产环境建议隐藏)
                .enable(enable)
                .select()
                //扫描的路径包,设置basePackage会将包下的所有被@Api标记类的所有方法作为api
                .apis(RequestHandlerSelectors.basePackage(basePath))
                //指定路径处理PathSelectors.any()代表所有的路径
                .paths(PathSelectors.any())
                .build();
    }
​
    /**
     * 配置基本信息
     *
     * @return
     */
    @Bean
    public ApiInfo apiInfo() {
        String termsOfServiceUrl = "http://"+address+":"+port+contextPath+"/";
        return new ApiInfoBuilder()
                //设置文档标题(API名称)
                .title(title)
                //文档描述
                .description(description)
                //服务条款URL
                .termsOfServiceUrl(termsOfServiceUrl)
                //版本号
                .version(version)
                .build();
    }
}

13.配置文件切换

13.1 系统的yml配置文件切换

在项目部署时,会默认加载同jar包文件夹下的application.yml配置文件,如果与项目yml的配置文件属性相同的值则会进行覆盖。如果要切换配置文件可在外部指定spring.profiles.active 属性。

通过spring.profiles.active属性进行配置文件切换时,springboot会优先查找config资源目录下的application-xxx.yml配置文件,来切换指定的yml

#切换配置
spring:
  profiles:
    #切换后缀
    active: windows
    #包含文件后缀数组
    include:
      - common

被切换的文件需指定自己的引用

spring:
  config:
    activate:
      on-profile: windows

13.2 properties配置文件自动切换实现

1.非静态属性变量或方法获取配置文件及属性值

当工具类读取属性配置文件时,通过@Value注解获取对应的config下已切换的application-xxx.yml的值 ,自定义的apply.properties.active.xxx的对应的切换后缀值进行注入,然后进行字符串或对应文件目录拼接获取。

在每种配置文件中加入自己对应的切换后缀,在spring.profiles.active切换yml时就能读取到对应的properties配置文件的后缀,再注入拼接字符串

如application-windows.yml

  #自定义配置文件切换
apply:  
  properties:
    active:
      #切换文件后缀配置
      dataxBase: windows
      config: windows

2.非静态属性变量或方法获取配置文件及属性值

由于@Value注解不能注入静态属性值,对于部分静态方法和构造调用如果强行注入则会为空值,则需通过自定义PropertiesActiveConfig下的yml工厂来获取指定的yml配置文件来手动获取对应的值, 在自定义工具类中,通过手动注入spring.profiles.active,来判断当前获取的是那个yml配置文件,由于获取的配置文件值的工厂方法为静态方法,所以需要通过手动的set方法对静态变量进行注入。

配置类

/**
 * yml切换配置静态属性文件读取工具类
 */
@Slf4j
@Component
public class PropertiesActiveConfig {
​
    private static String active;
​
    @Value("${spring.profiles.active}")
    public void setActive(String active) {
        PropertiesActiveConfig.active = active;
    }
​
    public static Object getCommonYml(Object key){
        Resource resource = new ClassPathResource("/config/application-"+active+".yml");
        log.info("属性文件切换:{}",active);
        Properties properties = null;
        try {
            YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
            yamlFactory.setResources(resource);
            properties =  yamlFactory.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return properties.get(key);
    }
​
}

调用获取指定属性

	public static String suffix = (String) PropertiesActiveConfig.getCommonYml("apply.properties.active.config");

你可能感兴趣的:(SpringBoot,SSM,JavaEE,java,spring,boot,spring)