Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
在resources下创建banner.txt文件,将要替换的内容复制进去即可
_
\`*-.
) _`-.
. : `. .
: _ ' \
; *` _. `*-._
`-.-' `-.
; ` `.
:. . \
. \ . : .-' .
' `+.; ; ' :
: ' | ; ;-.
; ' : :`-: _.`* ;
.*' / .*' ; .*`- +' `*'
`*-* `*-* `*-*'
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmkYqmZy-1659458588010)(F:\Paint\SpringBoot自动装配原理.png)]
Springboot的application.properties中配置
debug=true:(输出我们已经配置的组件)
Positive matches:(自动配置类启用的:正匹配)
Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)
Unconditional classes: (没有条件的类)
server:
prot: 8080
person:
name: luffy${random.uuid}
age: ${random.int}
happy: false
birth: 2020/12/2
# hello: haha
maps: {k1: v1,k2: v2}
lists:
- code
- music
- girl
dog:
firstName: ${person.hello:hehe}小黑
age: 3
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
yml中的first-name = bean中的firstName
@Validated
public class person{
// JSR303常用校验
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
}
// 空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
// Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
// 长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) string is between min and max included.
// 日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
优先级:
server:
port: 8080
# 选择使用哪些配置
spring:
profiles:
active: dev # 激活dev配置
---
server:
port: 8081
spring:
profiles: test
---
server:
port: 8082
spring:
profiles: dev
源码
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
// 第一种方式 webjars
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
// 第二种方式
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
webjars方法(不推荐)
推荐方法
/**:在http://localhost:8080/访问的静态资源会获取以下路径的静态资源
优先级排序:
在application.properties自己配置静态资源路径,那么系统规定的路径则不生效
spring.mvc.static-path-pattern=/hello/,classpath:/luffy/
在对应的静态资源路径下创建index.html即可
源码
private Resource getIndexHtml(String location) {
return this.getIndexHtml(this.resourceLoader.getResource(location));
}
private Resource getIndexHtml(Resource location) {
try {
Resource resource = location.createRelative("index.html");
if (resource.exists() && resource.getURL() != null) {
return resource;
}
} catch (Exception var3) {
}
return null;
}
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = "classpath:";
Resource getResource(String location);
@Nullable
ClassLoader getClassLoader();
}
新版本代码被更改
以下是旧版本的方法
具体看源码,这里不细说
thymeleaf模板引擎用的界面都放在templates下
源码
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
// 前缀
public static final String DEFAULT_PREFIX = "classpath:/templates/";
// 后缀
public static final String DEFAULT_SUFFIX = ".html";
...
}
使用步骤
引入依赖
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-java8timeartifactId>
dependency>
Controller层
@RequestMapping("/test")
public String test(Model model){
model.addAttribute("msg","hello,springboot!");
return "test";
}
html界面
<div th:text="${msg}">div>
转义语法和遍历操作
Controller层
@Controller
public class TestController {
@RequestMapping("/test")
public String test(Model model){
model.addAttribute("msg1","hello,springboot!
");
model.addAttribute("msg2","hello,springboot!
");
model.addAttribute("users", Arrays.asList("路飞","海贼王"));
return "test";
}
}
html界面
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div th:text="${msg1}">div>
<div th:utext="${msg2}">div>
<h1 th:each="users:${users}" th:text="${users}">h1>
body>
html>
常用语法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sB9Ro99p-1659458588012)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220622101953141.png)]
Sprint-boot一定要学会看源码,不会看源码一定学不好
在Config文件夹下创建一个个Config类,实现implements WebMvcConfigurer,重写一些方法,达到MVC的扩展
不能再使用@EnableWebMvc,因为这个注解里最终也实现了相同的类,继续使用会使他们全部失效
/**
* 扩展SpringMVC:实现WebMvcConfigurer接口,重写某些方法让我们实现一些新的配置
* 注意:不能再使用@EnableWebMvc,这个方法会让扩展方法失效
*/
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
/**
* 视图跳转 访问/ssl,跳转到config.html
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ssl").setViewName("config");
}
}
pojo
@Data
//有参构造
@AllArgsConstructor
//无参构造
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
}
@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;//0女1男
private Department department;
private Date birth;
public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
this.birth = new Date();
}
}
dao
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
@Autowired
private DepartmentDao departmentDao;
static {
employees = new HashMap<Integer,Employee>();//创建一个员工表
employees.put(1001,new Employee(1001,"马嘉祺","11@qq.com",1,new Department(101,"教学部")));
employees.put(1002,new Employee(1002,"丁程鑫","12@qq.com",1,new Department(101,"教学部")));
employees.put(1003,new Employee(1003,"宋亚轩","13@qq.com",1,new Department(102,"市场部")));
employees.put(1004,new Employee(1004,"刘耀文","14@qq.com",1,new Department(102,"市场部")));
employees.put(1005,new Employee(1005,"张真源","15@qq.com",1,new Department(103,"教研部")));
employees.put(1006,new Employee(1006,"严浩翔","16@qq.com",1,new Department(104,"运营部")));
employees.put(1007,new Employee(1007,"贺峻霖","17@qq.com",1,new Department(104,"运营部")));
}
//主键自增
private static Integer initId = 1008;
//增加一个员工
public void save(Employee employee){
if (employee.getId()==null){
employee.setId(initId++);
}
employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
employees.put(employee.getId(),employee);
}
//查询所有员工
public Collection<Employee> getAll(){
return employees.values();
}
//根据ID查询员工
public Employee getEmployeeById(Integer id){
return employees.get(id);
}
//根据员工id删除员工
public void delete(Integer id){
employees.remove(id);
}
}
@Repository
public class DepartmentDao {
private static Map<Integer, Department> departments = null;
static {
departments = new HashMap<Integer, Department>();//创建一个部门表
departments.put(101,new Department(101,"教学部"));
departments.put(102,new Department(102,"市场部"));
departments.put(103,new Department(103,"教研部"));
departments.put(104,new Department(104,"运营部"));
departments.put(105,new Department(105,"财务部"));
}
//获得所有部门信息
public Collection<Department> getDepartments(){
return departments.values();
}
//通过id得到部门
public Department getDepartmentById(Integer id){
return departments.get(id);
}
}
MyMvcConfig
//如果你想diy一些定制化的功能,只要写这个组件,然后将它交给Springboot,Springboot就会帮我们自动装配!
//扩展Springmvc dispatchservlet
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("index.html").setViewName("index");
}
}
关闭thymeLeaf缓存
#关闭thymeLeaf缓存
spring.thymeleaf.cache=false
前端界面
404.html
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
style>
head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company namea>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign outa>
li>
ul>
nav>
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z">path>
<polyline points="9 22 9 12 15 12 15 22">polyline>
svg>
Dashboard <span class="sr-only">(current)span>
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z">path>
<polyline points="13 2 13 9 20 9">polyline>
svg>
Orders
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart">
<circle cx="9" cy="21" r="1">circle>
<circle cx="20" cy="21" r="1">circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6">path>
svg>
Products
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2">path>
<circle cx="9" cy="7" r="4">circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87">path>
<path d="M16 3.13a4 4 0 0 1 0 7.75">path>
svg>
Customers
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2">
<line x1="18" y1="20" x2="18" y2="10">line>
<line x1="12" y1="20" x2="12" y2="4">line>
<line x1="6" y1="20" x2="6" y2="14">line>
svg>
Reports
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers">
<polygon points="12 2 2 7 12 12 22 7 12 2">polygon>
<polyline points="2 17 12 22 22 17">polyline>
<polyline points="2 12 12 17 22 12">polyline>
svg>
Integrations
a>
li>
ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reportsspan>
<a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10">circle><line x1="12" y1="8" x2="12" y2="16">line><line x1="8" y1="12" x2="16" y2="12">line>svg>
a>
h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Current month
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Last quarter
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Social engagement
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Year-end sale
a>
li>
ul>
div>
nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h1>404h1>
main>
div>
div>
<script type="text/javascript" th:src="@{/js/jquery-3.2.1.slim.min.js}" >script>
<script type="text/javascript" th:src="@{/js/popper.min.js}" >script>
<script type="text/javascript" th:src="@{/js/bootstrap.min.js}" >script>
<script type="text/javascript" th:src="@{/js/feather.min.js}" >script>
<script>
feather.replace()
script>
<script type="text/javascript" th:src="@{/js/Chart.min.js}" >script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
script>
body>
html>
dashboard.html
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
style>
head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company namea>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign outa>
li>
ul>
nav>
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z">path>
<polyline points="9 22 9 12 15 12 15 22">polyline>
svg>
Dashboard <span class="sr-only">(current)span>
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z">path>
<polyline points="13 2 13 9 20 9">polyline>
svg>
Orders
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart">
<circle cx="9" cy="21" r="1">circle>
<circle cx="20" cy="21" r="1">circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6">path>
svg>
Products
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2">path>
<circle cx="9" cy="7" r="4">circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87">path>
<path d="M16 3.13a4 4 0 0 1 0 7.75">path>
svg>
Customers
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2">
<line x1="18" y1="20" x2="18" y2="10">line>
<line x1="12" y1="20" x2="12" y2="4">line>
<line x1="6" y1="20" x2="6" y2="14">line>
svg>
Reports
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers">
<polygon points="12 2 2 7 12 12 22 7 12 2">polygon>
<polyline points="2 17 12 22 22 17">polyline>
<polyline points="2 12 12 17 22 12">polyline>
svg>
Integrations
a>
li>
ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reportsspan>
<a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10">circle><line x1="12" y1="8" x2="12" y2="16">line><line x1="8" y1="12" x2="16" y2="12">line>svg>
a>
h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Current month
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Last quarter
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Social engagement
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Year-end sale
a>
li>
ul>
div>
nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="chartjs-size-monitor" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;">
<div class="chartjs-size-monitor-expand" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0">div>
div>
<div class="chartjs-size-monitor-shrink" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;width:200%;height:200%;left:0; top:0">div>
div>
div>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboardh1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group mr-2">
<button class="btn btn-sm btn-outline-secondary">Sharebutton>
<button class="btn btn-sm btn-outline-secondary">Exportbutton>
div>
<button class="btn btn-sm btn-outline-secondary dropdown-toggle">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-calendar"><rect x="3" y="4" width="18" height="18" rx="2" ry="2">rect><line x1="16" y1="2" x2="16" y2="6">line><line x1="8" y1="2" x2="8" y2="6">line><line x1="3" y1="10" x2="21" y2="10">line>svg>
This week
button>
div>
div>
<canvas class="my-4 chartjs-render-monitor" id="myChart" width="1076" height="454" style="display: block; width: 1076px; height: 454px;">canvas>
main>
div>
div>
<script type="text/javascript" th:src="@{/js/jquery-3.2.1.slim.min.js}" >script>
<script type="text/javascript" th:src="@{/js/popper.min.js}" >script>
<script type="text/javascript" th:src="@{/js/bootstrap.min.js}" >script>
<script type="text/javascript" th:src="@{/js/feather.min.js}" >script>
<script>
feather.replace()
script>
<script type="text/javascript" th:src="@{/js/Chart.min.js}" >script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
script>
body>
html>
index.html
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Signin Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">
head>
<body class="text-center">
<form class="form-signin" action="dashboard.html">
<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">Please sign inh1>
<label class="sr-only">Usernamelabel>
<input type="text" class="form-control" placeholder="Username" required="" autofocus="">
<label class="sr-only">Passwordlabel>
<input type="password" class="form-control" placeholder="Password" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
label>
div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign inbutton>
<p class="mt-5 mb-3 text-muted">© 2017-2018p>
<a class="btn btn-sm">中文a>
<a class="btn btn-sm">Englisha>
form>
body>
html>
list.html
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
style>
head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company namea>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign outa>
li>
ul>
nav>
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z">path>
<polyline points="9 22 9 12 15 12 15 22">polyline>
svg>
Dashboard <span class="sr-only">(current)span>
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z">path>
<polyline points="13 2 13 9 20 9">polyline>
svg>
Orders
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart">
<circle cx="9" cy="21" r="1">circle>
<circle cx="20" cy="21" r="1">circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6">path>
svg>
Products
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2">path>
<circle cx="9" cy="7" r="4">circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87">path>
<path d="M16 3.13a4 4 0 0 1 0 7.75">path>
svg>
Customers
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2">
<line x1="18" y1="20" x2="18" y2="10">line>
<line x1="12" y1="20" x2="12" y2="4">line>
<line x1="6" y1="20" x2="6" y2="14">line>
svg>
Reports
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers">
<polygon points="12 2 2 7 12 12 22 7 12 2">polygon>
<polyline points="2 17 12 22 22 17">polyline>
<polyline points="2 12 12 17 22 12">polyline>
svg>
Integrations
a>
li>
ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reportsspan>
<a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10">circle><line x1="12" y1="8" x2="12" y2="16">line><line x1="8" y1="12" x2="16" y2="12">line>svg>
a>
h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Current month
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Last quarter
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Social engagement
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Year-end sale
a>
li>
ul>
div>
nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h2>Section titleh2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#th>
<th>Headerth>
<th>Headerth>
<th>Headerth>
<th>Headerth>
tr>
thead>
<tbody>
<tr>
<td>1,001td>
<td>Loremtd>
<td>ipsumtd>
<td>dolortd>
<td>sittd>
tr>
<tr>
<td>1,002td>
<td>amettd>
<td>consecteturtd>
<td>adipiscingtd>
<td>elittd>
tr>
<tr>
<td>1,003td>
<td>Integertd>
<td>nectd>
<td>odiotd>
<td>Praesenttd>
tr>
<tr>
<td>1,003td>
<td>liberotd>
<td>Sedtd>
<td>cursustd>
<td>antetd>
tr>
<tr>
<td>1,004td>
<td>dapibustd>
<td>diamtd>
<td>Sedtd>
<td>nisitd>
tr>
<tr>
<td>1,005td>
<td>Nullatd>
<td>quistd>
<td>semtd>
<td>attd>
tr>
<tr>
<td>1,006td>
<td>nibhtd>
<td>elementumtd>
<td>imperdiettd>
<td>Duistd>
tr>
<tr>
<td>1,007td>
<td>sagittistd>
<td>ipsumtd>
<td>Praesenttd>
<td>mauristd>
tr>
<tr>
<td>1,008td>
<td>Fuscetd>
<td>nectd>
<td>tellustd>
<td>sedtd>
tr>
<tr>
<td>1,009td>
<td>auguetd>
<td>sempertd>
<td>portatd>
<td>Mauristd>
tr>
<tr>
<td>1,010td>
<td>massatd>
<td>Vestibulumtd>
<td>laciniatd>
<td>arcutd>
tr>
<tr>
<td>1,011td>
<td>egettd>
<td>nullatd>
<td>Classtd>
<td>aptenttd>
tr>
<tr>
<td>1,012td>
<td>tacititd>
<td>sociosqutd>
<td>adtd>
<td>litoratd>
tr>
<tr>
<td>1,013td>
<td>torquenttd>
<td>pertd>
<td>conubiatd>
<td>nostratd>
tr>
<tr>
<td>1,014td>
<td>pertd>
<td>inceptostd>
<td>himenaeostd>
<td>Curabiturtd>
tr>
<tr>
<td>1,015td>
<td>sodalestd>
<td>ligulatd>
<td>intd>
<td>liberotd>
tr>
tbody>
table>
div>
main>
div>
div>
<script type="text/javascript" th:src="@{/js/jquery-3.2.1.slim.min.js}">script>
<script type="text/javascript" th:src="@{/js/popper.min.js}">script>
<script type="text/javascript" th:src="@{/js/bootstrap.min.js}">script>
<script type="text/javascript" th:src="@{/js/feather.min.js}">script>
<script>
feather.replace()
script>
<script type="text/javascript" th:src="@{/js/Chart.min.js}">script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
script>
body>
html>
实现页面中文和英文语言切换!!!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e512MW5a-1659458588014)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220702135818964.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vq7dzPgf-1659458588014)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220702140228426.png)]
#配置文件的位置
spring.messages.basename=i18n.login
<form class="form-signin" action="dashboard.html">
<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">h1>
<label class="sr-only">Usernamelabel>
<input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only">Passwordlabel>
<input type="password" class="form-control" th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" th:text="#{login.remember}">
label>
div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">button>
<p class="mt-5 mb-3 text-muted">© 2017-2018p>
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">Englisha>
form>
编写MyLocalResolver类
public class MyLocalResolver implements LocaleResolver {
//解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
String language = request.getParameter("l");
Locale locale = Locale.getDefault();//如果没有就使用默认的
//如果请求链接携带了国际化的参数
if (!StringUtils.isEmpty(language)){
String[] split = language.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
在mymvcconfig中注册
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("index.html").setViewName("index");
}
//自定义国际化
@Bean
public LocaleResolver localeResolver(){
return new MyLocalResolver();
}
}
注意:需要先在idea设置的file encoding中将编码语言全部设置为utf-8,否则可能会乱码。
LoginController
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
//登陆成功之后重定向页面
return "redirect:/main.html";
}else {
model.addAttribute("msg","用户名或者密码错误");
return "index";
}
}
}
在MyMvcConfig.java中配置虚拟请求
registry.addViewController("main.html").setViewName("dashboard");
在前端页面用P标签接收错误信息
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}">p>
LoginHandlerInterceptor
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登陆成功之后应该有用户的session
Object loginUser = request.getSession().getAttribute("loginUser");
if(loginUser == null){
request.setAttribute("msg","没有权限,请先登录");
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}else{
return true;
}
}
}
MyMvcConfig配置拦截器的bean
//登录拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/css/**","/img/**","/img/**");
}
在登录请求中配置session
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
session.setAttribute("loginUser",username);
//登陆成功之后重定向页面
return "redirect:/main.html";
}else {
model.addAttribute("msg","用户名或者密码错误");
return "index";
}
}
在首页前端登录成功取到session展示
[[${session.loginUser}]]
在templates创建子文件夹(emp放子页面,commons是碎片化管理)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pJMJqU3I-1659458588015)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220703103230189.png)]
commons的代码(主要区分了顶部和侧边的代码,方便共用)
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]a>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" th:href="@{/user/signOut}">注销a>
li>
ul>
nav>
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/main.html}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z">path>
<polyline points="9 22 9 12 15 12 15 22">polyline>
svg>
首页 <span class="sr-only">(current)span>
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z">path>
<polyline points="13 2 13 9 20 9">polyline>
svg>
Orders
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart">
<circle cx="9" cy="21" r="1">circle>
<circle cx="20" cy="21" r="1">circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6">path>
svg>
Products
a>
li>
<li class="nav-item">
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2">path>
<circle cx="9" cy="7" r="4">circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87">path>
<path d="M16 3.13a4 4 0 0 1 0 7.75">path>
svg>
员工管理
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2">
<line x1="18" y1="20" x2="18" y2="10">line>
<line x1="12" y1="20" x2="12" y2="4">line>
<line x1="6" y1="20" x2="6" y2="14">line>
svg>
Reports
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers">
<polygon points="12 2 2 7 12 12 22 7 12 2">polygon>
<polyline points="2 17 12 22 22 17">polyline>
<polyline points="2 12 12 17 22 12">polyline>
svg>
Integrations
a>
li>
ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reportsspan>
<a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10">circle><line x1="12" y1="8" x2="12" y2="16">line><line x1="8" y1="12" x2="16" y2="12">line>svg>
a>
h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Current month
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Last quarter
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Social engagement
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Year-end sale
a>
li>
ul>
div>
nav>
在dashboard和list下加入使用碎片的方法,使代码复用性提高
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSYPFiQd-1659458588016)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220703103626117.png)]
EmployeeController(点击员工管理模块时查询员工信息并返回到list.html展示)
@RequestMapping("/emps")
public String list(Model model){
Collection<Employee> employees = employeeDao.getAll();
model.addAttribute("emps",employees);
return "emp/list";
}
list页面遍历员工信息
<table class="table table-striped table-sm">
<thead>
<tr>
<th>idth>
<th>lastNameth>
<th>emailth>
<th>genderth>
<th>departmentth>
<th>birthth>
<th>操作th>
tr>
thead>
<tbody>
<tr th:each="emp:${emps}">
<td th:text="${emp.getId()}">td>
<td th:text="${emp.getLastName()}">td>
<td th:text="${emp.getEmail()}">td>
<td th:text="${emp.getGender()==0?'女':'男'}">td>
<td th:text="${emp.department.getDepartmentName()}">td>
<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"> td>
<td>
<button class="btn btn-sm btn-primary">editbutton>
<button class="btn btn-sm btn-danger">delbutton>
td>
tr>
tbody>
table>
在用户展示页面添加一个添加员工的按钮
<h2>
<a class="btn btn-sm btn-success" th:href="@{/emp}">添加员工a>
h2>
编写增加页面add.html
EmployeeController添加两个请求
//点击添加按钮请求到添加页面,因为添加需要部门信息,所以先查询部门信息作为参数传递
@RequestMapping("/emp")
public String toAddpage(Model model){
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
return "emp/add";
}
//点击添加成功按钮,将添加的数据保存,然后重定向到查询用户页面的请求
@PostMapping("/emp")
public String toAddEmp(Employee employee){
employeeDao.save(employee);
return "redirect:/emps";
}
修改日期格式
# 修改日期格式,默认是yyyy/MM/dd
spring.mvc.date-format=yyyy-MM-dd
编辑按钮传参
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑a>
EmployeeController的两个请求
//接收传递的id,然后查询部门信息,跳转到修改界面
@GetMapping("/emp/{id}")
public String toUpdateEmpPage(@PathVariable("id") Integer id, Model model) {
Employee employee = employeeDao.getEmployeeById(id);
model.addAttribute("emp", employee);
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
return "emp/update";
}
//将修改的信息进行修改后重定向到查询用户页面的请求
@PostMapping("/updateEmp")
public String updateEmp(Employee employee) {
employeeDao.save(employee);
return "redirect:/emps";
}
编写修改页面
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form th:action="@{/updateEmp}" method="post">
<input type="hidden" name="id" th:value="${emp.getId()}">
<div class="form-group">
<label>LastNamelabel>
<input name="lastName" th:value="${emp.getLastName()}" type="text" class="form-control"
id="exampleInputEmail1" placeholder="lastName">
div>
<div class="form-group">
<label>Emaillabel>
<input name="email" th:value="${emp.getEmail()}" type="email"
class="form-control" id="exampleInputPassword1" placeholder="email">
div>
<div class="form-group">
<label>Genderlabel>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender()==1}" class="form-check-input" type="radio"
value="1" name="gender"/>
<label class="form-check-label">男label>
div>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender()==0}" class="form-check-input" type="radio"
value="0" name="gender"/>
<label class="form-check-label">女label>
div>
div>
<div class="form-group">
<label>Departmentlabel>
<select name="department.id" class="form-control">
<option th:each="dept:${departments}"
th:selected="${dept.getId()==emp.getDepartment().getId()}"
th:text="${dept.getDepartmentName()}"
th:value="${dept.getId()}">option>
select>
div>
<div class="form-group">
<label>Birthlabel>
<input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" name="birth" type="text"
class="form-control" placeholder="yyyy-MM-dd">
div>
<button type="submit" class="btn btn-default">修改button>
form>
main>
删除按钮传参
<a class="btn btn-sm btn-danger" th:href="@{/deleteEmp/}+${emp.getId()}">删除a>
EmployeeController的一个请求
@GetMapping("/deleteEmp/{id}")
public String deleteEmp(@PathVariable("id") Integer id) {
employeeDao.delete(id);
return "redirect:/emps";
}
注销按钮
<a class="nav-link" th:href="@{/user/signOut}">注销a>
LoginController的一个请求
@RequestMapping("/user/signOut")
public String signOut(HttpSession session){
session.removeAttribute("loginUser");
return "redirect:/index.html";
}
加入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
使用yaml连接数据库配置
spring:
datasource:
username: root
password: root
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
测试整合JDBC环境
@SpringBootTest
class Springboot04DataApplicationTests {
//DI注入数据源
@Autowired(required = false)
DataSource dataSource;
@Test
public void contextLoads() throws SQLException {
//看一下默认数据源
System.out.println(dataSource.getClass());
//获得连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
//关闭连接
connection.close();
}
}
测试CRUD
@RestController
public class JDBCController {
@Autowired(required = false)
JdbcTemplate jdbcTemplate;
// 查询数据库的所有信息
// 没有实体类,获取数据库的东西,怎么获取? Map
@GetMapping("/userList")
public List<Map<String,Object>> userList() {
String sql = "select * from mybatis.user";
List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql);
return list_maps;
}
@GetMapping("/addUser")
public String addUser() {
String sql = "insert into mybatis.user(id, name, pwd) values(8,'小明','123456')";
jdbcTemplate.update(sql);
return "update-ok";
}
@GetMapping("/updateUser/{id}")
public String updateUser(@PathVariable("id") int id) {
String sql = "update mybatis.user set name = ?,pwd = ? where id = " + id;
//封装
Object[] objects = new Object[2];
objects[0] = "小明2";
objects[1] = "aaaaaaa";
jdbcTemplate.update(sql,objects);
return "update-ok";
}
@GetMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable("id") int id) {
String sql = "delete from mybatis.user where id = ?";
jdbcTemplate.update(sql,id);
return "update-ok";
}
}
添加 Druid 和log4j数据源依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.24version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
yaml配置文件添加Druid的需求
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
编写Druid配置类
@Configuration
public class DruidConfig {
//将自定义的Druid配置进IOC容器
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource getDataSource() {
return new DruidDataSource();
}
//配置 Druid 监控管理后台的Servlet;
//内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
// 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "root"); //后台管理界面的登录账号
initParams.put("loginPassword", "admin"); //后台管理界面的登录密码
//后台允许谁可以访问
//initParams.put("allow", "localhost"):表示只有本机可以访问
//initParams.put("allow", ""):为空或者为null时,表示允许所有访问
initParams.put("allow", "");
//deny:Druid 后台拒绝谁访问
//initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问
//设置初始化参数
bean.setInitParameters(initParams);
return bean;
}
//配置 Druid 监控 之 web 监控的 filter
//WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
//exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
Map<String, String> initParams = new HashMap<>();
initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
bean.setInitParameters(initParams);
//"/*" 表示过滤所有请求
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
配置完druid类之后访问http://localhost:8080/druid/可以进入druid的监控,账号密码为配置类里指定的,进入之后可以查看每条sql的信息
导入依赖
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.2version>
dependency>
配置application.yaml
# 整合数据源
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chacacterEncoding=utf-8&serverTimezone=UTC
# Springboot使用 com.mysql.cj.jdbc.Driver 针对Mysql8以上,5可能会有bug
driver-class-name: com.mysql.cj.jdbc.Driver
# 整合mybatis
mybatis:
type-aliases-package: com.example.springboot05mybatis.pojo
# 解决绑定异常:mapper.xml最好和接口的包名路径一致
mapper-locations: classpath:mybatis/mapper/*.xml
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
mapper层接口
@Mapper// 这个注解表示了这是一个mapper的注解类
@Repository
public interface UserMapper {
List<User> getAllUser();
User getUserById(@Param("id") int id);
void addUser(User user);
void deleteUser(@Param("id")int id);
User updateUser(User user);
}
mapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springboot05mybatis.mapper.UserMapper">
<select id="getAllUser" resultType="User">
SELECT * FROM mybatis.user;
select>
<select id="getUserById" parameterType="int" resultType="User">
select * from mybatis.user where id=#{id};
select>
<insert id="addUser" parameterType="User">
insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})
insert>
<update id="updateUser" parameterType="User">
update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id}
update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id}
delete>
mapper>
SpringSecurity 是针对spring项目的安全框架,也是springboot底层默认的安全模块技术选型,它可以实现强大的Web安全机制,只需要少数的spring-boot--spring-security
依赖,进行少量的配置,就可以实现强大的安全管理
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式【@Enablexxxx:开启某个功能】
SpringSecurity的两个主要目标
导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-java8timeartifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
<version>3.0.4.RELEASEversion>
dependency>
RouterController
@Controller
public class RouterController {
@RequestMapping({"/", "/index"})
public String index() {
return "index";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "views/login";
}
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") int id) {
return "views/level1/" + id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id") int id) {
return "views/level2/" + id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id") int id) {
return "views/level3/" + id;
}
}
导入静态资源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0VXsgx4T-1659458588017)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220706154208100.png)]
MySecurityConfig安全配置类编写
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
// url授权: HttpSecurity
@Override
protected void configure(HttpSecurity security) throws Exception {
// 首页所有人可以访问,但是功能也只有对有权限的人可以访问
security
.authorizeRequests() // 认证请求
.antMatchers("/").permitAll()//所有人都可以进入这个请求
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3")
;
// 自带登录页面,http://localhost:8080/login
// 定制登录页,loginPage("/toLogin")
// 指定表单提交url:loginProcessingUrl("/login")
security.formLogin().loginPage("/toLogin")
.usernameParameter("username").passwordParameter("password")
.loginProcessingUrl("/login");
// 防止网站工具 get post
security.csrf().disable();//关闭csrf功能,登录失败存在的原因
// 开启记住我功能:本质就是记住一个cookies,默认保存2周
security.rememberMe().rememberMeParameter("remember");
// 开启注销功能,源码http://localhost:8080/logout,并且注销成功后跳转到/的Controller
security.logout().logoutSuccessUrl("/");
}
// 用户认证:AuthenticationManagerBuilder
// SpringSecurity5 以后默认需要新郑密码密码加密方式
@Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
// 内存中测试数据
builder.inMemoryAuthentication() // SpringSecurity5 以后默认需要新郑密码密码加密方式
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("luffy").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1")
.and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2", "vip3")
;
}
}
index.html修改thymeleaf和security搭建的代码
<div class="right menu">
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon">i> 登录
a>
div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
用户名:<span sec:authentication="name">span>
a>
<a class="item" th:href="@{/logout}">
<i class="sign-out card icon">i> 注销
a>
div>
div>
login.html
<form th:action="@{/login}" method="post">
<div class="field">
<label>Usernamelabel>
<div class="ui left icon input">
<input type="text" placeholder="Username" name="username">
<i class="user icon">i>
div>
div>
<div class="field">
<label>Passwordlabel>
<div class="ui left icon input">
<input type="password" name="password">
<i class="lock icon">i>
div>
div>
<div class="field">
<div class="field">
<input type="checkbox" name="remember"> 记住我
div>
div>
<input type="submit" class="ui blue submit button"/>
form>
Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
导入依赖
<dependency>
<groupId>com.github.theborakompanionigroupId>
<artifactId>thymeleaf-extras-shiroartifactId>
<version>2.0.0version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.22version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.2version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-java8timeartifactId>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.5.3version>
dependency>
编写shiro.ini
[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5
编写Quickstart类
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// The easiest way to create a Shiro SecurityManager with configured
// realms, users, roles and permissions is to use the simple INI config.
// We'll do that by using a factory that can ingest a .ini file and
// return a SecurityManager instance:
// Use the shiro.ini file at the root of the classpath
// (file: and url: prefixes load from files and urls respectively):
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
IniRealm iniRealm=new IniRealm("classpath:shiro.ini");
defaultSecurityManager.setRealm(iniRealm);
// for this simple example quickstart, make the SecurityManager
// accessible as a JVM singleton. Most applications wouldn't do this
// and instead rely on their container configuration or web.xml for
// webapps. That is outside the scope of this simple quickstart, so
// we'll just do the bare minimum so you can continue to get a feel
// for things.
SecurityUtils.setSecurityManager(defaultSecurityManager);
// Now that a simple Shiro environment is set up, let's see what you can do:
// get the currently executing user:
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// let's login the current user so we can check against roles and permissions:
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
运行Quickstart类打印shiro基础信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ybDkGtR-1659458588017)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220707155611813.png)]
导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.23version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.3version>
dependency>
<dependency>
<groupId>com.github.theborakompanionigroupId>
<artifactId>thymeleaf-extras-shiroartifactId>
<version>2.0.0version>
<scope>compilescope>
dependency>
MyController
@Controller
public class MyController {
@RequestMapping({"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/login")
public String login(String username,String password,Model model) {
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
// 验证登录
try {
// 执行登录操作,跨类调用
subject.login(usernamePasswordToken);
model.addAttribute("msg", "成功登录");
return "index";
} catch (UnknownAccountException uae) {
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("msg", "用户密码错误");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未经授权无法访问";
}
}
ShiroConfig配置类
@Configuration
public class ShiroConfig {
//第三步:创建 ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证了才能访问
user:必须拥有"记住我"功能才能用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
//拦截
Map<String,String> filterMap = new LinkedHashMap<String,String>();
//授权,正常的情况下,没有权限会跳转到未授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
filterMap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录的请求
bean.setLoginUrl("/toLogin");
//设置未授权页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//第二步:创建 DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//第一步:创建 realm 对象 自定义
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合ShiroDialect 用来整合Shiro和Thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
shiro配置类需要的UserRealm类
//自定义 继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//这样是写死的,只要用户认证成功以后进来,这里给予权限就代表所有用户都拥有此权限
// info.addStringPermission("user:add");
//拿到当前登录的对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到user对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 模拟数据库中查出用户名、密码
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
User user = userService.queryUserByName(userToken.getUsername());
if(user == null){//没有这个人
return null;
}
Subject currentSubject= SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
// 密码验证,shiro完成,不需要用户判断.直接返回
return new SimpleAuthenticationInfo(user, user.getPwd(), "");
}
}
application.yaml
# 整合数据源
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chacacterEncoding=utf-8&serverTimezone=UTC
# Springboot使用 com.mysql.cj.jdbc.Driver 针对Mysql8以上,5可能会有bug
driver-class-name: com.mysql.cj.jdbc.Driver
# 整合mybatis
mybatis:
type-aliases-package: com.luffy.shirospringboot.pojo
# 解决绑定异常:mapper.xml最好和接口的包名路径一致
mapper-locations: classpath:mapper/*.xml
#SpringBoot默认是不注入这些的,需要自己绑定
#druid数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity
#则导入log4j 依赖就行
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionoProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
index.html
DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>homeh1>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录a>
div>
<p th:text="${msg}">p>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">adda>
div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">updatea>
div>
body>
html>
login.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>loginh1>
<p th:text="${msg}" style="color: red">p>
<form th:action="@{/login}">
<p>username:<input type="text" name="username">p>
<p>password:<input type="text" name="password">p>
<p>LOGIN<input type="submit">p>
form>
body>
html>
swagger号称世界上最流行的API框架,API文档与API定义同步更新,直接运行,支持多种语言。
在项目中使用swagger需要Springfox(2.x版本):
导入依赖
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>3.0.0version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>3.0.0version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-boot-starterartifactId>
<version>3.0.0version>
dependency>
controller类
@RestController
public class HelloController {
@RequestMapping(value = "/hello")
public String hello(){
return "hello";
}
}
编写SwaggerConfig配置类
@Configuration
@EnableSwagger2 //开启Swagger2
public class SwaggerConfig {
}
访问:http://localhost:端口号/swagger-ui/index.html
,(注意swagger2.x版本中访问的地址的为http://localhost:端口号/swagger-ui.html),即可访问Swagger首页
如果Swagger3.0版本跟Springboot不匹配,以下是匹配版本号(不想降低Springboot版本号也有别的解决方法自行百度)
Spring Boot版本 | Swagger 版本 |
---|---|
2.5.6 | 2.9.2 |
config配置类添加
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}
//配置文档信息
private ApiInfo apiInfo() {
Contact contact = new Contact("路飞", "http://www.baidu.com/", "2234179482@qq.com");
return new ApiInfo(
"Swagger学习", // 标题
"学习演示如何配置Swagger", // 描述
"v1.0", // 版本
"http://terms.service.url/组织链接", // 组织链接
contact, // 联系人信息
"Apach 2.0 许可", // 许可
"许可链接", // 许可连接
new ArrayList<>()// 扩展
);
}
构建Docket时通过select()方法配置怎么扫描接口
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(false) //配置是否启用Swagger,如果是false,在浏览器将无法访问
.select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
.apis(RequestHandlerSelectors.basePackage("com.luffy.swaggerdemo.controller"))
// .paths(PathSelectors.ant("/luffy/swaggerdemo/**"))
.build();
// RequestHandlerSelectors的所有扫描方式:
// any() // 扫描所有,项目中的所有接口都会被扫描到
// none() // 不扫描接口
// // 通过方法上的注解扫描,如withMethodAnnotation(GetMapping.class)只扫描get请求
// withMethodAnnotation(final Class annotation)
// // 通过类上的注解扫描,如.withClassAnnotation(Controller.class)只扫描有controller注解的类中的接口
// withClassAnnotation(final Class annotation)
// basePackage(final String basePackage) // 根据包路径扫描接口
// paths(PathSelectors.ant("/luffy/swaggerdemo/**")) //过滤什么路径:过滤/luffy/swaggerdemo下的所有路径
}
配置几个不同的properties环境,在总的properties配置使用的环境
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9RHfSspM-1659458588018)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220712123806470.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Jioetta-1659458588018)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220712123853933.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VTkWrKKb-1659458588019)(C:\Users\22341\AppData\Roaming\Typora\typora-user-images\image-20220712123907210.png)]
config配置类,根据显示的环境是否能够找到返回布尔值提供给enable方法决定是否开启swagger
@Bean
public Docket docket(Environment environment) {
// 设置要显示swagger的环境
Profiles of = Profiles.of("dev", "test");
// 判断当前是否处于该环境
// 通过 enable() 接收此参数判断是否要显示
boolean flag = environment.acceptsProfiles(of);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(flag) //配置是否启用Swagger,如果是false,在浏览器将无法访问
.select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
.apis(RequestHandlerSelectors.basePackage("com.luffy.swaggerdemo.controller"))
// .paths(PathSelectors.ant("/luffy/swaggerdemo/**"))
.build();
// RequestHandlerSelectors的所有扫描方式:
// any() // 扫描所有,项目中的所有接口都会被扫描到
// none() // 不扫描接口
// // 通过方法上的注解扫描,如withMethodAnnotation(GetMapping.class)只扫描get请求
// withMethodAnnotation(final Class annotation)
// // 通过类上的注解扫描,如.withClassAnnotation(Controller.class)只扫描有controller注解的类中的接口
// withClassAnnotation(final Class annotation)
// basePackage(final String basePackage) // 根据包路径扫描接口
// paths(PathSelectors.ant("/luffy/swaggerdemo/**")) //过滤什么路径:过滤/luffy/swaggerdemo下的所有路径
}
创建一个service
@Service
public class AsyncService {
@Async
public void hello(){
try {
Thread.sleep(3000);
}catch (Exception e){
}
System.out.println("数据正在处理!");
}
}
编写controller
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@RequestMapping("/hello")
public String hello(){
asyncService.hello();
return "OK!";
}
}
SpringbootTaskApplication主启动类
@EnableAsync
@SpringBootApplication
public class Springboot09TestApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot09TestApplication.class, args);
}
}
@EnableAsync注解:启动异步
@Async注解:标记一个方法为异步
一般一个请求执行以后要等待返回结果成功后才会返回对应界面,异步任务即是先返回成功界面再继续执行请求任务
导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
application.properties文件配置
spring.mail.username=2234179482@qq.com
spring.mail.password=dvfdnthbsjjreaei
spring.mail.host=smtp.qq.com
# qq需要配置ssl
spring.mail.properties.mail.smtp.ssl.enable=true
测试类
@SpringBootTest
class Springboot09TestApplicationTests {
public static void main(String[] args) {
}
@Autowired
JavaMailSenderImpl mailSender;
@Test
public void contextLoads() {
//邮件设置1:一个简单的邮件
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject("通知-明天来狂神这听课");
message.setText("今晚7:30开会");
message.setTo("2234179482@qq.com");
message.setFrom("2234179482@qq.com");
mailSender.send(message);
}
@Test
public void contextLoads2() throws MessagingException {
//邮件设置2:一个复杂的邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("通知-明天来狂神这听课");
helper.setText("今天 7:30来开会",true);
//发送附件
helper.addAttachment("1.jpg",new File("E:\\图片\\1.jpg"));
helper.setTo("2234179482@qq.com");
helper.setFrom("2234179482@qq.com");
mailSender.send(mimeMessage);
}
}
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候,分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式,提供了两个接口。
两个注解:
使用方式:
Springboot09TestApplication
@EnableAsync//开启异步注解功能
@EnableScheduling //开启基于注解的定时任务
@SpringBootApplication
public class Springboot09TestApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot09TestApplication.class, args);
}
}
Service类
@Service
public class ScheduledService {
//秒 分 时 日 月 周几
//0 * * * * MON-FRI
//注意cron表达式的用法;
@Scheduled(cron = "0/2 * * * * ?")
public void hello(){
System.out.println("hello.....");
}
/*
(1)0/2 * * * * ? 表示每2秒 执行任务
(1)0 0/2 * * * ? 表示每2分钟 执行任务
(1)0 0 2 1 * ? 表示在每月的1日的凌晨2点调整任务
(2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作
(4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
(5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
(6)0 0 12 ? * WED 表示每个星期三中午12点
(7)0 0 12 * * ? 每天中午12点触发
(8)0 15 10 ? * * 每天上午10:15触发
(9)0 15 10 * * ? 每天上午10:15触发
(10)0 15 10 * * ? 每天上午10:15触发
(11)0 15 10 * * ? 2005 2005年的每天上午10:15触发
(12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
(13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
(14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
(15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
(17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
(18)0 15 10 15 * ? 每月15日上午10:15触发
(19)0 15 10 L * ? 每月最后一日的上午10:15触发
(20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
(22)0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
*/
}
分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统
分布式系统(distributed system)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。
网址:https://zookeeper.apache.org/(下载带bin的)
运行/bin/zkServer.cmd ,初次运行会报错(闪退),在conf下没有zoo.cfg配置文件
将conf文件夹下面的zoo_sample.cfg复制一份改名为zoo.cfg
运行/bin/zkServer.cmd(服务端)
运行zkCli.cmd(客户端)
在客户端输入:ls /(列出zookeeper根下保存的所有节点)
在客户端输入:create –e /luffy 123(创建一个luffy节点,值为123)
在客户端输入:get /luffy(获取/luffy节点的值)
dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。
但是为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序dubbo-admin,不过这个监控即使不装也不影响使用。
使用方法
下载dubbo-admin
解压进入目录
在Dubbo-admin项目目录下打包dubbo-admin
执行 dubbo-admin\target 下的dubbo-admin-0.0.1-SNAPSHOT.jar
这里给的下载网址直接有jar包,如果zookeeper和dubbo的配置都一致,即可直接运行jar包
在jar包目录运行cmd:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
注意:zookeeper服务要开启
全部执行完访问 http://localhost:7001/ ,输入登录账户和密码,默认的是root-root,即可显示dubbo-admin界面
1. 启动zookeeper !
2. IDEA创建一个空项目;
3.创建一个模块,实现服务提供者:provider-server , 选择web依赖即可
卖票接口
package com.luffy.service;
public interface TicketService {
public String getTicket();
}
卖票实现类
package com.luffy.service;
public class TicketServiceImpl implements TicketService {
@Override
public String getTicket() {
return "《狂神说Java》";
}
}
提供者依赖
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.3version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
提供者application.properties
#当前应用名字
dubbo.application.name=provider-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#扫描指定包下服务
dubbo.scan.base-packages=com.luffy.service
4.创建一个模块,实现服务消费者:consumer-server , 选择web依赖即可
5.项目创建完毕,我们写一个服务,比如用户的服务
消费者依赖
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.3version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
消费者application.properties
#当前应用名字
dubbo.application.name=consumer-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
消费者的服务类
package com.luffy.service;
import com.guo.provider.service.TicketService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
@Service //注入到容器中
public class UserService {
@Reference //远程引用指定的服务,他会按照全类名进行匹配,看谁给注册中心注册了这个全类名
TicketService ticketService;
public void buyTicket(){
String ticket = ticketService.getTicket();
System.out.println("在注册中心买到"+ticket);
}
}
6.编写测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class ConsumerServerApplicationTests {
@Autowired
UserService userService;
@Test
public void contextLoads() {
userService.buyTicket();
}
}