springboot

152 阅读12分钟

springboot中对各个层的理解以及流程

  • entity层:
    实体层,数据库在项目中的类。也被称为model层,pojo层
    用于定义与数据库对象的属性,提供get/set方法,带参和无参的构造方法
    一般数据库一张表对应一个[实体类,类属性同表字段一一对应

  • dao层:
    持久层,即mapper层,对数据库进行持久化操作,他的方法是针对数据库操作额,基本上用的就是增删改查,就是一个接口,只有方法名,具体实现在mapper.xml中。
    简单的说就是:dao层的作用为访问数据库,向数据库发送sql语句,完成数据的增删改查任务。

  • service层:
    业务层,存放业务逻辑处理,不直接对数据库进行操作,有接口和接口实现类,提供controller层调用方法。

  • controller层:
    控制层,导入service层,调用你service方法,controller通过接收前端传来的参数进行业务操作,在返回一个指定的路径或数据表。

  • 流程:
    前端发送请求,controller控制层接收请求信息,然后调用service层的接口以及接口实现类,实现类再调用dao层去操作数据库,dao层把数据返回给service层,然后再在service层进行业务处理,再接着把数据返回给controller控制层。

SpringBoot自动装配

启动类的@SpringBootApplication注解由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解组成,三个注解共同完成自动装配;

@SpringBootConfiguration 注解标记启动类为配置类
@ComponentScan 注解实现启动时扫描启动类所在的包以及子包下所有标记为bean的类由IOC容器注册为bean
@EnableAutoConfiguration通过 @Import 注解导入 AutoConfigurationImportSelector类,
    然后通过AutoConfigurationImportSelector 类的 selectImports 方法去读取需要被自动装配的组件依赖下的
    spring.factories文件配置的组件的类全名,并按照一定的规则过滤掉不符合要求的组件的类全名,
    将剩余读取到的各个组件的类全名集合返回给IOC容器并将这些组件注册为bean

springboot.png

一、springboot中的常用注解

  • springboot中常用的注解主要可以分为三种:放入容器型注解、从容器中取出型注解和功能型注解。其中的放入容器型和从容器中取出型就是我们平时所说的控制反转(IOC)和依赖注入(DI)的概念。

二、控制反转

创建一个实例对象,然后将这个对象交给spring管理
  • 1、@Component:放在类上,把普通类实例化到spring容器中。可以说很多注解都是基于这个注解的。

  • 2、@Bean: 放在方法上,用@Bean标注方法等价于XML中配置bean,这个方法一般返回一个实体对象,告诉spring这里产生一个对象,然后这个对象会交给Spring管理。产生这个对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的容器中。

  • 3、@Configuration:标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。(其实就是靠@Component注解)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public interface Configuration {
    String value() default "";
}
  • 4、@ConfigurationProperties:将配置文件中的参数映射成一个对象,通过prefix来设定前缀,然后将后面的和对象的属性名一致就能实现注入(当然这个对象需要注入的属性需要提供get和set方法 - - - 因为spring底层其实就是通过反射调用该对象的set方法)

image-20220808144856071.png

image-20220808145022497.png

  • 5、@Value : value的作用其实和ConfigurationProperties作用差不多,就是读取配置文件中参数的值,但是value是放在变量上面的,且是单值读取,还有一点就是value标注的变量并不需要和配置文件的参数名字一致。

image-20220808144651938-16599412146631.png

image-20220808145010959.png

图片.png

  • 6、@RestController、@Controller、@Service、@Repository

三、依赖注入

在这里插入图片描述

  • 1、@Resource:是按照名称来注入的,当找不到与名称匹配的bean才会按照类型来注入。其实我们平时用的@Resource都是用了他的默认的方式,即都不指定名字和类型。spring通过反射机制使用byName方法自动注入。
@Resource(type = ShiroService.class, name = "shiroService") 
private ShiroService shiroService;
@Resource 的装配顺序:
1. 如果同时指定了 name 属性和 type 属性,那么 Spring 将从容器中找唯一匹配的 bean 进行装配,找不到则抛出异常
2. 如果指定了 name 属性值,则从容器中查找名称匹配的 bean 进行装配,找不到则抛出异常
3. 如果指定了 type 属性值,则从容器中查找类型匹配的唯一的 bean 进行装配,找不到或者找到多个都会抛出异常
4. 如果都不指定,则会自动按照 byName 方式进行装配(我们一般都用的是这个。。。)
  • 2、@Autowried:默认是按照类型进行装配注入,如果允许 null 值,可以设置它 required 为false。即:当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false) ,这等于告诉 Spring:在找不到匹配 Bean 时也不报错。
@Autowired(required = false)
private ShiroService shiroService;

简单来说,Qualifier就是规定一下Bean的名字,相当于@Resource规定了name属性

  • 3、@Qualifier:@Autowired是根据类型进行自动装配的。如果当spring上下文中存在不止一个A类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在A类型的bean,而且我们又使用A类型,也会抛出BeanCreationException异常。针对存在多个A类型的Bean,我们可以联合使用@Qualifier和@Autowired来解决这些问题。
@Autowried
@Qualifier("adminDAO")
private AdminDAO adminDAO;

四、功能型注解

  • 1、@SpringBootApplication:这个注解就是集成了:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解。其中@SpringBootConfiguration:表示这个类为配置类;@EnableAutoConfiguration:表示开启自动配置,我们平时所说springboot无配置就是这个参数起的作用,他读取了springboot默认的配置;@ComponentScan:表示自动扫描,这个扫描默认只能扫同一级的目录。

  • 2、@EnableConfigurationProperties:将带有@ConfigurationProperties注解的类注入为Spring容器的Bean。如果使用了@ConfigurationProperties但是没有在启动类上增加这个注解,则@ConfigurationProperties将不起作用

  • 3、@Async与@EnableAsync:其中@Async表示这个方法为异步方法;@EnableAsync这个注解需要加在启动类上,表示支持异步操作;如果不加,则@Async将不起作用

@Async就相当于另起一个线程

@RestController
public class AsyncController {
    @Autowired
    private AsyncService asyncservice;

    @RequestMapping("/asyncTest.do")
    public String asyncTest() {
        System.out.println("############asyncTest############");
        System.out.println("############a############");
        asyncservice.test();
        System.out.println("############b############");
        return "success";
    }

    @RequestMapping("/asyncTest2.do")
    public String asyncTest2() {
        System.out.println("############asyncTest2############");
        System.out.println("############a############");
        asyncservice.test2();
        System.out.println("############b############");
        return "success";
    }

    @RequestMapping("/asyncTest3.do")
    public String asyncTest3() {
        System.out.println("############asyncTest3############");
        System.out.println("############a############");
        asyncservice.test3();
        System.out.println("############b############");
        return "success";
    }
}
@Service
public class AsyncService {

    public void test() {
        System.out.println("############c############");
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
        }
        System.out.println("############d############");
    }

    @Async
    public void test2() {
        System.out.println("############c############");
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
        }
        System.out.println("############d############");
    }

    /**
     * @Async就相当于另起一个线程
     */
    public void test3() {
        new Thread() {
            @Override
            public void run() {
                System.out.println("############c############");
                for (int i = 0; i < 5; i++) {
                    System.out.println(i);
                }
                System.out.println("############d############");
            }
        }.start();
    }

}
  • 4、@Scheduled与@EnableScheduling: 定时任务。@EnableScheduling这个注解需要加在启动类上,表示支持定时任务。
@Scheduled(cron = "0/5 * *  * * ? ")
public void doTest() {
  System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date()));
}

五、springboot热部署

1 导入依赖

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

2 选择【File】→【Settings】选项,打开Compiler面板设置页。

image-20220806162251697.png

3 Shift+Alt+/”打开Maintenance选项框,选中并打开Registry页面。

image-20220806161559263.png

4 启动项目,浏览器访问,修改controller中的内容,直接刷新页面

image-20220806162647713.png

image-20220806162728873.png

六、@SpringBootApplication

  • Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法;
  • @SpringBootApplication能够扫描Spring组件并自动配置Spring Boot。
  • @SpringBootApplication注解是一个组合注解,包含@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个核心注解

image-20220806162954196.png

七、使用@Profile注解进行多环境配置

image-20220807161617872.png

八、springboot整合Redis

1. Redis简介

Redis 是一个开源(BSD许可)的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件,并提供多种语言的API。

2. Redis优点

  • 存取速度快:Redis速度非常快,每秒可执行大约110000次的设值操作,或者执行81000次的读取操作。
  • 支持丰富的数据类型:Redis支持开发人员常用的大多数数据类型,例如列表、集合、排序集和散列等。
  • 操作具有原子性:所有Redis操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新后的值。
  • 提供多种功能:Redis提供了多种功能特性,可用作非关系型数据库、缓存中间件、消息中间件等。

3. 导入依赖

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

4. 简单Redis

@SpringBootTest
class SpringbootRedisApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Test
    public void testSet(){
        //存数据
        redisTemplate.boundValueOps("name").set("zhangsan");
    }
    @Test
    public void testGet(){
        //读数据
        Object name = redisTemplate.boundValueOps("name").get();
        System.out.println(name);
    }
}

4.5Redis序列化

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<Object , Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //使用JSON格式序列化对象,对缓存数据key和value进行转换
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //设置RedisTemplate模板PAI的序列化方式为JSON

        template.setDefaultSerializer(jackson2JsonRedisSerializer);
        return template;
    }
}

九、整合mybatis

1. 导入依赖

//mybatis依赖
<dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.1.4</version>
</dependency>

//mysql依赖
<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
</dependency>

2. 准备数据库

image-20220808155957617.png

3. 建立实体类

public class User {

    private Integer id;
    private String username;
    private String password;
    
    set and get。。。
}

4. 建立mapper接口

@Mapper
public interface Usermapper {
    
    //查询全部
    @Select("select * from user ")
    public List<User> findAll();
}

5. 测试

@SpringBootTest
class SpringbootMybatisApplicationTests {

    @Autowired
    private Usermapper usermapper;

    @Test
    public void findAll(){
        List<User> all = usermapper.findAll();
        System.out.println(all);
    }

}

十、thymeleaf模板引擎

1. 引入依赖

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

2. 测试

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>test</h1>
</body>
</html>

controller层

@Controller
public class Test {

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

3. 存入数据

前端

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"> //要使用thymeleaf,需要在html文件中导入命名空间的约束xmlns:th="http://www.thymeleaf.org"
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>test</h1>

<!--用th:text不会解析html-->
<div th:text="${msg}"></div>
<!--用th:utext会解析html-->
<div th:utext="${msg}"></div>

<!--遍历数据-->
<!--th:each-->
<h4 th:each="m :${msg2}" th:text="${m}"></h4>

<h4>
    <!--行内写法-->
    <span th:each="user:${msg2}">[[${user}]]</span>
</h4>

</body>
</html>

controller层

@Controller
public class Test {

    @RequestMapping("/test")
    public String test(Model model){
        model.addAttribute("msg","<h1>hello thylemeaf</h1>");
        model.addAttribute("msg2", Arrays.asList("zhangsan","lisi"));
        return "test";
    }
}

4. 页面可以选择语言练习

1. 导入依赖
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. controller层
@Controller
public class LoginController {
    @GetMapping("/login")
    public String toLoginPage(Model model) {
        model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
        return "login";
    }
}

3. Configuration
@Configuration
public class MyLocalResovle implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
        String l = httpServletRequest.getParameter("l");

        if (l != null){
            String[] split = l.split("_");
            Locale locale = new Locale(split[0], split[1]);
            return locale;
        }else {
            Locale locale =Locale.getDefault();
            return locale;
        }
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }

    @Bean
    public LocaleResolver localeResolver(){
        return  new MyLocalResovle();
    }
}
4. 前端
<!DCOTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link th:href="@{/css/login.css}" rel="stylesheet">
</head>
<body class="text-center">
<form>
    <h1 th:text="#{login.tip}">请登录</h1>
    <input type="text" th:placeholder="#{login.username}" required="" autofocus="" class="userName">
    <input type="password" th:placeholder="#{login.password}" required="" class="psd">
    <div>
        <label>
            <input type="checkbox" value="remember-me" class="rem">[[#{login.rememberme}]]
        </label>
    </div>
    <button type="submit" th:text="#{login.button}">登录</button>
    <p>©<span th:text="${currentYear}">2022</span>
        <span th:text="${currentYear+1}">2023</span>
    </p>
    <a th:href="@{/login(l='zh_CN')}">中文</a>
    <a th:href="@{/login(l='en_US')}">English</a>
    <a th:href="@{/login(l='ko_KR')}">韩文</a>
</form>
</body>
</html>
form{
    text-align: center;
}
input.psd,input.userName{
    display: block;
    width: 300px;
    height: 40px;
    margin-bottom: 10px;
    margin: 10px auto;
    padding-left: 10px;
    padding-right: 10px;
}
button{
    width: 325px;
    border-radius: 10px;
    height: 40px;
    background: #ccc;
    margin-bottom: 40px;
}
div{
    margin-bottom: 20px;
}
a{
    margin-left: 10px;
}
5. 配置

messages.properties

login.tip=请登录
login.username=用户名
login.password=密码
login.rememberme=记住我
login.button=登录

中文 :messages_zh_CN.properties

login.tip=请登录
login.username=用户名
login.password=密码
login.rememberme=记住我
login.button=登录

英语 :messages_en_US.properties

login.tip=Please sign in
login.username=Username
login.password=Password
login.rememberme=Remember me
login.button=Login

image-20220809161712896.png

image-20220809161824564.png

十一、security认证授权

1 基于内存的自定义身份认证

@Autowired
    private PasswordEncoder encoder;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//         1.基于内存的自定义身份认证
        auth.inMemoryAuthentication()
                .passwordEncoder(encoder)
                .withUser("admin")
                .password(encoder.encode("123456"))
                .roles("vip");
    }
    @Bean
    PasswordEncoder encoder() {
        return NoOpPasswordEncoder.getInstance();
    }

2. 基于JDBC的身份认证

@Autowired
private PasswordEncoder encoder;
    
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//2.基于JDBC的身份认证
        String userSql = "select username, password, valid from account where username = ?";
        String authoritysql = "select username, authority from account where username = ?";
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery(userSql)
                .authoritiesByUsernameQuery(authoritysql)
                .passwordEncoder(encoder);
}            
@Bean
PasswordEncoder encoder() {
    return NoOpPasswordEncoder.getInstance();
}

3. 基于UserDetailsService的身份认证

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
@Autowired
private PasswordEncoder encoder;

@Autowired
private AccountService accountService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        3.基于UserDetailsService的身份认证
        auth.userDetailsService(accountService).passwordEncoder(encoder);
    }
    @Bean
    PasswordEncoder encoder() {
        return NoOpPasswordEncoder.getInstance();
    }

4. 注销 和 记住我

  //开启自动配置的注销的功能
    // logout 注销请求
    // .logoutSuccessUrl("/跳转首页"); 注销成功来到首页
http.logout().logoutSuccessUrl("/首页地址");


   //记住我
   http.rememberMe();

十二、异步 定时任务

1. 异步

service层

@Service
public class AsyncService {

   @Async           //告诉Spring这是一个异步方法
   public void hello(){
       try {
           Thread.sleep(3000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println("进行中....");
  }
}

controller层

@RestController
public class AsyncController {

   @Autowired
   AsyncService asyncService;

   @GetMapping("/hello")
   public String hello(){
       asyncService.hello();
       return "ok";
  }
}

主启动类中添加@EnableAsync ,开启异步注解

@EnableAsync //开启异步注解功能
@SpringBootApplication
public class AsApplication {

   public static void main(String[] args) {
       SpringApplication.run(AsApplication.class, args);
  }
}

测试

2. 定时任务

service层

@Service
public class ScheduledService {

   //注意cron表达式的用法
   //秒  分   时   日   月   周几
   //0 * * * * 0-7
   @Scheduled(cron = "0 0 12 * * ?")   //表示每天中午12点触发
   public void hello(){
       System.out.println("hello.....");
  }
}

主启动类中添加@EnableScheduling,开启基于注解的定时任务

@EnableAsync      //开启异步注解功能
@EnableScheduling //开启基于注解的定时任务
@SpringBootApplication
public class SdApplication {

   public static void main(String[] args) {
       SpringApplication.run(SdApplication.class, args);
  }
}

十三、发送邮件

1. 引入依赖

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

2. 配置文件

image-20220809181554600.png 配置类中配置

spring:
	mail:
    	host: smtp.qq.com      #QQ
    	username: @qq.com   #自己的QQ邮箱
    	password:     #QQ邮箱的授权码
    	default-encoding: UTF-8

3. 前端编写

<form  method="post" style="text-align: center">
    username:<input name="username" ><br>
    msg:<input name="msg"><br>
    <button type="submit">提交</button>
</form>

4. service层

@Service
public class
EmailService {

    @Autowired
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")  //得到QQ邮箱
    private String from;


    /**
     * 发送普通邮件
     */
    public void sendSimpleMail(String to, String subject, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(to);	//收信人
        message.setSubject(subject);	//主题
        message.setText(content);	//内容
        message.setFrom(from);	//发信人

        mailSender.send(message);
    }


    /**
     * 发送HTML文件
     */
    public void sendHtmlMail(String to, String subject, String content) {
        //使用MimeMessage,MIME协议
        MimeMessage message = mailSender.createMimeMessage();

        MimeMessageHelper helper;
        //MimeMessageHelper帮助我们设置更丰富的内容
        try {
            helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);	//true代表支持html
            mailSender.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }


    /**
     * 发送带附件的邮件
     * @param filePath
     */
    public void sendAttachmentMail(String to, String subject, String content, String filePath) {
        MimeMessage message = mailSender.createMimeMessage();

        MimeMessageHelper helper;
        try {
            helper = new MimeMessageHelper(message, true);
            //true代表支持多组件,如附件,图片等
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            FileSystemResource file = new FileSystemResource(new File(filePath));
            String fileName = file.getFilename();
            helper.addAttachment(fileName, file);	//添加附件,可多次调用该方法添加多个附件
            mailSender.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }


    /**
     * 发送带图片的邮件
     */
    public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId) {
        MimeMessage message = mailSender.createMimeMessage();

        MimeMessageHelper helper;
        try {
            helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            FileSystemResource res = new FileSystemResource(new File(rscPath));
            helper.addInline(rscId, res);	//重复使用添加多个图片
            mailSender.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

}

5. 主启动类中测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class EmApplicationTests {
    @Autowired
    private EmailService emailService;
    @Autowired
    private TemplateEngine templateEngine;

    //普通文件
    @Test
    public void emailService() {
        String to ="2842245203@qq.com";
        String subject = "文本";
        String text = "需要好好学习springboot";
        emailService.sendSimpleMail(to, subject, text);
    }

    //HTML邮件
        @Test
        public void sendHtmlMail(){
        String to = "2842245203@qq.com";
        String subject = "【HTML邮件模板】标题";
        //使用HTML模板邮件定制邮件正文内容
        Context context = new Context();
        context.setVariable("username","2842245203@qq.com");
        context.setVariable("msg","shenme");
        //使用TemplateEngine设置要处理的模板页面
        String emailContent = templateEngine.process("sendMsg",context);
        //发送HTML模板邮件
        emailService.sendHtmlMail(to,subject,emailContent);
    }

    @Test
    public void sendComplexEmailTest(){
        String to = "2842245203@qq.com";
        String subject = "【复杂邮件】标题";
        //指定静态资源文件和附件路径
        String content = "可爱小猫咪";
        String filePath = "路径";
        //发送复杂邮件
        emailService.sendAttachmentMail(to,subject,content,filePath);
    }
}

测试