目录
简介:
01.Spring Boot 入门程序
02.常用注解【spring的java配置】
@Controller --控制器
传参方式
03. Spring Boot热部署
04.Spring Boot的自动配置原理以及启动分析(难点)
点进去spring-boot-starter-parent以后这依赖管理里面写和很从版本
05.Spring Boot的两种配置文件语法【重中之重】
06.@Value读取配置文件以及验证
07.Profile配置文件详解
7.1为什么要使用profiles
08.配置文件加载优先级和外部配置文件
09.自动配置原理以及 @Conditional派生注解
10. 整合logback
11.AOP开发
12.WEB静态资源访问规则
13.Thymeleaf模板的使用
14. 内嵌tomcat加载原理分析
15.启动内嵌jetty服务器【了解】
16.注册Web三大组件【重点】
17.数据源配置和自动管理【重中之中】
18.集成JdbcTemplate【熟悉】
19.集成mybatis【重点】
20.集成mybatisplus【重点】
21.集成swagger3.0【熟悉】
22.集成shiro非前后端分离【重点】
23.集成shiro前后端分离
24.普通缓存(非redis)
25.Spring Boot定时任务
26.Spring Boot邮件发送
Spring SpringMVC和SpringBoot 区别
1、Spring
Spring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,并且可以配置各种bean,和维护bean与bean之间的关系。其核心就是控制反转(IOC),和面向切面(AOP),简单的说就是一个分层的轻量级开源框架。
2、SpringMVC
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。SpringMVC是一种web层mvc框架,用于替代servlet(处理|响应请求,获取表单参数,表单校验等。SpringMVC是一个MVC的开源框架,SpringMVC=struts2+spring,springMVC就相当于是Struts2加上Spring的整合。
3、SpringBoot
Springboot是一个微服务框架,延续了spring框架的核心思想IOC和AOP,简化了应用的开发和部署。Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置。提供了一堆依赖打包,并已经按照使用习惯解决了依赖问题—>习惯大于约定
区别与总结
1.简单理解为:Spring包含了SpringMVC,而SpringBoot又包含了Spring或者说是在Spring的基础上做得一个扩展。spring mvc < spring < springboot
2、Spring Boot 对比Spring的一些优点包括:
提供嵌入式容器支持
使用命令java -jar独立运行jar
在外部容器中部署时,可以选择排除依赖关系以避免潜在的jar冲突
部署时灵活指定配置文件的选项
用于集成测试的随机端口生成
3、结论
Spring Boot只是Spring本身的扩展,使开发,测试和部署更加方便。
创建第一个springboot项目
org.springframework.boot
spring-boot-starter-web
这个spring-boot-starter-web 是web的启动器,封装了springmv所需要的jar包
启动测试
启动项目访问http://localhost:8080/index/hello
jar包启动测试,先打包
测试访问
项目结构
彩蛋
spring Boot启动的时候会有一个默认的启动图案。
在src/main/resources路径下新建一个banner.txt文件,并输入想要的内容即可
类上面的注解:
@Controller --控制器
@RestController --返回json的控制器 =Controller +ResponseBody
@Service --标记服务接口
@Respority --标记Dao接口
@Component --标记组件
@RequestMapping --请求映射 (也可在方法上)
方法上的注解:
|--GetMapping
|--PostMapping
|--DeleteMapping
|--PutMapping
|--PatchMapping
@ResponseBody --返回JSON对象
参数上的注解:
@RequestBody --入参是JSON对象
@PathVariable(value=””) --将路径上面的参数映射到入参里面
http://127.0.0.1:8080/car/user/1
@RequestParam 如果Controller里面使用map接收参数
|--如果controller不知道页面要传多少参数,那么可以使用Map
去接收。 必须加@RequestParam的注解 属性上面的注解
@Autowried --自动注入(首选按照类型) byName byType
@Resource --自动注入(首选按照名字)
http://127.0.0.1:8080/car/user?id=1
http://127.0.0.1:8080/car/user/1 加@pathvarible注解
@Configuration
作用于类上,相当于一个xml配置文件;|--application-dao.xml
@Bean 作用于方法上,相当于xml配置中的
@Qualifier注解
qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,我们修改调用代码,添加@Qualifier注解,需要注意的是@Qualifier的参数名称必须为我们之前定义@Bean注解的名称之一
@Primary 主候选的
当IOC容器里面有多个同类型的对象时,就会发生冲突,标注了该注解的就作为主候选对象
什么是热部署
spring为开发者提供了一个名为spring-boot-devtools的模块来使springboot应用支持热部署,提高开发的效率,修改代码后无需重启应用
org.springframework.boot
spring-boot-devtools
runtime
true
配置idea的启动面板
如果不配置面板,那么可直接使用ctrl+F9去刷新
4.1.pom的依赖分析
org.springframework.boot
spring-boot-starter-parent
2.6.4
org.springframework.boot
spring-boot-dependencies
2.6.4
这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
4.2.启动器 spring-boot-starter
org.springframework.boot
spring-boot-starter
2.6.4
springboot-boot-starter-xxx:就是spring-boot的场景启动器
org.springframework.boot
spring-boot-starter-web
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ,也可以自己自定义 starter;
4.3原理分析
注解功能划分
如何自动扫描(默认扫描启动类所在的包以下面的及子包)
@Import(AutoConfigurationPackages.Registrar.class)
在AutoConfigurationPackages中registerBeanDefinitions
如图:
疑问:这是springmvc中的写法,我们要这么用,必须配置相关xml
为什么引入spring-boot-starter-web之后能直接集成springmvc
回顾SSM的配置:
- 包扫描
- 注解
- 配置适配器
- Contoller的实方式
- 实现Controller接口
- 实现HttpRequestHandler
- 使用注解
- Controller
- RestController
- 总结,就是为了适配Controller的不同实现方式
- 配置映射器
- 处理用户请求地址和Controller里方法的映射关系的
- 配置视图解释器
- org.springframework.web.servlet.view.InternalResourceViewResolver
- 配置拦截器
- 拦截请求 Inteceptor
- 配置文件上传管理器
配置前端控制器
web.xml
是因为上面配置是springboot帮我们做了
如何加载自动配置类
找到@Import(AutoConfigurationImportSelector.class)
在这个类里面的getAutoConfigurationEntry()方法
springboot在启动的时候默认加载了所有的配置
进入getCandidateConfigurations()方法查看究竟
这个方法是spring的加载工厂去筛选所有引入(link)EnableAutoConfiguration的配置类
EnableAutoConfiguration是Key 返回的List
接着进入loadFactoryNames()方法
最后回到getAutoConfigurationEntry()方法里面往下执行
剔除不需要的(pom里面没有引入starter的自动配置类)
至此自动加载配置类的初探就结束了
(从加载到的配置类中选讲)如何加载前端控制器,在ssm里面,我们需要手动去创建DispatcherServlet对象,然后注入到Servlet容器中,在springboot里面,已经帮我们自动配置了
查看DispatcherServletAutoConfiguration这个自动配置类
设置servlet的名称,loadonstart 启动时加载 路径
Springboot的run方法到底执行了什么
Springboot默认提供了哪些候选的starter
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
引入依赖
org.springframework.boot
spring-boot-configuration-processor
true
创建Hero 类
properties方式:
hero.id=2
hero.name=LJH
hero.age=18
hero.birth=2012/12/31
hero.hobby=LOL,DNF,CF
hero.list=C#,Python,PS
hero.set=football,basketball,swim
hero.map.k1=v1
hero.map.k2=v2
yml方式:
hero:
id: 1
age: 18
name: 超人
birth: 2012/12/31
hobby:
- LOL
- DNF
- CF
list:
- JAVA
- JS
- C++
set:
- 足球
- 篮球
- 排球
map:
k1: v1
k2: v2
测试
配置文件取值
hero.name=${hero.age}
两种配置文件的用法说明
- 如果properties里面配置了就不会去yml里面去取值,如果没有配置就会去yml里面去取
- 两种配置文件是互补的存在
注入对象
总结说明
1,@Value只能注入普通的属性[也就是基本数据类型和String] 其它的复杂类型是不能取到值的[如果yaml配置是array:LOL,DNF]这样的配置是可以取 或者在perpeties里面配置
2,如果属性是使用驼峰命名法则不能使用属性名注入,要使用@Value("${hero.class-name}")来取值
不能使用@Value("${hero.className}")来取值
@Value和@ConfigurationProperties取值比较
注解验证
引入依赖
org.springframework.boot
spring-boot-starter-validation
常用的验证注解
@NotNull --不能为null,但可以空着不写 (name:)
@NotEmpty --不能为空,也不能为null,但可以是空格 “ ”
@NotBlank --不能为空格,也不能为null,也不能为空格
@Min 最大值
@Max 最小值
@Size(min = 1, max = 6) 长度限制
@Range(min = 1,max = 2) 范围限制
@Pattern(regexp"[0,1]{1}") 正则限制
全局异常
package com.bjsxt.exception;
import com.bjsxt.vo.ResultObj;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {BindException.class})
public ResultObj bindException(BindException e){
BindingResult result = e.getBindingResult();
return getResultObj(result);
}
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResultObj methodArgumentNotValidException(MethodArgumentNotValidException e){
BindingResult result = e.getBindingResult();
return getResultObj(result);
}
private ResultObj getResultObj(BindingResult result) {
List
在开发中,一般有两种环境
1,生产环境 [项目上线,客户在使用中,就是生产环境]
2,开发环境 [就是开发环境,自己开发测试的环境]
有时候开发环境和生产环境的配置方法是不一样的,那么如何快速的切换呢,这里就要使用profiles文件
7.2使用方法
创建application.yml
创建application-dev.yml
创建application-pro.yml
运行测试
总结
在application.yml的主配置文件中,激活哪个配置文件,就会使用该配置文件进行运行
8.1项目内部配置文件
spring boot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
其中同一目录下的properties文件的优先级大于yml文件
配置文件可以放的位置和优先级
classpath:/ --优先级4 默认放的位置
classpath:/config/ --优先级3
file:./ --优先级2 跟jar包同级别的目录
file:./config/ --优先级1
测试前两个
测试前三个
先打包
启动jar包运行
在jar包同级路面建config包,里面application.yml端口为8004
启动测试:
Win10 下关闭已运行jar包的方法,本文直接查找端口杀之
查找端口命令 netstat -aon|findstr “8081”
使用命令杀掉:taskkill /pid 8080 -f 杀掉端口
查看ConfigFileApplicationListener
8.2外部的配置文件
在D盘放一个application.yml文件 端口指定为8009
java -jar 01springboot-hello-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.yml
9.1自动配置
我们就了解到了Spring Boot在启动的时候:
- 去扫描加载了所有引入依赖的jar包下面的MATE-INF/spring.factories文件,
- 然后通过EnableAutoConfiguration为key,下面所有自动配置类的全限定类名作为value,(List
) - 经过筛选排除掉不符合要求的(pom中没有添加依赖的配置类)
- 最后把(List
:符合条件的自配配置类的全限定类名)添加到IOC容器去管理,从而实现了自动配置原理
@Conditional派生注解
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置类里面的所有内容才生效;(条件之间是并且的关系)
10.1概述
在搭建新的系统时候必不可少的是需要日志的,日志的作用就不用多说了吧,可以用来调试程序,记录程序运行的状态,最重要的是可以用来排查线上的问题。
那我们该如何在项目中使用日志呢?
SpringBoot内部集成了LogBack日志依赖,SpringBoot默认使用LogBack记录日志信息,默认根据base.xml配置内容来输出到控制台和文件之中,这是默认的配置不能达到企业级项目的要求
创建logback-spring.xml
logback
info
${CONSOLE_LOG_PATTERN}
UTF-8
${log.path}/log_debug.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
UTF-8
${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
100MB
15
debug
ACCEPT
DENY
${log.path}/log_info.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
UTF-8
${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
100MB
15
info
ACCEPT
DENY
${log.path}/log_warn.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
UTF-8
${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
100MB
15
warn
ACCEPT
DENY
${log.path}/log_error.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
UTF-8
${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
100MB
15
ERROR
ACCEPT
DENY
创建启动类测试
查看本地日志
11.1概述
aop是spring的两大功能模块之一,功能非常强大,为解耦提供了非常优秀的解决方案。SpringBoot集成aop是非常方便的,下面使用aop来拦截业务组件的方法
Aop的作用:在不修改源代码的情况下,对类里面的方法进行增强
(前置,后置,环绕,异常)
11.2使用方法
org.springframework.boot
spring-boot-starter-aop
创建Man测试
@Component
public class TestAop {
public void eat(String food){
// int a = 10/0;
System.out.println("吃"+food);
}
}
创建切面类
package com.wxz.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* TODO
*
* @author wxz
* @date 2022/3/2014:16
*/
@Component
@Aspect
public class TestAspect {
public static final String pc ="execution(* com.wxz.domain.*.*(..))";
// @Before(pc)
public void before(){
System.out.println("饭前来点水果");
}
// @After(pc)
public void after(){
System.out.println("饭后来一把");
}
@Around(pc)
public Object around(ProceedingJoinPoint point){
before();
Object[] args = point.getArgs();
String game = args[0].toString();
System.out.println(game);
//执行目标方法,获得返回值
Object proceed = null;
try {
proceed = point.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
after();
//返回出去
return proceed;
}
@AfterThrowing(value = pc, throwing = "tw")
public void afterThrowing(Throwable tw) {
System.out.println("出现异常" + tw.getMessage());
}
}
- classpath:META-INF/resources 优先级最高
- classpath:resources 其次
- classpath:static
- classpath:public
查看WebMvcAutoConfiguration里面的静态类WebMvcAutoConfigurationAdapter
关于资源管的方法addResourceHandlers
如果这四个目录下面都有相同的文件,那么访问的优先级为:
META-INF/resources>resources>static>public
Thymeleaf概述
Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:
1、Thymeleaf 在有网络和无网络的环境下皆可
运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
2、Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、改jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
3、Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能
org.springframework.boot
spring-boot-starter-thymeleaf
Spring Boot项目Thymeleaf模板页面存放位置
查看ThymeleafAutoConfiguration自动配置类,找到模板存放的位置
禁用缓存
templates目录不能直接访问,需要controller进行页面跳转
测试访问:
Thymeleaf的相关语法:
1,简单表达式
1、变量的表达式:${...} 取作用域里面的值(request,session,applicationContext)
2、选择变量表达式:*{...}
3、信息表达式:#{...} 取IOC容器里面的值
4、链接URL表达式:@{...} US.
5,实用工具对象
#dates: java.util的实用方法。对象:日期格式、组件提取等.
#calendars:类似于#日期,但对于java.util。日历对象
#numbers:格式化数字对象的实用方法。
#strings:字符串对象的实用方法:包含startsWith,将/附加等。
Thymeleaf读取Model里面的对象
创建类Hero:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hero {
private Integer id;
private String name;
private String sex;
private Integer age;
private String country;
private String phone;
private Date birth;
private Double salary;
}
@RequestMapping("helloHero")
public String helloHero(Model model){
Hero hero=new Hero(1,"后羿","男",18,"中国","110",new Date());
model.addAttribute("hero",hero);
return "showHero";
}
修改页面
Title
英雄id
英雄名字
英雄性别
英雄年龄
英雄年龄
英雄国家
英雄电话
英雄生日
英雄生日
测试访问
Thymeleaf读取Model里面的集合
@RequestMapping("helloHeroList")
public String helloHeroList(Model model){
List list=new ArrayList<>();
for (int i = 1; i <=5 ; i++) {
list.add(new Hero(i,"后羿"+i,"男",18,"中国","110",new Date()))
}
model.addAttribute("heros",list);
return "showHeroList";
}
Title
英雄id
英雄名字
英雄性别
英雄年龄
英雄年龄
英雄国家
英雄电话
英雄生日
英雄生日
测试访问
ThymeleafObjects的使用
Title
model:
request:
session:
session:
servletContext:
我们在使用springboot项目的时候并没有使用外部的tomcat,那么springboot是如何帮我们管理内置的服务器的呢?
查看启动原理
查看ServletWebServerFactoryAutoConfiguration
发现包含了3个内嵌的服务器,默认是使用了内嵌的tomcat
点击EmbeddedTomcat 进入
继续往下走查看TomcatServletWebServerFactory
点击getTomcatWebServer
点击TomcatWebServer
在springboot里面默认使用内嵌的tomcat 可以改成jetty
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-start-tomcat
org.springframework.boot
spring-boot-starter-jetty
@Configuration
public class JettyConfig {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
JettyServletWebServerFactory jettyServletWebServerFactory = new JettyServletWebServerFactory();
return jettyServletWebServerFactory;
}
}
启动查看
web的三大件有哪些
Servlet ---web.xml @WebServlet
Filter --web.xml @WebFilter
Listener --web.xml @WebListener
16.1首先查看注册组件的结构
16.2注册自己的Servlet
我们可以模仿DispatcherServlet的注册方式
创建UserServlet
package com.wxz.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("hello-userServlet");
writer.flush();
writer.close();
System.out.println("hello-UserServlet");
}
}
在配置类注册自己的servlet
@Configuration
public class WebConfig {
@Bean
public UserServlet userServlet() {
return new UserServlet();
}
@Bean
public ServletRegistrationBean userServletServletRegistrationBean(UserServlet userServlet) {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>();
servletRegistrationBean.setServlet(userServlet);
servletRegistrationBean.setUrlMappings(Arrays.asList("/user1.action", "/user2.action"));
return servletRegistrationBean;
}
}
16.3注册自己的Filter
创建MyFilter
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("前置过滤");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("后置过滤");
}
@Override
public void destroy() {
}
}
在配置类注册自己的Filter
@Bean
public MyFilter myFilter() {
return new MyFilter();
}
/**
* 注册自己的filter
*
* @param myFilter
* @return
*/
@Bean
public FilterRegistrationBean myFilterFilterRegistrationBean(MyFilter myFilter) {
FilterRegistrationBean myFilterFilterRegistrationBean = new FilterRegistrationBean<>();
myFilterFilterRegistrationBean.setFilter(myFilter);
//设置所有的请求都走过滤器
myFilterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
return myFilterFilterRegistrationBean;
}
16.4注册自己的Listener
创建MyListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContextEvent被创建");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContextEvent被销毁");
}
}
在配置类注册自己的Listener
@Bean
public MyListener myListener() {
return new MyListener();
}
@Bean
public ServletListenerRegistrationBean listenerRegistrationBean(MyListener myListener) {
ServletListenerRegistrationBean listenerRegistrationBean = new ServletListenerRegistrationBean<>();
listenerRegistrationBean.setListener(myListener);
return listenerRegistrationBean;
}
选择依赖
17.1使用DriverManagerDataSource
修改配置文件
spring:
datasource: #数据源的配置
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
type: org.springframework.jdbc.datasource.DriverManagerDataSource #spring自带的数据源
测试连接
17,2使用Druid数据源【自己配置】
数据源
数据源的监控页面
添加durid的依赖
com.alibaba
druid
1.1.21
添加MyDruidProperties配置文件类
@Data
@ConfigurationProperties(prefix = "my.druid")
public class MyDruidProperties {
private String url;
private String username;
private String password;
private String driverClassName;
//初始化数量
private Integer initialSize;
//最大活跃数
private Integer maxActive;
//最小连接数
private Integer minIdle;
//检查连接的
private String validationQuery;
private StatView statView;
/**
* 监控配置
*/
@Data
static class StatView {
//监控登录名
private String loginUsername;
//监控登录密码
private String loginPassword;
//白名单
private String allow;
//黑名单
private String deny;
//映射路径
private String[] urlMapping;
}
}
添加MyDruidAutoConfiguration自动配置类
@Data
@Configuration
@Log4j2
@EnableConfigurationProperties(MyDruidProperties.class)
@ConditionalOnClass(value = {DruidDataSource.class}) //必须有DruidDataSource的这个类
public class MyDruidAutoConfiguration {
// @Autowired 如果不想使用Autowired就必须提交一个构造方法,如下
private MyDruidProperties myDruidProperties;
public MyDruidAutoConfiguration(MyDruidProperties myDruidProperties) {
this.myDruidProperties = myDruidProperties;
}
/**
* 创建数据源对象
*
*/
@Bean(initMethod = "init",destroyMethod = "close")
public DruidDataSource druidDataSource(){
if (myDruidProperties.getUrl() == null) {
log.error("URL can not be null");
throw new RuntimeException("URL can not be null");
}
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName(myDruidProperties.getDriverClassName());
dataSource.setUrl(myDruidProperties.getUrl());
dataSource.setUsername(myDruidProperties.getUsername());
dataSource.setPassword(myDruidProperties.getPassword());
dataSource.setMaxActive(myDruidProperties.getMaxActive());
dataSource.setMinIdle(myDruidProperties.getMinIdle());
dataSource.setValidationQuery(myDruidProperties.getValidationQuery());
return dataSource;
}
/**
* 注册StatViewServlet
*/
@Bean
@ConditionalOnClass(StatViewServlet.class)
public ServletRegistrationBean statViewServletServletRegistrationBean(){
StatViewServlet statViewServlet=new StatViewServlet();
ServletRegistrationBean registrationBean=new ServletRegistrationBean<>();
registrationBean.setServlet(statViewServlet);
registrationBean.addInitParameter("loginUsername",myDruidProperties.getStatView().getLoginUsername());
registrationBean.addInitParameter("loginPassword",myDruidProperties.getStatView().getLoginPassword());
registrationBean.addInitParameter("allow",myDruidProperties.getStatView().getAllow());
registrationBean.addInitParameter("deny",myDruidProperties.getStatView().getDeny());
registrationBean.addUrlMappings(myDruidProperties.getStatView().getUrlMapping());
return registrationBean;
}
}
修改yml配置文件
my:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
initial-size: 5
max-active: 10
min-idle: 2
validation-query: select 'x'
stat-view:
login-username: admin
login-password: admin
allow:
deny:
url-mapping:
- /druid/*
- /druid2/*
测试访问
17.3使用Druid数据源【官方starter】
添加durid starter的依赖
com.alibaba
druid-spring-boot-starter
1.1.21
修改配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
# type: com.alibaba.druid.pool.DruidDataSource
druid:
max-active: 10
min-idle: 2
validation-query: select 'x'
stat-view-servlet:
login-username: admin
enabled: true #启用监控页
login-password: admin
allow:
deny:
url-pattern: /druid/*
我们之前在学spring的时候接触过JdbcTemplate,这个东西是官方的,那么在springboot里面怎么用,需要配置什么呢,答案:只要有数据源就行了
测试
因为数据源已经搞定了,所以直接测试即可
原理
找到JdbcTemplateAutoConfiguration
找到JdbcTemplateConfiguration
从上面的代码可以看出,只要有数据源,就直接创建了,所以我们可以直接使用
19.1注解方式的整合
创建项目
com.alibaba
druid-spring-boot-starter
1.1.21
创建数据库
创建User对象
package com.wxz.domain;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
* 用户编号
*/
private Integer id;
/**
* 用户姓名
*/
private String name;
/**
* 用户地址
*/
private String address;
/**
* 出生时间
*/
private Date birth;
/**
* 是否删除1删除0未删除
*/
private Integer flag;
}
创建UserMapper
package com.wxz.mapper;
import com.wxz.domain.User;
import org.apache.ibatis.annotations.*;
public interface UserMapper {
@Delete("delete from sys_user where id= #{id}")
int deleteByPrimaryKey(Integer id);
@Insert("insert into sys_user(id,name,address,birth,flag) values(#{user.id},#{user.name},#{user.address} ,#{user.birth},#{user.flag})")
int insert(@Param("user") User record);
@Select("select * from sys_user where id=#{id} ")
User selectByPrimaryKey(Integer id);
@Update("update sys_user set name=#{user.name},address=#{user.address} ,birth=#{user.birth} ,flag=#{user.flag} where id=#{user.id} ")
int updateByPrimaryKey(@Param("user") User user);
}
修改yml配置文件
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
# type: com.alibaba.druid.pool.DruidDataSource
druid:
max-active: 10
min-idle: 2
validation-query: select 'x'
stat-view-servlet:
login-username: admin
enabled: true #启用监控页
login-password: admin
allow:
deny:
url-pattern: /druid/*
#配置mybatis
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #输出sql
启动了上扫描mapper所在的包
测试查询
19.2配置文件方式整合(推荐)
生成UserMapper接口
public interface UserMapper {
int deleteByPrimaryKey(Integer id);
int insert(User record);
User selectByPrimaryKey(Integer id);
int updateByPrimaryKey(User record);
List selectAll();
}
在resources/mapper生成UserMapper.xml
id, `name`, address, birth, flag
delete from sys_user
where id = #{id,jdbcType=INTEGER}
insert into sys_user (`name`, address, birth,
flag)
values (#{name,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{birth,jdbcType=TIMESTAMP},
#{flag,jdbcType=INTEGER})
update sys_user
set `name` = #{name,jdbcType=VARCHAR},
address = #{address,jdbcType=VARCHAR},
birth = #{birth,jdbcType=TIMESTAMP},
flag = #{flag,jdbcType=INTEGER}
where id = #{id,jdbcType=INTEGER}
修改yml配置文件
#配置mybatis
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #输出sql
mapper-locations:
- classpath:mapper/*Mapper.xml
启动类添加mapper扫描
19.3配置PageHelper插件分页
依赖pageHelper的starter
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.13
19.4事务管理
和spring里面一样的,但是在boot里面只要在XXXXServiceImpl上Transactionl
创建项目引入依赖
com.baomidou
mybatis-plus-boot-starter
3.4.0
com.alibaba
druid-spring-boot-starter
1.1.21
配置yml
server:
port: 8080
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
initial-size: 5
max-active: 10
min-idle: 2
validation-query: select 'x'
stat-view-servlet:
login-username: admin
login-password: 123456
enabled: true #启用监控页面
url-pattern: /druid/*
#配置mybatis-plus
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations:
- classpath:mapper/*.xml
生成User UserMapper UserMapper.xml
修改启动类
测试
package com.wxz.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
@SpringBootTest
class ApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void queryForPage() {
Integer pageNum=1;
Integer pageSize=10;
Page page=new Page<>(pageNum,pageSize);
this.userMapper.selectPage(page,null);
List records = page.getRecords();
long total = page.getTotal();
System.out.println("总条数:"+total);
for (User record : records) {
System.out.println(record);
}
}
}
使用步骤
创建项目加入依赖
io.springfox
springfox-boot-starter
3.0.0
创建SwaggerProperties信息配置类
@Data
@ConfigurationProperties(prefix = "swagger2")
public class SwaggerProperties {
//作者姓名
private String name;
//作者主页链接
private String url;
//作者邮箱
private String email;
//版本号
private String version;
//分组名称
private String groupName;
//文档标题
private String title;
//文档描述
private String description;
//组织地址
private String termsOfServiceUrl;
}
创建SwaggerAutoConfiguration自动配置类
package com.wxz.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
@Configuration
@EnableConfigurationProperties(value = {SwaggerProperties.class})
@EnableOpenApi //启用swagger
public class SwaggerAutoConfiguration {
private SwaggerProperties swaggerProperties;
public SwaggerAutoConfiguration(SwaggerProperties swaggerProperties) {
this.swaggerProperties = swaggerProperties;
}
/**
* 创建文档
*
* @return
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.groupName(swaggerProperties.getGroupName())
.select()
//要扫描的包
.apis(RequestHandlerSelectors.basePackage("com.wxz"))
// .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
// .apis(RequestHandlerSelectors.withMethodAnnotation(GetMapping.class))
.build();
}
/**
* 自定义文档信息
*
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfo(swaggerProperties.getTitle(), //文档标题
swaggerProperties.getDescription(), //文档描述
swaggerProperties.getVersion(), //版本号
swaggerProperties.getTermsOfServiceUrl(), //组织信息
new Contact(swaggerProperties.getName(), //个人信息
swaggerProperties.getUrl(),
swaggerProperties.getEmail()
),
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
修改yml文件
swagger2:
name: "小白"
description: "测试的接口"
email: [email protected]
title: "后台"
version: 1.0
group-name: leige
terms-of-service-url: https://gitee.com/smiledouble
url: https://gitee.com/smiledouble
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mvc:
format:
date-time: yyyy-MM-dd HH:mm:ss
创建Hero类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "Hero", description = "英雄对象")
public class Hero {
@ApiModelProperty(value = "英雄的id")
private Integer id;
@ApiModelProperty(value = "英雄的名称")
private String name;
@ApiModelProperty(value = "英雄的地址")
private String address;
@ApiModelProperty(value = "英雄的生日")
private Date birth;
@ApiModelProperty(value = "英雄的爱好")
private List hobby;
@ApiModelProperty(value = "英雄的map")
private Map map;
}
创建Controller
@Api(tags = "英雄的管理接口") //标记一个controller信息
@RestController
public class TestController {
/**
* 获取一个英雄
*
* @param id 使用restful风格,讲参数拼接在url上
* @return
*/
@GetMapping("getOneHero/{id}")
@ApiOperation(value = "获取一个英雄", notes = "根据id获取一个英雄对象")
@ApiImplicitParam(name = "id", value = "英雄编号(必填)", required = true, dataType = "Integer", paramType = "path")
public Hero getOne(@PathVariable(value = "id") Integer id) {
Map map = new HashMap<>(4);
map.put("q", "哈萨克");
return new Hero(id, "亚索", "峡谷", new Date(), Arrays.asList("吹风,笛子"), map);
}
/**
* 添加英雄
*
* @param hero
* @return
*/
@PostMapping("addHero")
@ApiOperation(value = "创建英雄", notes = "根据Hero创建英雄对象")
@ApiImplicitParam(name = "hero", value = "英雄对象", required = true, dataTypeClass = Hero.class, paramType = "body")
public Map addHero(@RequestBody Hero hero) {
System.out.println(hero);
Map res=new HashMap<>();
res.put("code",200);
res.put("msg","添加成功");
return res;
}
/**
* 删除一个英雄
*
* @param id
* @return
*/
@DeleteMapping("deleteHero")
@ApiOperation(value = "删除一个英雄", notes = "根据删除一个英雄对象")
@ApiImplicitParam(name = "id", value = "英雄编号(必填)", required = true, dataType = "Integer", paramType = "query")
public Map deleteHero(@RequestParam("id") Integer id) {
System.out.println(id);
Map res=new HashMap<>();
res.put("code",200);
res.put("msg","删除成功");
return res;
}
}
测试访问文档页面
启动项目访问 http://localhost:8080/swagger-ui/index.html
测试接口
补充注解说明
https://gumutianqi1.gitbooks.io/specification-doc/content/tools-doc/spring-boot-swagger2-guide.html
更换页面ui
com.github.xiaoymin
swagger-bootstrap-ui
1.9.6
注解总结
model(实体类) 的注解
|--@ApiModel(value = "Hero", description = "英雄对象") 标记这个类的描述
|--@ApiModelProperty(value = "英雄的id") 标记类的属性的作用
controller上的注解
|--@Api(tags = "英雄测试类接口",description = "描述")
|--作用在类上,标记这个contorller的作用
|--@ApiOperation(value = "根据ID获取英雄对象",notes = "根据id获取一个英雄对象") //当前接口的描述
|--作用在方法上,标记这个方法的作用
|--@ApiImplicitParam(name = "hero", value = "英雄对象", required = true, dataTypeClass = Hero.class, paramType = "body")
|--作用在方法上,标记这个方法需要哪些参数 及需要的数据格式
配置类
|--@EnableOpenApi //启用swagger
之前SSM的配置
导入sql建表
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50724
Source Host : localhost:3306
Source Schema : shiro
Target Server Type : MySQL
Target Server Version : 50724
File Encoding : 65001
Date: 15/12/2020 14:53:39
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`perid` int(11) NOT NULL AUTO_INCREMENT,
`pername` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`percode` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`perid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '用户查询', 'user:query');
INSERT INTO `permission` VALUES (2, '用户添加', 'user:add');
INSERT INTO `permission` VALUES (3, '用户修改', 'user:update');
INSERT INTO `permission` VALUES (4, '用户删除', 'user:delete');
INSERT INTO `permission` VALUES (5, '导出用户', 'user:export');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`roleid` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`roleid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '超级管理员');
INSERT INTO `role` VALUES (2, 'CEO');
INSERT INTO `role` VALUES (3, '保安');
-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
`perid` int(255) NOT NULL,
`roleid` int(11) NOT NULL,
PRIMARY KEY (`perid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (2, 2);
INSERT INTO `role_permission` VALUES (3, 1);
INSERT INTO `role_permission` VALUES (3, 2);
INSERT INTO `role_permission` VALUES (4, 1);
INSERT INTO `role_permission` VALUES (5, 3);
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`userpwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zhangsan', '639ffb0cbcca39d4fff8348844b1974e', '男', '武汉');
INSERT INTO `user` VALUES (2, 'lisi', '0d303fa8e2e2ca98555f23a731a58dd9', '女', '北京');
INSERT INTO `user` VALUES (3, 'wangwu', '473c41db9af5cc0d90e7adfd2b6d9180', '女', '成都');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`userid` int(11) NOT NULL,
`roleid` int(11) NOT NULL,
PRIMARY KEY (`userid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1);
INSERT INTO `user_role` VALUES (2, 2);
INSERT INTO `user_role` VALUES (3, 3);
SET FOREIGN_KEY_CHECKS = 1;
分析表的数据
zhangsan/123456 超级管理员 1234号权限user:query user:add user:update user:delete
lisi/123456 CEO 123号权限user:query user:add user:update
wangwu/123456 保安 15号权限 user:query user:export
创建项目选择依赖
com.alibaba
druid-spring-boot-starter
1.1.21
org.apache.shiro
shiro-spring
1.5.3
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
修改主启动类
创建yml
#配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/0817-shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
druid:
# filters: log4j,stat
max-active: 20
min-idle: 5
validation-query: select x
initial-size: 3
max-wait: 5000
stat-view-servlet:
login-username: root
login-password: 123456
allow:
deny:
url-pattern: /druid/*
enabled: true #启用数据源监控
#mybatis的配置
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
type-aliases-package: com.wxz.domain #配置字别名
#shiro的配置
shiro:
hash-iterations: 2
hash-algorithm-name: md5
login-url: /index.html
unauthorized-url: /unauthorized.html
log-out-url: /login/logout
anon-url: #这些地址不用认证
- /index.html*
- /login.html*
- /login/toLogin*
- /login/doLogin*
authc-url: #下面的需要认证
- /**
生成User
package com.wxz.domain;
public class User {
/*
*用户id
*/
private Integer userid;
/*
*用户名
*/
private String username;
/*
*密码
*/
private String userpwd;
/*
*性别
*/
private String sex;
/*
*地址
*/
private String address;
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpwd() {
return userpwd;
}
public void setUserpwd(String userpwd) {
this.userpwd = userpwd;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
UserMapper
package com.wxz.mapper;
import com.wxz.domain.User;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
User queryUserByUserName(@Param("username") String username);
}
UserMapper.xml
userid, username, userpwd, sex, address
UserService
package com.wxz.service;
import com.wxz.domain.User;
public interface UserService{
/**
* 据用户名去数据库查询用户
* @param username
* @return
*/
User queryUserByUserName(String username);
}
UserServiceImpl
package com.wxz.service.impl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import com.wxz.domain.User;
import com.wxz.mapper.UserMapper;
import com.wxz.service.UserService;
@Service
public class UserServiceImpl implements UserService{
@Resource
private UserMapper userMapper;
@Override
public User queryUserByUserName(String username) {
return this.userMapper.queryUserByUserName(username);
}
}
生成Role
package com.wxz.domain;
public class Role {
/*
*角色id
*/
private Integer roleid;
/*
*角色名
*/
private String rolename;
public Integer getRoleid() {
return roleid;
}
public void setRoleid(Integer roleid) {
this.roleid = roleid;
}
public String getRolename() {
return rolename;
}
public void setRolename(String rolename) {
this.rolename = rolename;
}
}
RoleMapper
package com.wxz.mapper;
import com.wxz.domain.Role;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface RoleMapper {
List queryRolesByUserId(@Param("userid") Integer userid);
}
RoleMapper.xml
roleid, rolename
RoleService
package com.wxz.service;
import com.wxz.domain.Role;
import java.util.List;
public interface RoleService{
/**
* 根据用户ID查询用户拥有的角色
* @param userid
* @return
*/
List queryRolesByUserId(Integer userid);
}
RoleServiceImpl
package com.wxz.service.impl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import com.wxz.mapper.RoleMapper;
import com.wxz.domain.Role;
import com.wxz.service.RoleService;
import java.util.ArrayList;
import java.util.List;
@Service
public class RoleServiceImpl implements RoleService{
@Resource
private RoleMapper roleMapper;
@Override
public List queryRolesByUserId(Integer userid) {
List roleList=this.roleMapper.queryRolesByUserId(userid);
List roles=new ArrayList<>();
for (Role role : roleList) {
roles.add(role.getRolename());
}
return roles;
}
}
生成Permission
package com.wxz.domain;
public class Permission {
/*
*权限id
*/
private Integer perid;
/*
*权限名
*/
private String pername;
private String percode;
public Integer getPerid() {
return perid;
}
public void setPerid(Integer perid) {
this.perid = perid;
}
public String getPername() {
return pername;
}
public void setPername(String pername) {
this.pername = pername;
}
public String getPercode() {
return percode;
}
public void setPercode(String percode) {
this.percode = percode;
}
}
PermissionMapper
package com.wxz.mapper;
import com.wxz.domain.Permission;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface PermissionMapper {
List queryPermissionsByUserId(@Param("userid") Integer userid);
}
PermissionMapper.xml
perid, pername, percode
PermissionService
package com.wxz.service;
import java.util.List;
public interface PermissionService{
/**
* 根据用户ID查询用户拥有的权限
* @param userid
* @return
*/
List queryPermissionsByUserId(Integer userid);
}
PermssionServiceImpl
package com.wxz.service.impl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import com.wxz.mapper.PermissionMapper;
import com.wxz.domain.Permission;
import com.wxz.service.PermissionService;
import java.util.ArrayList;
import java.util.List;
@Service
public class PermissionServiceImpl implements PermissionService{
@Resource
private PermissionMapper permissionMapper;
@Override
public List queryPermissionsByUserId(Integer userid) {
List permissionList=this.permissionMapper.queryPermissionsByUserId(userid);
List permissions=new ArrayList<>();
for (Permission permission : permissionList) {
permissions.add(permission.getPercode());
}
return permissions;
}
}
创建ActiveUser
package com.wxz.common;
import com.wxz.domain.User;
import com.wxz.domain.User;
import java.util.List;
public class ActiveUser {
private User user;
private List roles;
private List permissions;
public ActiveUser() {
this.user = user;
this.roles = roles;
this.permissions = permissions;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
public List getPermissions() {
return permissions;
}
public void setPermissions(List permissions) {
this.permissions = permissions;
}
}
创建UserRealm
package com.wxz.realm;
import com.wxz.common.ActiveUser;
import com.wxz.domain.User;
import com.wxz.service.PermissionService;
import com.wxz.service.RoleService;
import com.wxz.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PermissionService permissionService;
/**
* 做认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//得到用户名
String username = token.getPrincipal().toString();
//根据用户名去数据库查询用户
User user=this.userService.queryUserByUserName(username);
if(null!=user){
ActiveUser activeUser = new ActiveUser();
activeUser.setUser(user);
//根据用户ID查询用户拥有的角色
List roles=this.roleService.queryRolesByUserId(user.getUserid());
//根据用户ID查询用户拥有的权限
List permissions=this.permissionService.queryPermissionsByUserId(user.getUserid());
activeUser.setRoles(roles);
activeUser.setPermissions(permissions);
//进行密码匹配
ByteSource salt=ByteSource.Util.bytes(user.getUsername()+user.getAddress());
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser,user.getUserpwd(),salt,this.getName());
return info;
}else{
//用户名不存在
return null;
}
}
/**
* 作授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//取出activeUser
ActiveUser activeUser= (ActiveUser) principals.getPrimaryPrincipal();
List roles = activeUser.getRoles();
List permissions = activeUser.getPermissions();
if(null!=roles&&!roles.isEmpty()){ //一定要判断,如果roles为空shiro会出异常
info.addRoles(roles);
}
if(permissions!=null&&!permissions.isEmpty()){
info.addStringPermissions(permissions);
}
return info;
}
}
创建ShiroProperties配置信息类
package com.wxz.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties("shiro")
public class ShiroProperties {
//散列次数
private Integer hashIterations=2;
//加密方式
private String hashAlgorithmName="MD5";
//登录
private String loginUrl;
//未授权证
private String unauthorizedUrl;
//登出
private String logOutUrl;
//放行的请求
private String[] anonUrl;
//需要认证的请求
private String[] authcUrl;
}
创建ShiroAutoConfiguration配置类
package com.wxz.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.wxz.realm.UserRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableConfigurationProperties(value = ShiroProperties.class)
public class ShiroAutoConfiguration {
private static final String SHIRO_FILTER = "shiroFilter";
private ShiroProperties properties;
public ShiroAutoConfiguration(ShiroProperties properties) {
this.properties = properties;
}
/**
* 创建凭证匹配器
* @return
*/
@Bean
public CredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
//设置加密算法
credentialsMatcher.setHashAlgorithmName(properties.getHashAlgorithmName());
//设置散列次数
credentialsMatcher.setHashIterations(properties.getHashIterations());
return credentialsMatcher;
}
/**
* 配置自定义的realm
* @param credentialsMatcher
* @return
*/
@Bean
public UserRealm userRealm(CredentialsMatcher credentialsMatcher){
UserRealm userRealm=new UserRealm();
//注入一个凭证匹配器
userRealm.setCredentialsMatcher(credentialsMatcher);
return userRealm;
}
/**
* 配置shiro的核心管理器
*/
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(UserRealm userRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 配置过滤器链
*/
@Bean(SHIRO_FILTER)
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactoryBean=new ShiroFilterFactoryBean();
//注入安全管理器
filterFactoryBean.setSecurityManager(securityManager);
//设置登陆页面
filterFactoryBean.setLoginUrl(properties.getLoginUrl());
//设置未授权的页面
filterFactoryBean.setUnauthorizedUrl(properties.getUnauthorizedUrl());
//注入过滤器链
Map map=new HashMap<>();
String[] anonUrl = properties.getAnonUrl();//不用认证
String[] authcUrl = properties.getAuthcUrl();//需要认证之后才能访问的
//注入不用认证的路径
if(null!=anonUrl&&anonUrl.length>0){
for (String anon : anonUrl) {
map.put(anon,"anon");
}
}
//注入要认证的路径
if(null!=authcUrl&&authcUrl.length>0){
for (String authc : authcUrl) {
map.put(authc,"authc");
}
}
filterFactoryBean.setFilterChainDefinitionMap(map);
return filterFactoryBean;
}
/**
* 替换web.xml的配置 注册过滤器
*/
@Bean
public FilterRegistrationBean delegatingFilterProxyFilterRegistrationBean(){
DelegatingFilterProxy filterProxy=new DelegatingFilterProxy();
FilterRegistrationBean filterProxyFilterRegistrationBean=new FilterRegistrationBean<>();
filterProxyFilterRegistrationBean.setFilter(filterProxy);
filterProxyFilterRegistrationBean.addInitParameter("targetBeanName",SHIRO_FILTER);
filterProxyFilterRegistrationBean.addInitParameter("targetFilterLifecycle","true");
//设置拦击的servlet 就是前端控制器
filterProxyFilterRegistrationBean.setServletNames(Arrays.asList(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
return filterProxyFilterRegistrationBean;
}
/**
* 这里是为了能在html页面引用shiro标签,不然不会走授权方法
*/
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
创建LoginController
package com.wxz.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("login")
public class LoginController {
/**
* 跳转到登陆页面
*/
@GetMapping("toLogin")
public String toLogin(){
return "login";
}
/**
* 做登陆
*/
@PostMapping("doLogin")
public String doLogin(String username, String password, Model model){
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try{
Subject subject = SecurityUtils.getSubject();
subject.login(token);
return "list";
}catch (UnknownAccountException e){
model.addAttribute("error","用户名不存在");
}catch (IncorrectCredentialsException e){
model.addAttribute("error","密码不正确");
}catch (Exception e){
model.addAttribute("error","登陆失败,原因:"+ e.getMessage());
}
return "login";
}
}
创建static/index.html
Title
创建template/login.html
用户登陆
用户登陆
创建template/list.html
用户管理
用户查询
用户添加
用户修改
用户删除
用户导出
启动项目测试:
zhangsan/123456 超级管理员 1234号权限user:query user:add user:update user:delete
lisi/123456 CEO 123号权限user:query user:add user:update
wangwu/123456 保安 15号权限 user:query user:export
zhangsan登录:
lisi登录
wangwu登录:
复制上一个项目
创建ResultObj
情况说明:
创建全局异常监控
package com.wxz.exception;
import com.wxz.commons.ResultObj;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice //如果出在异常,直接以json串的形式返回
public class GlobalExceptionHandler {
/**
* 捕获未授权的全局异常
*/
@ExceptionHandler(value = {UnauthorizedException.class})
public ResultObj unauthorizedExp(){
return new ResultObj(302,"您没有访问权限","");
}
}
创建LoginFormAuthenticationFilter 处理未登陆重定向的问题
package com.wxz.config;
import com.alibaba.fastjson.JSON;
import com.wxz.commons.ResultObj;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class LoginFormAuthenticationFilter extends FormAuthenticationFilter {
/**
* @param servletRequest
* @param servletResponse
* @return 返回值 如果为false就代表拦截
* 没有登陆才会走这个方法
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest request= (HttpServletRequest) servletRequest;
ResultObj resultObj=new ResultObj(302,"您没有登陆","");
String json= JSON.toJSONString(resultObj);
servletResponse.setCharacterEncoding("UTF-8");
servletResponse.setContentType("application/json");
servletResponse.getWriter().write(json);
return false;
}
}
加入fastjson依赖
com.alibaba
fastjson
1.2.56
修改ShiroAutoConfiguration
/**
* shiro的filter
*/
@Bean(SHIRO_FILTER)
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactoryBean=new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());
filterFactoryBean.setUnauthorizedUrl(shiroProperties.getUnauthorizedUrl());
//覆盖之前的authc过滤器
Map filters=new HashMap<>();
filters.put("authc",new LoginFormAuthenticationFilter());
filterFactoryBean.setFilters(filters);
String[] anonUrl = shiroProperties.getAnonUrl();//不用认证
String[] authcUrl = shiroProperties.getAuthcUrl();//需要认证之后才能访问的
Map map=new HashMap<>();
if(anonUrl!=null&&anonUrl.length>0){
for (String url : anonUrl) {
map.put(url,"anon");
}
}
if(authcUrl!=null&&authcUrl.length>0){
for (String url : authcUrl) {
map.put(url,"authc");
}
}
map.put(shiroProperties.getLogOutUrl(), "logout");
filterFactoryBean.setFilterChainDefinitionMap(map);
return filterFactoryBean;
}
/**
* 替换web.xml的配置 注册过滤器
*/
@Bean
public FilterRegistrationBean delegatingFilterProxyFilterRegistrationBean(){
DelegatingFilterProxy filterProxy=new DelegatingFilterProxy();
FilterRegistrationBean filterProxyFilterRegistrationBean=new FilterRegistrationBean<>();
filterProxyFilterRegistrationBean.setFilter(filterProxy);
filterProxyFilterRegistrationBean.addInitParameter("targetBeanName",SHIRO_FILTER);
filterProxyFilterRegistrationBean.addInitParameter("targetFilterLifecycle","true");
//设置拦击的servlet 就是前端控制器
filterProxyFilterRegistrationBean.setServletNames(Arrays.asList(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
return filterProxyFilterRegistrationBean;
}
/*加入注解的使用,不加入这个注解不生效--开始*/
/**
* @param securityManager
*
*
*
*
*
*
*
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/*加入注解的使用,不加入这个注解不生效--结束*/
修改LoginController
package com.wxz.controller;
import com.wxz.commons.ActiverUser;
import com.wxz.commons.ResultObj;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("login")
public class LoginController {
/**
* 登陆
*/
@GetMapping("doLogin")
public ResultObj doLogin(String username, String password, Model model){
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
String msg="";
try{
Subject subject = SecurityUtils.getSubject();
subject.login(token);
return new ResultObj(200,"登陆成功",username);
}catch (UnknownAccountException e){
model.addAttribute("error","用户名不存在");
msg="用户名不存在";
}catch (IncorrectCredentialsException e){
model.addAttribute("error","密码不正确");
msg="密码不正确";
}catch (Exception e){
model.addAttribute("error","登陆失败,原因:"+ e.getMessage());
msg="登陆失败,原因:"+ e.getMessage();
}
return new ResultObj(401,msg,"");
}
}
修改UserController
package com.wxz.controller;
import com.wxz.commons.ResultObj;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping("query")
@RequiresPermissions(value = {"user:query"})
public Object query(){
return new ResultObj(200,"查询成功","");
}
@GetMapping("add")
@RequiresPermissions(value = {"user:add"})
public Object add(){
return new ResultObj(200,"添加成功","");
}
@GetMapping("update")
@RequiresPermissions(value = {"user:update"})
public Object update(){
return new ResultObj(200,"更新成功","");
}
@GetMapping("delete")
@RequiresPermissions(value = {"user:delete"})
public Object delete(){
return new ResultObj(200,"删除成功","");
}
@GetMapping("export")
@RequiresPermissions(value = {"user:export"})
public Object export(){
return new ResultObj(200,"导出成功","");
}
}
测试
http://127.0.0.1:8080/login/doLogin?username=lisi&password=123456
http://127.0.0.1:8080/user/query
http://127.0.0.1:8080/user/export
在SpringBoot中可以使用注解式开发缓存,默认没有开启缓存中间件,那么使用的就是存储在Map中的原理,但是我们还可以配置自己的缓存中间件,比如redis
com.alibaba
druid-spring-boot-starter
1.1.21
org.springframework.boot
spring-boot-starter-cache
创建yaml
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
druid:
max-active: 10
min-idle: 2
validation-query: select 'x'
stat-view-servlet:
login-username: admin
enabled: true #启用监控页
login-password: admin
allow:
deny:
url-pattern: /druid/*
#配置mybatis
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #输出sql
mapper-locations:
- classpath:mapper/*Mapper.xml
开启缓存
创建UserController
package com.wxz.controller;
import com.wxz.domain.User;
import com.wxz.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
/**
* 查询ID查询一个用户
*/
@GetMapping("queryUserById")
public User queryUserById(Integer id){
return this.userService.queryUserById(id);
}
/**
* 添加一个用户
*/
@GetMapping("addUser")
public User addUser(User user){
return this.userService.addUser(user);
}
/**
* 修改一个用户
*/
@GetMapping("updateUser")
public User updateUser(User user){
return this.userService.updateUser(user);
}
/**
* 删除一个用户
*/
@GetMapping("deleteUserById")
public String deleteUserById(Integer id){
int i=this.userService.deleteUserById(id);
return i>0?"删除成功":"删除失败";
}
}
创建UserService
package com.wxz.service;
import com.wxz.domain.User;
public interface UserService {
User queryUserById(Integer id);
User addUser(User user);
User updateUser(User user);
Integer deleteUserById(Integer id);
}
创建UserServiceImpl
package com.wxz.service.impl;
import com.wxz.domain.User;
import com.wxz.mapper.UserMapper;
import com.wxz.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Cacheable(value = "user",key = "#id",unless = "#result==null",sync = true)
@Override
public User queryUserById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
@Override
@CachePut(value = "user",key = "#user.id")
public User addUser(User user) {
userMapper.insertSelective(user);
return user;
}
@Override
@CachePut(value = "user",key = "#user.id")
public User updateUser(User user) {
userMapper.updateByPrimaryKeySelective(user);
return this.userMapper.selectByPrimaryKey(user.getId());
}
@Override
@CacheEvict(value = "user",key = "#id")
public Integer deleteUserById(Integer id) {
return this.userMapper.deleteByPrimaryKey(id);
}
}
修改启动类
相关注解
@Scheduled()
@PostConstruct 这个注解不是定时任务的
当IOC容器里面的所有对象全部初始化完成之后会调用的方法
- sprignBoot定时任务是与quartz整合,不需要添加任何的依赖
- 在springBoot的启动类上添加`@EnableScheduling`注解开启定时调度
- 在需要定时调度的方法上添加`@Scheduled`这个注解即可,其中可以指定**cron表达式和其他的定时方式
开启定时任务
执行定时任务
package com.wxz.task;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MyTask {
// @Scheduled(fixedDelay = 2000) //固定延时2秒执行一次
// @Scheduled(fixedRate=2000) //fixedRate 每过多少秒执行一次
// @Scheduled(initialDelay = 2000, fixedRate = 5000) //第一次延迟两秒执行,后面按照fixedRate的规则执行
@Scheduled(cron = "0 30 0 1/1 * ? *") //每天晚上12点半执行一次https://www.matools.com/cron/
public void task1(){
System.out.println("定时任务执行了。。。。。。");
}
}
https://cron.qqe2.com/
SpringBoot实现邮件功能是非常的方便快捷的,因为SpringBoot默认有starter实现了Mail。
发送邮件应该是网站的必备功能之一,什么注册验证,忘记密码或者是给用户发送营销信息。
最早期的时候我们会使用JavaMail相关api来写发送邮件的相关代码,后来spring退出了JavaMailSender更加简化了邮件发送的过程,在之后springboot对此进行了封装就有了现在的spring-boot-starter-mail。
准备工作
先去qq邮箱设置smtp开启,并获得授权码
邮箱->设置->账户->POP3/SMTP服务:开启服务后会获得权码、
org.springframework.boot
spring-boot-starter-mail
编写测试类:
package com.wxz;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
@SpringBootTest
class SpringbootHelloApplicationTests {
@Autowired
private JavaMailSender javaMailSender;
@Test
void contextLoads() {
System.out.println(javaMailSender);
}
@Test
void sendSimpleMail(){
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
//设置发送人
simpleMailMessage.setFrom("[email protected]");
//设置接收人
simpleMailMessage.setTo("[email protected]");
//设置主题
simpleMailMessage.setSubject("这是一个测试开发的邮件发送");
//设置内容
simpleMailMessage.setText("收到后请认真查看 http://www.leige.plus:8088?id=123456");
javaMailSender.send(simpleMailMessage);
}
/**
* 发送一个带图片,带附件的邮件
*/
@Test
void sendComplexMail() throws MessagingException {
//创建mimeMessage
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
//创建一个MimeMessageHelper,来组装参数 true表示开启附件,utf-8表示编码
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
//设置发送人
mimeMessageHelper.setFrom("[email protected]");
//设置接收人
String[] to ={"[email protected]"};
mimeMessageHelper.setTo(to);
//设置主题
mimeMessageHelper.setSubject("这是一个复杂的邮件,带html解析和附件的哦");
//拼接内容参数
StringBuilder sb = new StringBuilder();
sb.append(" springboot 测试邮件发送复杂格式o
");
sb.append("哈哈哈
");
sb.append("居中
");
sb.append(" "); //如果要插入图片src='cid:picture'
//设置内容,可以被html解析
mimeMessageHelper.setText(sb.toString(), true);
//添加内容图片
mimeMessageHelper.addInline("picture", new File("D:\\img\\bg_car.jpg"));
//添加附件
mimeMessageHelper.addAttachment("美女大图.zip", new File("D:\\img\\dog.jpg"));
javaMailSender.send(mimeMessage);
}
}