SpringBoot(一)配置介绍、项目搭建
2022-09-22 22:47:08

目录
[TOC]


martinfowler关于微服务的论文

1. 项目搭建

Demo

IDEA:new project,然后spring initializr

springBoot的作用简单说就是:简化开发

写Demo时,直接再springBootApplication启动类 同级目录 创建 controller service dao等即可

比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.yue.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@Controller
public class HelloController {

@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "SpringBoot for WebCrud";
}

@RequestMapping("/success")
public String success(Map<String,Object> map){
map.put("user","胖子");
return "success";
}

}

启动后

浏览器通过http://localhost:8080/hello 显示 SpringBoot for WebCrud 字样

可以在 application.properties 通过 server.port=8080修改端口

热部署

  1. 首先需自动编译

    1. 打开顶部工具栏 File -> Settings -> Default Settings -> Build -> Compiler 然后勾选 Build project automatically
      image-20200816193757349
    2. 同时按住 Ctrl + Shift + Alt + / 然后进入Registry ,勾选自动编译并调整延时参数。image-20200816193612025
  2. pom文件 添加热部署插件

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    </dependency>

2. 自动配置初探

pom.xml

版本控制都在spring-boot-dependencies-2.3.0.RELEASE.pom

starter 启动器

启动器:就是SpringBoot的启动场景

根据场景,使用对应的启动器就可以,starters 参考文档

主程序

@SpringBootApplication

  • @SpringBootConfiguration:SpringBoot的配置

    • @Configuration:Spring配置类
      • @Component:说明这也是Spring的一个组件
  • @EnableAutoConfiguration:自动配置

    • @AutoConfigurationPackage:自动配置包

      • @Import({Registrar.class})包注册
    • @Import({AutoConfigurationImportSelector.class}):导入选择器

      • List configurations = this.getCandidateConfigurations(annotationMetadata, attributes):

        获取候选的配置

        1
        2
        3
        4
        5
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
        }

spring-boot-autoconfigure/2.3.0.RELEASE/spring-boot-autoconfigure-2.3.0.RELEASE.jar!/META-INF/spring.factories
核心配置文件

image-20200817230359568

image-20200817230359568

1
Properties properties = PropertiesLoaderUtils.loadProperties(resource);

所有的资源加载到配置类中

总结

  1. SpringBoot在启动的时候,从类路径下的META-INF/spring.factories获取指定的值:

  2. 将这些自动配置的类导入容器,自动配置就会生效

结论

SpringBoot所有自动配置是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定会生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功!

3. 基础配置

yaml

也可以写做:yml

application.yamlapplication.properties可以作为Spring的配置文件,来更改SpringBoot的默认配置

application.yaml 写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 写法是 key: value (value前面必须有个一个空格。用空格的多少来控制属性的同级or上下级关系)
server:
port: 8080
## 对象
student:
name: shangsan
sex: 0
## 对象行内写法
person: {name: lisi,age: 18}
## 列表
pets:
- cat
- dog
- pig
## 列表行内写法
pets: [cat,dog,pig]

application.properties 写法

1
2
3
4
server.port=8080

student.name=zhangsan
student.sex=0

Demo:配置文件读取

  • 写配置文件
  • 创建实体类
    • Person.java
    • Dog.java
  • 测试

代码

application.yaml配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 8080

## 1. yaml支持松散绑定 比如 last-name 可以匹配 lastName 属性
## 2. 可以利用${}来插入一些东西,比如${randomm.uuid}等等
person:
name: 小明
sex:
happy: true
brithday: 2019/11/02
maps: {k1: v1,k2: v2,k3: v3}
dog:
name: 黑黑
age: 6
friends:
- 小明
- 小明2
- 小明3

实体类

  1. Person.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.chasing.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;
/**
* 使用@ConfigurationProperties注解 需要在pom文件中添加依赖
*
* <dependency>
* <groupId>org.springframework.boot</groupId>
* <artifactId>spring-boot-configuration-processor</artifactId>
* <optional>true</optional>
* </dependency>
*
*/

/**
* 要是自己建立别的配置文件 比如:myconfig.properties
* 就不能用@ConfigurationProperties
* 而是需要用这个注解 来指定配置文件 @PropertySource(value = "classpath:myconfig.properties")

* 并且 需要配合SPEL表达式,如:
* @Value("${name1}")
* private String name;
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {

private String name;
private String sex ;
private Boolean happy;
private Date brithday;
private Map<String,Object> maps;
private Dog dog;
public Person(){}

public Person(String name, String sex, Boolean happy, Date brithday, Map<String, Object> maps, Dog dog) {
this.name = name;
this.sex = sex;
this.happy = happy;
this.brithday = brithday;
this.maps = maps;
this.dog = dog;
}

//toString + Getter + Setter 省略
}
  1. Dog.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.chasing.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;

@Component
public class Dog {

private String name;
private Integer age;
private List friends;

public Dog(){
}

public Dog(String name, Integer age, List friends) {
this.name = name;
this.age = age;
this.friends = friends;
}

//toString + Getter + Setter 省略

}

测试,利用自带的测试类

image-20200818220107944

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.chasing;
import com.chasing.pojo.Dog;
import com.chasing.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Springboot02ConfigApplicationTests {

@Autowired
private Person p1;

@Test
void contextLoads() {
System.out.println(p1);
}

}

运行:image-20200818221013873

结果:

1
2
Person{name='小明', sex='男', happy=true, brithday=Sat Nov 02 00:00:00 CST 2019, maps={k1=v1, k2=v2, k3=v3}, dog=Dog{name='黑黑', age=6, friends=[小明, 小明2, 小明3]}}

在实际使用中,推荐使用yml的方式

JSR303校验

  1. 加入pom依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
  2. 使用

    • @Component
      @Validated //@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
      @ConfigurationProperties(prefix = "blackdog")
      public class Dog {
           //2.在属性上进行校验 这里的意思是年龄超过51 报错信息为‘年龄错了’
          @Max(value = 5l,message = "年龄错了")
          private Integer age;
          private List friends;
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32

      其他校验注解

      ```java
      @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 对象是否符合正则表达式的规则

      .......等等
      除此以外,我们还可以自定义一些数据校验规则

配置文件的加载及优先级

1。 springboot启动会扫描以下位置的application.properties或者application.yml作为默认的配置文件

  • 工程根目录:./config/

  • 工程根目录:./

  • classpath:/config/

  • classpath:/

    加载的优先级顺序是从上向下加载,并且所有的文件都会被加载,高优先级的内容会覆盖底优先级的内容,形成互补配置

  1. 也可以通过指定配置spring.config.location来改变默认配置,一般在项目已经打包后,我们可以通过指令   java -jar xxxx.jar –spring.config.location=D:/kawa/application.yml来加载外部的配置

多环境切换

方式一:

  1. 建立一个 application配置文件

  2. 建立其他配置文件(名字格式必须是application-xxx

    比如 开发环境是application-dev.yaml,``application-dev.test`是测试环境1

image-20200819232647583

3.文件中通过image-20200819232909507来指定配置文件,来方便的切换

方式二:

只写一个配置文件application.yml 通过active来指定配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server:
port: 8080
# 启动dev的1配置
spring:
profiles:
active: dev
---
# 配置一
server:
port: 8084

spring:
profiles: test
---
# 配置二
server:
port: 8086

spring:
profiles: dev

静态资源优先级

resources > static > public

首页:在resources、 static、 public中刃任意一个放如index.html即可

templates下的文件只能通过 controller来进行跳转

Thymeleaf

配置Thymeleaf

  1. pom引入
1
2
3
4
5
<!--thymeleaf-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  1. Html引入
1
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  1. 在setting中开始输入提示

image-20200821221356141

Demo

  • test.HTML
  • HelloController.java

image-20200821222449699

test.HTML

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>SpringBoot</title>
</head>
<body>
<h1 >Done</h1>
<h1 th:text="${msg}"></h1>
</body>
</html>

HelloController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.chasing.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {

@RequestMapping("hello")
public String hello(Model modle){
modle.addAttribute("msg","i can ! ");
return "test";
}
}

结果

image-20200821222711706s’sss

4. 员工管理系统

4.1 准备工作

概要

  • 安装lombok
  • 编写实体类 Department.java Employee.java 部门和员工类
  • 编写Dao DepartmentDao.java EmployeeDao.java(不用装数据库,暂时在Dao中模拟数据)
  • 导入前台模板

使用lombok简化开发

LomBok作用:简单说就是通过加注解的方式,帮你自动生成toString\Getter\Setter\构造函数\equals方法等等,不用我们手动去编写,代码也看起来更加简洁

  1. pom引入

    1
    2
    3
    4
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    </dependency>
  2. 安装lombok插件。(两种情况)
    现在新版本的IDEA可能在商场里搜不到lombok插件,先试着搜插件,若能搜到就Install。进入第三步
    image-20200823090201181

    若搜索不到

    进入该网址下载lombok插件,要根据你的IDEA版本下载
    image-20200823090449769

    下载后安装lombok

    image-20200823091721045

    然后找到你下载的lombok image-20200823091908092 进行安装即可,提示restart就restart

  3. 使用lombok
    根据需要在类上加注解,即可
    image-20200823090946293

    注解含义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    常用注解:
    @Setter :注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
    @Getter :使用方法同上,区别在于生成的是getter方法。
    @ToString :注解在类,添加toString方法。
    @EqualsAndHashCode: 注解在类,生成hashCode和equals方法。
    @NoArgsConstructor: 注解在类,生成无参的构造方法。
    @RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
    @AllArgsConstructor: 注解在类,生成包含类中所有字段的构造方法。
    @Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
    @Slf4j: 注解在类,生成log变量,严格意义来说是常量。

创建实体类

src\main\java\com\chasing\pojo\Department.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.chasing.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {

//id
private Integer id;
//部门名字
private String departmentName;
}

src\main\java\com\chasing\pojo\Employee.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.chasing.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; // 0 famle ,1 male
private Department department;
private Date brith;

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.brith = new Date();//每次都用当前时间代替
}
}

编写Dao

src\main\java\com\chasing\dao\EmployeeDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.chasing.dao;
import com.chasing.pojo.Department;
import com.chasing.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Repository
public class EmployeeDao {

@Autowired
private DepartmentDao departmentDao;
//模拟数据库中的员工数据
private static Map<Integer, Employee> employees = null;
static {
employees = new HashMap<Integer, Employee>();
employees.put(1001,new Employee(1001,"小米","4832747382@qq.com",1,new Department(104,"建设部")));
employees.put(1002,new Employee(1002,"小黄","6122212@qq.com",0,new Department(102,"体育部")));
employees.put(1003,new Employee(1003,"小名","1025047382@qq.com",0,new Department(101,"教学部")));
employees.put(1004,new Employee(1004,"小分","233747382@qq.com",1,new Department(105,"学习部")));
employees.put(1005,new Employee(1005,"小五","8789482@qq.com",1,new Department(103,"图书部")));
}

//主键自增
private static Integer initId = 1006;
//增加一个员工
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 getEmployeeeById(Integer id){
return employees.get(id);
}
//删除员工通过id
public void delEmployeeeById(Integer id) {
employees.remove(id);
}
}

src\main\java\com\chasing\dao\DepartmentDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.chasing.dao;
import com.chasing.pojo.Department;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
//部门Dao
@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(101,new Department(102,"体育部"));
departments.put(101,new Department(103,"学习部"));
departments.put(101,new Department(104,"建设部"));
departments.put(101,new Department(105,"图书部"));
}
//获取所有的部门信息
public Collection<Department> getDepartment(){
return departments.values();
}
//根据id获取部门
public Department getDepartmentByid(Integer id){
return departments.get(id);
}
}

导入前台模板

image-20200823095357517

  • 如图copy到项目
    image-20200823095206084

4.2 登录功能和拦截器

  • index.html的表单加入name属性,并加入
  • 编写LoginController.java控制器
  • 编写LoginHandlerInterceptor.java拦截器
  • 编写MyMvcConfig.java配置mvc

src\main\resources\templates\index.html

1
2
3
4
5
6
7
8
9
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<!--若登录错误,在界面上显示提示信息-->
<p style="color: red" th:if="${not #strings.isEmpty(msg)}" th:text="${msg}" ></p>
<label class="sr-only">Username</label>
<!--加上name=uname字段,便于和后台交互-->
<input type="text" name="uname" class="form-control" placeholder="Username" required="" autofocus="">
<label class="sr-only">Password</label>
<!--加上name=pwd字段,便于和后台交互-->
<input type="password" name="pwd" class="form-control" placeholder="Password" required="">

src\main\java\com\chasing\controller\LoginController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.chasing.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.net.HttpCookie;

@Controller
@RequestMapping("/user")
public class LoginController {

@RequestMapping(value = "/login")
public String login(@RequestParam("uname") String uname,
@RequestParam("pwd") String pwd,
Model model, HttpSession session){
//若登录成功 则重定向到主界面(简单判断用户名不为空 和 密码=121)
if(!StringUtils.isEmpty(uname) && "121".equals(pwd)){
//将用户名存入session 便于 拦截器 进行判断
session.setAttribute("loginUser",uname);
return "redirect:/main";
}else{
//否则,告诉用户登陆失败
model.addAttribute("msg","用户名或者密码错误!");
return "index";
}
}
}

src\main\java\com\chasing\config\LoginHandlerInterceptor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.chasing.config;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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){
return true;
}
request.setAttribute("msg","没有权限,请先登录!");
request.getRequestDispatcher("/index").forward(request,response);
return false;
}
}

src\main\java\com\chasing\config\MyMvcConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.chasing.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/404").setViewName("404");
registry.addViewController("/main").setViewName("dashboard");
registry.addViewController("/list").setViewName("list");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
//addInterceptor(new LoginHandlerInterceptor()) 拦截后进入我们自定义的拦截器
//addPathPatterns("/**") 拦截所用请求
//excludePathPatterns("/index","/","/user/login","/css/*","/js/**","/img/**"); //但是,以上请求不拦截
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index","/","/user/login","/css/*","/js/**","/img/**");
}
}

Prev
2022-09-22 22:47:08
Next