SpringBoot 学习笔记(1)

相关概念

微服务

  • 微服务是一种架构风格

  • 一个应用拆分为一组小型服务

  • 每个服务运行在自己的进程内,也就是可独立部署和升级

  • 服务器之间使用轻量级 HTTP 交互

  • 服务围绕业务功能拆分

  • 可以由全自动部署机制独立部署

  • 去中心化,服务自治,服务可以使用不同的语言,不同的存储技术

HelloWorld 程序

创建 Maven 工程

引入依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>


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

</dependencies>

创建主程序和业务程序

/**
 * 主程序类
 * @SpringBootApplication:这是一个SpringBoot应用
 */
@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class,args);
    }
}
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle01(){
        return "Hello World";
    }
}

统一配置

springboot 为我们提供了一个简化的配置文件 application.properties,我们可以在里面进行对如 tomcat、springmvc 的配置。

例如修改访问端口

server.port=8888

加入可以简化部署的插件

在 pom.xml 中

 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

之后选择打包

运行对应 jar 包

SpringBoot 特点

依赖管理

  • 父项目依赖管理
依赖管理    
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
</parent>

他的父项目
 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
  </parent>

几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
  • 默认版本号的修改
1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
2、在当前项目里面重写配置
    <properties>
        <mysql.version>5.1.43</mysql.version>
    </properties>
  • starter 场景启动器
1、见到很多 spring-boot-starter-* : *就代表某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
4、见到的  *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>2.3.4.RELEASE</version>
  <scope>compile</scope>
</dependency>

自动配置

  • 自动配置 Tomcat

引入 Tomcat 依赖

配置 Tomcat

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
</dependency>
  • 自动配置 SpringMVC

引入 SpringMVC 全套组件

自动配置号 SpringMVC 常用组件

  • 默认的包结构

主程序所在的包以及下面所有子包里的路径里面的组件都会被默认扫描出来,无需包扫描配置。

可以通过 @SpringBootApplication(scanBasePackages="com.an") 或者 @ComponentScan 指定扫描路径

@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.an.boot")

容器功能

组件添加

配置类

package com.an.boot.config;

import com.an.boot.bean.Pet;
import com.an.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author an
 * @create 2021-07-03-20:05
 * 1、配置类里面使用 @Bean 标注在方法上给容器注册组件,默认也是单实例的
 * 2、配置类本身也是属性
 * 3、proxyBeanMethod 是否为代理 Bean 的方法
 *    可以解决组件依赖问题
 */

@Configuration(proxyBeanMethods = true) // 告诉 Springboot 这是一个配置类
public class MyConfig {

	/**
	 * 外部物理对配置类中的这个组件调用多少次获取的都是之前注册容器中的单实例对象
	 * @return
	 */
	@Bean // 给容器中添加组件,以方法名作为组件 id,返回类型就是组件类型,返回的值为组件实例
	public User user01() {
		return new User("zhangsan",18);
	}

	@Bean("tom") // Bean后面括号内的字符串为组件 id
	public Pet tomcatPet() {
		return new Pet("tomcat");
	}

}

主程序类

package com.an.boot;

import com.an.boot.bean.Pet;
import com.an.boot.bean.User;
import com.an.boot.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;


/**
 * 主程序类
 * @SpringBootApplication 告诉 SpringBoot 这是一个 SpringBoot 应用
 */
@SpringBootApplication
public class MainApplication {
	public static void main(String[] args) {
		// 1、返回 IOC 容器
		ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
		// 2、查看容器里面的值
		String[] names = run.getBeanDefinitionNames();
		for (String name : names) {
			System.out.println(name);
		}

		// 3、从容器中获取组件,组件默认为单实例对象
		Pet tom01 = run.getBean("tom",Pet.class);
		Pet tom02 = run.getBean("tom",Pet.class);

		MyConfig bean = run.getBean(MyConfig.class);
		// 如果 @Configuration(proxyBeanMethods=true) 代理对象调用方法,springboot 总会检查这个组件是否在容器中有,保存组件单实例
		User user = bean.user01();
		User user1 = bean.user01();
		System.out.println(user==user1);
	}
}

传统的方式

可以在类上面添加 @Bean@Component@Controller 等注解来注册组件

import 注解

@Import({User.class})

使用无参构造器默认导入对应的组件,默认组件名称为全类名

Condition 注解

条件装配,满足 Condition 指定的条件,才进行组件注入

=====================测试条件装配==========================
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
//@ConditionalOnBean(name = "tom")
@ConditionalOnMissingBean(name = "tom")
public class MyConfig {


    /**
     * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
     * @return
     */

    @Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        //user组件依赖了Pet组件
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom22")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

public static void main(String[] args) {
        //1、返回我们IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

        //2、查看容器里面的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

        boolean tom = run.containsBean("tom");
        System.out.println("容器中Tom组件:"+tom);

        boolean user01 = run.containsBean("user01");
        System.out.println("容器中user01组件:"+user01);

        boolean tom22 = run.containsBean("tom22");
        System.out.println("容器中tom22组件:"+tom22);


}

原生配置文件导入

@ImportResource

======================beans.xml=========================
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="haha" class="com.atguigu.boot.bean.User">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>

    <bean id="hehe" class="com.atguigu.boot.bean.Pet">
        <property name="name" value="tomcat"></property>
    </bean>
</beans>
@ImportResource("classpath:beans.xml")
public class MyConfig {}

======================测试=================
boolean haha = run.containsBean("haha");
boolean hehe = run.containsBean("hehe");
System.out.println("haha:"+haha);//true
System.out.println("hehe:"+hehe);//true

配置绑定

使用 Java 读取到 properties 文件中的内容,并把它封装到 JavaBean 中,以供随时使用。

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean。
         }
     }
 }

@ConfigurationProperties

/**
 * 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
 */
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private String brand;
    private Integer price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}
public class HelloController {
    @Autowired
    Car car
    
    @RequestMapping("/car")
    public Car car() {
        return car;
    }
}

方式2,不使用 @Conponent

@EnableConfigurationProperties(Car.class)
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig {
}

自动配置原理

引导自动加载类

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}


======================

@SpringBootConfiguration 代表当前是一个配置类

@ComponentScan 指定扫描哪些类

@EnableAutoConfiguration

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage

@Import(AutoConfigurationPackages.Registrar.class)  //给容器中导入一个组件
public @interface AutoConfigurationPackage {}

//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。

@Import(AutoConfigurationImportSelector.class)

1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories

最佳实践与开发技巧

引入相关的场景依赖

https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

查看自动配置了哪些

更改配置文件 debug=true 就可以查看自动报告,Negative 为不生效

简化 java bean 方法(Lombok)

引入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

在idea中搜索安装lombok插件

简化后的 Bean

get、set 和 toString

package com.an.boot.bean;

import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author an
 * @create 2021-07-04-10:21
 */
@ToString
@Data
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
	private String brand;
	private Integer price;
}

全参和无参构造器、equals 和 hashcode 方法

package com.an.boot.bean;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * @author an
 * @create 2021-07-03-20:03
 */
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Pet {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Pet{" +
				"name='" + name + '\'' +
				'}';
	}
}


日志

@Slf4j
log.info("日志打印");

dev-tools

引入依赖

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

作用:热更新,不必在每次修改项目时都需要重启

只需要按 ctrl+f9 就能实时生效

Spring Initialize

idea中,此项目 可以快速创建 SpringBoot 应用

配置文件

properties 配置文件

默认配置文件

yaml

1、定义

YAML 是 "YAML Ain't Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。

非常适合用来做以数据为中心的配置文件

2、基本语法:

  • key: value; kv 之间存在空格

  • 大小写敏感

  • 使用缩进表示层级关系

  • 缩进只允许使用空格

  • 缩进空格数量不重要,只要左对齐

  • # 表示注释

  • '' 内字符串会被 / 转义 "" 不会被转义

3、数据类型

字面量:单个、不可再分的值

k: v

对象:键值对的集合

行内写法:  k: {k1:v1,k2:v2,k3:v3}
#或
k: 
  k1: v1
  k2: v2
  k3: v3

数组

行内写法:  k: [v1,v2,v3]
#或者
k:
 - v1
 - v2
 - v3

4、示例

@Data
public class Person {
	
	private String userName;
	private Boolean boss;
	private Date birth;
	private Integer age;
	private Pet pet;
	private String[] interests;
	private List<String> animal;
	private Map<String, Object> score;
	private Set<Double> salarys;
	private Map<String, List<Pet>> allPets;
}

@Data
public class Pet {
	private String name;
	private Double weight;
}
# yaml表示以上对象
person:
  userName: zhangsan
  boss: false
  birth: 2019/12/12 20:12:33
  age: 18
  pet: 
    name: tomcat
    weight: 23.4
  interests: [篮球,游泳]
  animal: 
    - jerry
    - mario
  score:
    english: 
      first: 30
      second: 40
      third: 50
    math: [131,140,148]
    chinese: {first: 128,second: 136}
  salarys: [3999,4999.98,5999.99]
  allPets:
    sick:
      - {name: tom}
      - {name: jerry,weight: 47}
    health: [{name: mario,weight: 47}]

5、自定义类绑定的配置提示

添加配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>


<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-configuration-processor</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

静态资源访问

静态资源目录

只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources
访问 : 当前项目根路径/ + 静态资源名

原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面

更改默认的静态资源路径

spring:
  mvc:
    static-path-pattern: /res/**

  resources:
    static-locations: [classpath:/static/]

webjar

自动映射 /webjars/**


        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.5.1</version>
        </dependency>

欢迎页支持

静态资源欢迎页面

给静态资源路径下放入 index.html

不可以配置静态资源的访问前缀,否则会导致 index.html 不能被默认访问

配置controller处理/index

自定义 favicon

将 favicon.ico 放入静态资源路径中

静态资源配置原理

SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)

SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效

请求处理

请求映射

@xxxMapping

Rest 风格(使用 HTTP 请求方式动词来表示对资源的操作

以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
核心Filter;HiddenHttpMethodFilter
用法: 表单method=post,隐藏域 _method=put
SpringBoot中手动开启
扩展:如何把_method 这个名字换成我们自己喜欢的。

    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String getUser(){
        return "GET-张三";
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String saveUser(){
        return "POST-张三";
    }


    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String putUser(){
        return "PUT-张三";
    }

    @RequestMapping(value = "/user",method = RequestMethod.DELETE)
    public String deleteUser(){
        return "DELETE-张三";
    }

由于 form 表单只支持 post,get 请求,想要使用其他请求,将所有提交的方式全部修改为 post 方式,设置一个隐藏的 input 选项,name 为 _method,即为提交一个 _method 的隐藏参数

======================== 原理 ====================================
	@Bean
	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
	@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
	public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
		return new OrderedHiddenHttpMethodFilter();
	}


//自定义filter
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m");
        return methodFilter;
    }
<form>
  <input name="_method" type="hidden" value="DELETE"/>
</form>

表单提交会带上_method=PUT

请求过来被HiddenHttpMethodFilter拦截
请求是否正常,并且是POST
获取到_method的值。
兼容以下请求;PUT.DELETE.PATCH
原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

常用参数注解

@PathVariable:路径变量,一般用在 Rest 请求
@PathVaribale("id")..:提取单个路径变量
@PathVarible Map<String,String>:提取所有路径变量
@RequestHeader("User-Agent"):获取单个请求头
@RequestHeader Map<String,String>:获取所有请求头
@RequestParam("age"):获取单个请求参数
@RequestParam Map<String,String>:获取所有请求参数
@CookieValue("_ga"):获取单个 Cookie 变量的值
@CookieValue Map<String,String>:获取所有 Cookie 变量的值
@RequestBody:获取请求体
@RequestAttribute:获取 request 域属性
@MatrixVariable:矩阵变量

/cars/{path;low=34;brand=byd} :矩阵变量

@RestController
public class ParameterTestController {


    //  car/2/owner/zhangsan
    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params,
                                     @CookieValue("_ga") String _ga,
                                     @CookieValue("_ga") Cookie cookie){


        Map<String,Object> map = new HashMap<>();

//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("headers",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        map.put("_ga",_ga);
        System.out.println(cookie.getName()+"===>"+cookie.getValue());
        return map;
    }


    @PostMapping("/save")
    public Map postMethod(@RequestBody String content){
        Map<String,Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }


    //1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
    //2、SpringBoot默认是禁用了矩阵变量的功能
    //      手动开启:原理。对于路径的处理。UrlPathHelper进行解析。
    //              removeSemicolonContent(移除分号内容)支持矩阵变量的
    //3、矩阵变量必须有url路径变量才能被解析
    @GetMapping("/cars/{path}")
    public Map carsSell(@MatrixVariable("low") Integer low,
                        @MatrixVariable("brand") List<String> brand,
                        @PathVariable("path") String path){
        Map<String,Object> map = new HashMap<>();

        map.put("low",low);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }

    // /boss/1;age=20/2;age=10

    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
                    @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
        Map<String,Object> map = new HashMap<>();

        map.put("bossAge",bossAge);
        map.put("empAge",empAge);
        return map;
    }
}

视图解析与模板引擎

SpringBoot 默认不支持 JSP,需要引入第三方模板引擎技术来实现页面渲染。

thymeleaf

1、基本语法

${...} 获取请求域,session域、对象等

*{...} 获取上下文对象值

#{...} 获取国际化等值

@{...} 生成链接

~{...} 引入公共页面

2、条件运算

if-then:(if)?(then)

if-then-else:(if)?(then):(else)

Default:(value)?:(defaultvalue)

3、设置属性值 - th:attr

设置单个值:

<form action="subscribe.html" th:attr="action=@{/subscribe}">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
  </fieldset>
</form>

设置多个值

<img src="../../images/gtvglogo.png"  th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

代替写法

<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
<form action="subscribe.html" th:action="@{/subscribe}">

4、迭代

<tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
        <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
  <td th:text="${prod.name}">Onions</td>
  <td th:text="${prod.price}">2.41</td>
  <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

5、条件运算

<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administrator</p>
  <p th:case="#{roles.manager}">User is a manager</p>
  <p th:case="*">User is some other thing</p>
</div>

thymeleaf 使用

1、引入 Starter

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

2、页面开发

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${msg}">哈哈</h1>
<h2>
    <a href="www.atguigu.com" th:href="${link}">去百度</a>  <br/>
    <a href="www.atguigu.com" th:href="@{link}">去百度2</a>
</h2>
</body>
</html>

拦截器

HandlerInterceptor 接口

/**
 * 登录检查
 * 1、配置好拦截器要拦截哪些请求
 * 2、把这些配置放在容器中
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        log.info("preHandle拦截的请求路径是{}",requestURI);

        //登录检查逻辑
        HttpSession session = request.getSession();

        Object loginUser = session.getAttribute("loginUser");

        if(loginUser != null){
            //放行
            return true;
        }

        //拦截住。未登录。跳转到登录页
        request.setAttribute("msg","请先登录");
//        re.sendRedirect("/");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     * 目标方法执行完成以后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行{}",modelAndView);
    }

    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行异常{}",ex);
    }
}

配置拦截器

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有请求都被拦截包括静态资源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
    }
}

文件上传

表单页面

<form method="post" th:action="@{/upload}" enctype="multipart/form-data">
    <input type="file" name="headerImg" id="exampleInputFile"><br>
    <input type="file" name="photos" multiple><br>
    <input type="submit" value="提交">
</form>

文件上传代码

/**
 * MultipartFile 自动封装上传过来的文件
 * @param email
 * @param username
 * @param headerImg
 * @param photos
 * @return
 */
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
                     @RequestParam("username") String username,
                     @RequestPart("headerImg") MultipartFile headerImg,
                     @RequestPart("photos") MultipartFile[] photos) throws IOException {

    log.info("上传的信息:email={},username={},headerImg={},photos={}",
            email,username,headerImg.getSize(),photos.length);

    if(!headerImg.isEmpty()){
        //保存到文件服务器,OSS服务器
        String originalFilename = headerImg.getOriginalFilename();
        headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
    }

    if(photos.length > 0){
        for (MultipartFile photo : photos) {
            if(!photo.isEmpty()){
                String originalFilename = photo.getOriginalFilename();
                photo.transferTo(new File("H:\\cache\\"+originalFilename));
            }
        }
    }


    return "main";
}

异常处理机制

默认规则

1、默认情况下,Spring Boot 提供 /error 处理所有错误的映射

2、对于机器客户端,将生成 JSON 相应,其中包含错误,HTTP 状态和异常消息的详细信息,对于浏览器客户端,响应一个 whitelabel 错误视图,以 HTML 格式呈现相同数据

自定义

1、要对其进行自定义,添加 View 解析为 error

2、要完全替换默认行为,可以实现 ErrorController 并注册该类型的 Bean 定义,或添加 ErrorAttributes 类型的组件以使用现有机制但是替换其内容

3、error/ 下的 4xx,5xx 页面会被自动解析

Web 原生组件注入(Servlet、Filter、Listener)

使用 Servlet API

package servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author an
 * @create 2021-07-04-19:21
 */
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.getWriter().write("6666");
	}
}

同时在主配置类中加入

@ServletComponentScan(basePackage="com.an")

直接响应,没有经过 Spring 的拦截器。

需要拦截就需要再写 Filter

package servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @author an
 * @create 2021-07-04-19:39
 */
@Slf4j
@WebFilter(urlPatterns = {"/css/*","/images/*","/my/*"})
public class MyFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		log.info("初始化");
	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		log.info("MyFilter 工作");
		filterChain.doFilter(servletRequest,servletResponse);
	}

	@Override
	public void destroy() {
		log.info("销毁");
	}
}

数据访问

SQL

1、导入 JDBC 场景

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

2、导入 mysql 驱动

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

自定义 Druid 数据源

1、引入数据源

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>

2、写配置类

package com.an.boot.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * @author an
 * @create 2021-07-04-20:12
 */
@Configuration
public class MyDataSourceConfig {

	// 默认的自动配置是判断容器中没有才生效
	@ConfigurationProperties(prefix = "spring.datasource")
	@Bean
	public DruidDataSource druidDataSource() {

		return new DruidDataSource();
	}

	/**
	 * 配置 druid 的监控页
	 * @return
	 */
	@Bean
	public ServletRegistrationBean statViewServlet() {
		StatViewServlet statViewServlet = new StatViewServlet();
		ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean = new ServletRegistrationBean<StatViewServlet>(statViewServlet, "/druid/*");
		return statViewServletServletRegistrationBean;
	}
}

3、测试

package com.an.boot;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Slf4j
@SpringBootTest
class Web1ApplicationTests {

	@Autowired
	JdbcTemplate jdbcTemplate;


	@Autowired
	DataSource dataSource;

	@Test
	void contextLoads() {
		log.info("数据源类型{}",dataSource.getClass());
	}

}

4、另一种方式:引入官方 starter

MyBatis 整合

引入 starter

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

配置模式

传统模式

1、全局配置文件

2、SqlSessionFactory

3、SqlSession

4、Mapper

SpringBoot 模式

1、SqlSessionFactory 自动配置好了

2、SqlSession:自动配置了 SqlSessionTemplate 组合了 SqlSession

3、Mapper:只要我们写的操作 MyBatis 的接口标注了 @Mapper 就会被自动扫描进来

yaml配置文件


posted @ 2021-07-03 23:07  ans20xx  阅读(74)  评论(0编辑  收藏  举报