Spring Boot 入门(四)整合 Thymeleaf、国际化、发邮件、批量处理


代码在 https://github.com/betterGa/SpringBootDemo

一、Spring Boot Thymeleaf 示例

概述
    Thymeleaf 是一个基于 Java 的库,用于创建 Web 应用程序, 它为在Web 应用程序中提供 XHTML/HTML5 提供了很好的支持。
    Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP ,相较与其他的模板引擎,它有如下三个吸引人的特点:
(1)Thymeleaf 在 有网络 和 无网络 的环境下皆可运行,即 它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板 + 数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
(2)Thymeleaf 开箱即用的特性,它提供标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、OGNL 表达式效果,避免每天套模板、改 JSTL、改标签的困扰,同时开发人员也可以扩展和创建自定义的方言。
(3)Thymeleaf 提供 Spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
    
引入 thymeleaf 依赖

  • pom 文件中加入以下依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

    
创建 thymeleaf 模板
在 resoures 包下新建 templates下包,新建 index.html :

<!doctype html> <!--注意:引入thymeleaf的名称空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title></head>
<body><p th:text="hello">Hello</p></body>
</html>

提供控制类:

@Controller
public class indexController {
    
    
    @RequestMapping("/index")
    public String index(){
    
    
        // 默认找 temple.index.html 这个模板视图文件
        return "index";
    }
}

运行结果:
在这里插入图片描述
    
通过控制器定位 thymeleaf 模板
    接下来看看 thymeleaf 常用标签的使用。
首先,提供一个实体类:

public class User {
    
    
    private String userName;
    private String password;
    private List<String> hobbies;
    private HashMap<String,String> secrets;
  }

控制类:

@Controller
public class indexController {
    
    
    @RequestMapping("/index")
    public String index(Model model){
    
    
        User user= new User();
        user.setUserName("jia");
        user.setPassword("123456");
        user.setHobbies(Arrays.asList(new String[]{
    
    "football","singing","dance"}));
        HashMap<String,String> map=new HashMap();
        map.put("1","a");
        map.put("2","b");
        map.put("3","c");
        map.put("4","d");
        user.setSecrets(map);
        model.addAttribute("user",user);
        // 默认找 temple.index.html 这个模板视图文件
        return "index";
    }
}

然后在 index.html 中 显示 user 对象:

<p th:text="${user}"></p>

    这里 ${user} 的 “user 和 :model.addAttribute("user",user); 里的第一个 “user” 是对应的。运行结果:
在这里插入图片描述
接下来依次获取属性,可以直接获取属性,也可以通过 getter 方法:

<p th:text="${user}"></p>
<p th:text="${user.getUserName()}"></p>
<p th:text="${user.getPassword()}"></p>
<p th:text="${user.hobbies}"></p>
<p th:text="${user.secrets}"></p>

<table th:each="item,sta:${user.hobbies}">
<tr>
    <td th:text="${item}"></td>
    <td th:text="${sta.index}"></td>
    <td th:text="${sta.odd}"></td>
    <td th:text="${sta.size}"></td>
</tr>
</table>

<div th:each="item:${user.secrets}" th:text="${item.key}"></div>
<div th:each="item:${user.secrets}" th:text="${item.value}"></div>

<input type="text" th:attr="value=${user.userName}">
<input type="text" th:attr="value=${user.password}">

运行结果:
在这里插入图片描述
    

二、SpringBoot 使用 RESTful 服务

提供商品实体类:

public class Products {
    
    
    private String name;
    private String id;
 }

提供两个控制类,它们 会默认找 template 包里的 html thymeleaf 模板 的使用就是这样的,控制类上加 @Controller 注解,返回的就是 template 包里对应的 html

@Controller
public class ProductController {
    
    

    static List<Products> list = new ArrayList<>();

    static {
    
    
        list.add(new Products("1", "衣服"));
        list.add(new Products("2", "鞋子"));
        list.add(new Products("3", "帽子"));
    }


    // 查询
    @RequestMapping("/view-products")
    public String viewProducts() {
    
    
        return "view-product";
    }

    // 新增
    @RequestMapping("/add-products")
    public String addProducts() {
    
    
        return "add-product";
    }

    // 获取商品列表
    @GetMapping("/products")
    @ResponseBody
    public List<Products> getProducts(){
    
    
        return list;
    }

    @PostMapping("/products")
    @ResponseBody
    public Products addProducts(@RequestBody Products products){
    
    
        list.add(products);
        return products;
    }

接下来提供对应的 html,添加 jQuery 库并 编写代码,在页面加载时使用 RESTful Web服务,先写 view-products.html :

<html xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
    <script> $(document).ready(function () {
     
     
        $.getJSON("http://localhost:9999/products", function (result) {
     
     
            $.each(result, function (key, value) {
     
     
                $("#productsJson").append(value.id + " " + value.name + " ");
            });
        });
    });
    </script>
</head>
<bodyb
<div id="productsJson"></div>
</body>
</html>

运行结果:
在这里插入图片描述
再写 add-products.html :

<html>
<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>

    <script>
        $(document).ready(function () {
     
     
            $("button").click(function () {
     
     
                var newProduct = {
     
     id: "4", name: "车子"};
                var productstr = JSON.stringify(newProduct);
                $.ajax({
     
     
                        type: "POST",
                        url: "http://localhost:9999/products",
                        contentType: "application/json;charset=UTF-8",
                        data: productstr,
                        success: function (data) {
     
     
                            alert("新增" + data.name + "成功!");
                        },
                        error: function (data) {
     
     
                            alert(data);
                        }
                    }
                );
            });
        });
    </script>
</head>
<body>
<button>新增商品</button>
</body>
</html>

运行结果:
在这里插入图片描述
在这里插入图片描述
    

三、Spring Boot 国际化

    国际化 是一个 使应用程序适应不同语言和区域 而 无需对源代码进行工程更改的过程。就是说,根据不同的国家展示不同的语言。
    默认情况下,Spring Boot 应用程序从 类路径下的 src/main/resources 文件夹中获取 消息源。 缺省语言环境消息文件名应为 message.properties ,每个语言环境的文件应命名为 messages_XX.properties 。 “XX”表示区域代码。 (注意喔,是 messages ,达成 message 都是访问不到的!)应将所有消息属性设置为键值对。 如果在语言环境中找不到任何属性,则应用程序将使用 messages.properties 文件中的默认属性。

比如,英文,messages.properties :

welcome.text = welcome everyone

中文,messages_cn.properties :

welcome.text = 欢迎大家

提供访问路径:

@RequestMapping("/locale")
    public String locale(){
    
    
        return "locale";
    }

    
提供相应的 locale.html 页面:

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>springboot国际化</title>
</head>
<body>
<h1 th:text="#{welcome.text}">
</h1>

</h1>
</body>
</html>

需要确定应用程序的默认 Locale,添加 LocaleResolver bean :

@Bean 
    public LocaleResolver localeResolver(){
    
    
        SessionLocaleResolver sessionLocaleResolver=new SessionLocaleResolver();
        sessionLocaleResolver.setDefaultLocale(Locale.US);
        return sessionLocaleResolver;
    }

需要拦截器 用于 根据 添加到请求的语言参数的值 更改新的 Locale,在 InterceptorConfig 类中新增方法:

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor(){
    
    
        LocaleChangeInterceptor localeChangeInterceptor=new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("language");
        return localeChangeInterceptor;
    }

然后注册这个拦截器:
在这里插入图片描述
运行结果:
在这里插入图片描述

将参数 language=cn 传入:
在这里插入图片描述
(如果中文出现了乱码,注意把 IDEA 项目字符编码为 UTF-8 :
在这里插入图片描述

四、Spring Boot 启动 Swagger3

     Swagger3 是一个开源项目,用于为 RESTful Web 服务生成 REST API 文档,文档里会描述接口的信息,比如它的功能、参数、返回值等等。 它提供了一个用户界面,可通过 Web 浏览器访问 RESTful Web 服务。
     要在Spring Boot应用程序中启用Swagger3,需要在 pom.xml 中添加以下依赖项:

		<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>

接下来,在 主Spring Boot应用程序中 添加 @EnableOpenApi 注释。 @EnableOpenApi 注释用于为Spring Boot应用程序启用 Swagger3:
在这里插入图片描述

    

控制类方法上
在这里插入图片描述
在这里插入图片描述
其他方法也是同理,在方法上加 @ApiOperation 注解,并写入方法的作用即可。

实体类上使用 @ApiModel 注解,属性使用 @ApiModelProperty :
在这里插入图片描述
运行结果:
在这里插入图片描述
接下来,进行一些配置,比如说只显示我们注释了的类,需要创建 Docket Bean 以为 Spring Boot 应用程序配置 Swagger3 :

 @Bean
    public Docket createRestApi(){
    
    
     return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo(){
    
    
        return new ApiInfoBuilder()
                .title("Swagger3 接口文档")
                .description("描述")
                .contact(new Contact("jia","http://www.jia.com","[email protected]"))
                .version("1.0")
                .build();
    }

这时再运行:
在这里插入图片描述

五、Spring Boot 发送电子邮件

    通过使用 Spring Boot RESTful Web 服务,可以发送包含 pop/stml 传输层安全性的电子邮件。
    
首先,在 pom.xml 中添加 Spring Boot Starter Mail 依赖项。

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

接下来,需要打开邮箱的 POP3/SMTP服务,开启后会给出一串授权码,需要拷贝保存下来:

在这里插入图片描述
如果只是发送简单邮件(没有附件),则只需要在 application.properties 文件中配置

spring.mail.default-encoding=UTF-8
spring.mail.host=smtp.163.com
[email protected]
##注意这个配置是设置邮件接收配置后给的授权码,不是邮箱密码
spring.mail.password=QSTSIXQQLJUIGWQV
spring.mail.properties.mail.smtp.auth=true 
spring.mail.properties.mail.smtp.starttls.enable=true 
spring.mail.properties.mail.smtp.starttls.required=true

提供控制类:

package com.jia.SpringBootDemo.controller;

@RestController
public class EmailController {
    
    
    
    @Autowired
    JavaMailSender javaMailSender;
    
    @RequestMapping("/sendMail")
    public String sendMail(){
    
    
        mail();
        return "发送成功";
    }

    public void mail(){
    
    
        // 发送简单邮件,无附件
        SimpleMailMessage message=new SimpleMailMessage();
        message.setFrom("[email protected]");
        message.setSubject("邀请函");
        message.setText("发来一封邀请函");
        message.setTo("[email protected]");
        javaMailSender.send(message);
    }
}

运行结果:
在这里插入图片描述
    而且可以看到,确实是发送、接受到了邮件。
    
接下来发送带附件的邮件:

 // 复杂邮件 = 正文 + 附件
    private void mail1() throws MessagingException, IOException {
    
    

        // 设置邮件服务器的属性
        Properties properties=new Properties();
        properties.put("mail.host","smtp.163.com");
        properties.put("mail.transport.protocol","smtp");
        properties.put("mail.smtp.auth","true");
        properties.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
        properties.put("mail.smtp.port","465");

        Session session=Session.getDefaultInstance(properties);

        MimeMessage mimeMessage = new MimeMessage(session);

        // 设置邮件来源
        mimeMessage.setFrom(new InternetAddress("[email protected]",false));

        // 设置邮件接收方
        mimeMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse("[email protected]"));

        // 设置邮件主题
        mimeMessage.setSubject("一封邮件");

        // 设置邮件日期
        mimeMessage.setSentDate(new Date());

        // 正文部分
        MimeBodyPart mimeBodyPart = new MimeBodyPart();
        mimeBodyPart.setText("发来一封邀请函,带附件");

        // 附件部分
        MimeBodyPart attachPart = new MimeBodyPart();
        attachPart.attachFile("D:/pictures/pickGirl.jpg");

        // 消息载体
        MimeMultipart mimeMultipart = new MimeMultipart();
        mimeMultipart.addBodyPart(mimeBodyPart);
        mimeMultipart.addBodyPart(attachPart);

        // 将消息载体作为整个邮件的主体内容
        mimeMessage.setContent(mimeMultipart);


        Transport transport=session.getTransport();

        // 连接发送端
        transport.connect("smtp.163.com","[email protected]","QSTSIXQQLJUIGWQV");

        transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());
        transport.close();
    }

    

六、Spring Boot 批量处理

    批处理服务是 在单个任务中执行多个命令的过程,
    Spring Batch 是 处理大量数据操作的一个框架,主要用来读取大量数据,然后进行一定的处理后输出指定的形式。比如我们可以将 csv 文件中的数据(数据量几百万甚至几千万都是没问题的)批处理插入保存到数据库中 ,就可以使用该框架。

  • Spring Batch 框架的组成部分
    (1)JobRepository:用来注册 Job 容器,设置数据库相关属性。
    (2)JobLauncher:用来启动 Job 的接口。
    (3)Job:我们要实际执行的任务,包含一个或多个。
    (4)Step:即步骤,包括:ItemReader->ItemProcessor->ItemWriter。
    (5)ItemReader:用来读取数据,做实体类与数据字段之间的映射。比如读取 csv 文件中的人员数据,之后对应实体 person 的字段做 mapper 。
    (6)ItemProcessor:用来处理数据的接口,同时可以做数据校验(设置校验器,使用 JSR-303(hibernate-validator) 注解),比如将中文性别 男/女,转为 M/F。同时校验年龄字段是否符合要求等 。
    (7)ItemWriter:用来输出数据的接口,设置数据库源。编写预处理 SQL 插入语句。
        

  • 批处理流程图
    在这里插入图片描述

    

  • 实战

首先,添加依赖:

  		<!-- spring批量处理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <!-- 实体验证-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.7.Final</version>
        </dependency> 
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency> 
        <!--连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>

接下来,在 resources 包下添加简单的 csv 文件:
在这里插入图片描述
建一个表 person :

CREATE TABLE `person` 
( `id` int(11) NOT NULL, 
`name` varchar(10) DEFAULT NULL, 
`age` int(11) DEFAULT NULL, 
`gender` varchar(2) NOT NULL, 
PRIMARY KEY (`id`) ) ENGINE=InnoDB;

然后需要一个对应的实体类 :

public class personEntry {
    
    
    
    private int id;
    
    @Size(min = 2, max = 5)
    private String age;
    private String name;
    private String gender;

	/*省略构造方法、getter、setter 方法*/

提供 ItemReader 读取数据:

@Configuration
public class BatchConfig {
    
    

    @Bean
    public ItemReader<personEntry> reader() {
    
    

        // new 一个文件阅读器
        FlatFileItemReader<personEntry> reader = new FlatFileItemReader<>();

        // 设置文件
        reader.setResource(new ClassPathResource("person.csv"));

        // 文件与实体类映射
        // 设置 key
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setNames(new String[]{
    
    "id", "name", "age", "gender"});

        // 设置实体对应
        BeanWrapperFieldSetMapper fieldSetMapper = new BeanWrapperFieldSetMapper<personEntry>();
        fieldSetMapper.setTargetType(personEntry.class);

        DefaultLineMapper<personEntry> personDefaultLineMapper = new DefaultLineMapper<personEntry>();
        personDefaultLineMapper.setLineTokenizer(lineTokenizer);
        personDefaultLineMapper.setFieldSetMapper(fieldSetMapper);

		// 关联到阅读器
 		reader.setLineMapper(personDefaultLineMapper);
 		
        return reader;
    }
}

提供 ItemProcessor,进行 校验和处理 ,将中文性别 男/女,转为 M/F。:

// 处理和校验数据
public class csvItemProcesser extends ValidatingItemProcessor<personEntry> {
    
    
    Logger logger= LoggerFactory.getLogger(csvItemProcesser.class);
    @Override
    public personEntry process(personEntry item) throws ValidationException {
    
    
        // 将 男/女 换成 M/F
        logger.info("process is validating......");

	    // 在超类中进行校验
        super.process(item);
        
        // 进行处理
        if(item.getGender().equals("男")) {
    
    
            item.setGender("M");
        } else {
    
    
            item.setGender("F");
        }
        
        logger.info("process end validating......");
        return item;
    }
}

另外,注意到 personEntry 实体类中,使用注解 对年龄的长度进行了限制,校验年龄字段是否符合要求。 注意 ,@ Size 只能用在 CharSequence 也就是 字符、字符串;Collection、Map 也就是集合;还有 Array 数组上,表示它们的 个数,不能用在数值类型上,不是进行范围校验

@Size(min = 2, max = 5)
    private String age;

接下来需要对应地定义校验器:因为 @Size 是使用 JSR-303(hibernate-validator) 注解,来校验 ItemReader 读取到的数据是否满足要求,如不满足则不会进行接下来的批处理任务:

import org.springframework.batch.item.validator.Validator;
import org.springframework.beans.factory.InitializingBean;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.ValidatorFactory;
import java.util.Set;

public class BeanValidator<T> implements Validator<T>, InitializingBean {
    
    
    javax.validation.Validator validator;

    @Override
    public void validate(T t) {
    
    
        Set<ConstraintViolation<T>> constraintViolationSet = validator.validate(t);
        if (constraintViolationSet.size() > 0) {
    
    
            StringBuilder message = new StringBuilder();
            for (ConstraintViolation<T> constraintViolationSets : constraintViolationSet) {
    
    
                message.append(constraintViolationSets.getMessage() + "\n");
            }
            throw new ValidationException(message.toString());
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        validator = validatorFactory.usingContext().getValidator();
    }
}

有了校验和处理数据的处理器,接下来需要在配置类 BatchConfig 中提供方法,把这俩处理器配置进来:

	// 处理数据
    @Bean
    public ItemProcessor<personEntry,personEntry> processor(){
    
    
      csvItemProcesser csvItemProcesser=  new csvItemProcesser();
      csvItemProcesser.setValidator(beanValidator());
      return csvItemProcesser;
    }

    @Bean
    public BeanValidator beanValidator(){
    
    
        return new BeanValidator();
    }

提供 ItemWritter:

 @Bean
 public ItemWriter<personEntry> writer(DataSource dataSource){
    
    
       JdbcBatchItemWriter writer= new JdbcBatchItemWriter<>();
       writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<personEntry>());
       String sql="insert into person values(:id,:name,:age,:gender)";
       writer.setSql(sql);
       writer.setDataSource(dataSource);
       return writer;
    }

    
注册 Job 容器:

@Bean
public JobRepository cvsJobRepository(DataSource dataSource, PlatformTransactionManager manager) throws Exception {
    
    
    JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
    jobRepositoryFactoryBean.setDatabaseType("mysql");
    jobRepositoryFactoryBean.setTransactionManager(manager);
    jobRepositoryFactoryBean.setDataSource(dataSource);
    return jobRepositoryFactoryBean.getObject();
}

有了 Job 容器,接下来就需要提供启动 Job 容器的接口:

// 容器启动器
    @Bean
    public SimpleJobLauncher csvJobLauncher(DataSource dataSource, PlatformTransactionManager manager) throws Exception {
    
    
       SimpleJobLauncher simpleJobLauncher= new SimpleJobLauncher();
       simpleJobLauncher.setJobRepository(cvsJobRepository(dataSource,manager));
       return simpleJobLauncher;
    }

提供监视器:

// 监听器 
public class JobListener implements JobExecutionListener {
    
    

    Logger logger = LoggerFactory.getLogger(JobListener.class);
    long startTime;
    long endTime;

    @Override
    public void beforeJob(JobExecution jobExecution) {
    
    
        startTime = System.currentTimeMillis();
        logger.info("job process strat......");


    }

    @Override
    public void afterJob(JobExecution jobExecution) {
    
    
        endTime = System.currentTimeMillis();
        logger.info("job process end......");
        logger.info("cost time:"+(endTime-startTime)+" ms");
    }
}

这样,ItemReader、ItemProcessor、ItemWritter 都有了,接下来需要定制 step :

@Bean
    public Step step(StepBuilderFactory stepBuilderFactory, ItemReader<personEntry> reader,
                     ItemWriter<personEntry> writer, ItemProcessor<personEntry, personEntry> processor) {
    
    

        return stepBuilderFactory.get("step")

                // 数据是按块处理的,chunk 的机制是
                // 每次拿一条数据,积累到 65000 再一次性提交给数据库
                .<personEntry,personEntry>chunk(65000)
                .reader(reader)
                .processor(processor)
                .writer(writer)
                .build();
    }

这时需要给 BatchConfig 类上添加 @EnableBatchProcessing 注解,开启批量处理器:
在这里插入图片描述

还需要 @Import(DruidDBConfig.class) 注解,导入数据源的相关配置,配置写在 DruidDBConfig 类中:

// 数据库配置

@Configuration
public class DruidDBConfig {
    
    
    private Logger logger = LoggerFactory.getLogger(DruidDBConfig.class);

    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Bean
    @Primary
    // 被注入的优先级最高
    public DataSource dataSource() {
    
    
        DruidDataSource dataSource = new DruidDataSource();
        logger.info("-------->dataSource[url=" + dbUrl + " ,username=" + username + "]");
        dataSource.setUrl(dbUrl);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driverClassName);
        return (DataSource) dataSource;
    }

    @Bean
    public ServletRegistrationBean druidServletRegistrationBean() {
    
    
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        servletRegistrationBean.setServlet(new StatViewServlet());
        servletRegistrationBean.addUrlMappings("/druid/*");
        return servletRegistrationBean;
    }

    /*** 注册DruidFilter拦截 ** @return */
    @Bean
    public FilterRegistrationBean duridFilterRegistrationBean() {
    
    
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        Map<String, String> initParams = new HashMap<String, String>(); //设置忽略请求 initParams.put("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
        filterRegistrationBean.setInitParameters(initParams);
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }
}

相应的,需要在 application.yml 中配置数据源的相应信息 和 batch 信息:

##数据源配置
##数据源配置
spring:
 datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://192.168.1.109:3306/SpringBatch?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&allowPublicKeyRetrieval=true
  password: jia
  username: root
 batch:
    job:
      # 默认自动执行定义的 job(true),该为 false,需要 jobLaucher.run 执行
     enable: false
    initialize-schema: always

提供 JobController 类:

@RestController
public class JobController {
    
    

    @Autowired
    SimpleJobLauncher simpleJobLauncher;

    @Autowired
    Job importJob

    @RequestMapping("/startJob")
    public void startJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
    
    
        JobParameters jobParametERS = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
                .toJobParameters();
        simpleJobLauncher.run(importJob, jobParametERS);
    }
}

启动项目,发现报了个错:
在这里插入图片描述
可以看到,出现了 jar 包冲突 ,多个 jar 包 中包含同一个类。解决思路:将 The method’s class, javax.servlet.ServletContext, is available from the following locations: 提示的 jar 删除。
又报错,
在这里插入图片描述
主要问题是 java.sql.SQLException: null, message from server: “Host ‘LAPTOP-FDS94CNE’ is not allowed to connect to this MySQL server” ,遇到这样的问题,需要到数据库中把 host 名换成 IP 地址:
在这里插入图片描述
运行结果:
在这里插入图片描述
这时在 person.csv 中新增一条 6,jia,1,女 ,再运行程序,会看到报错:
在这里插入图片描述
可以看到,是进行了 age 的长度验证的。
    

七、总结

(1)在 Spring Boot 中集成 thymeleaf 后,在 resources/template 包中提供 html,
就可以在控制类中访问到 命名相同的模板视图文件。
    
(2)Spring Boot 应用程序会从 类路径下的 src/main/resources 文件夹中获取 消息源,来适配语言, 默认的消息文件名为 message.properties ,每个语言环境的文件应命名为 messages_XX.properties 。 “XX”表示区域代码。
    
(3) Spring Boot应用程序中 @EnableOpenApi 注释用于启用 Swagger3,用于启动类上。控制类上用 @Api 注解,其方法用 @ApiOperation 注解,实体类上用 @ApiModel 注解,属性使用 @ApiModelProperty 注解。
    
(4)发送电子邮件需要发送方的邮箱授权,无附件的邮件使用 SimpleMailMessage 类即可。
    
(5)批量处理是指使用 Spring Batch 框架,读取并处理大量数据,按照批处理流程。粗略来看就是从文件中读取数据,然后写入数据库,但实际上比较负责,需要提供:
① 数据文件
② 读取数据,做数据字段与实体类映射的 ItemReader
③ 进行校验和处理的 ItemProcessor
④ 设置数据源、SQL 语句的 ItemWritter、
⑤ 用于注册 Job 容器的 JobRepository
⑥ 用于启动 Job 的 JobLautcher
⑦ 实际执行的任务 Job
⑧ 步骤 step ,也就是 ItemReader->ItemProcessor->ItemWriter 即读取数据->处理校验数 据->写入数据的步骤。
最后通过 JobLautcher 的 run 方法即可启动任务。
    以上都以 bean 的形式在 配置类中进行配置,配置类需要使用@EnableBatchProcessing 注解开启批量处理器。
    另外可以创建监听器,在 job 执行前 和 后进行输出信息等。
    ③ 中进行校验还可以在实体类上使用 @Size 注解对属性进行长度的校验,相应地需要自己实现 Validator 接口,要注意的是,@Size 注解只能用于字符、字符串、集合、数组上,限制长度或个数,不是用于数值上的范围校验。

猜你喜欢

转载自blog.csdn.net/weixin_41750142/article/details/113520986