124.【SpringBoot 源码刨析C】

news2024/11/25 21:36:04

SpringBoot源码刨析C

  • (三)、SpringBoot核心功能
    • 2.Web
      • 4.数据响应与内容协商
        • (1).响应JSON
          • (1.1)jackson.jar+@ResponseBody
            • (1.1.1)、返回值解析器
            • (1.1.2)、返回值解析器原理
          • (1.2).SpringMVC到底支持哪些返回值
          • (1.3)/HTTPMessageConverter原理
            • (1.3.1)、MessageConverter规范
            • (1.3.2)、默认的MessageConverter
        • (2).内容协商 (MessageConverter)
          • (2.1)、引入XML文件
          • (2.2)、postman分别测试返回json和xml
          • (2.3)、基于请求参数的内容协商 ⭐⭐
          • (2.4)、内容协商原理
          • (2.5)、自定义 MessageConverter
          • (2.6)、运用参数的方式请求自定义内容协商
        • 5.视图解析与模板引擎
          • (1).视图解析
            • (1.1)、视图解析原理流程
          • (2).Thymeleaf基本语法
            • (2.1)、表达式
            • (2.2)、字面量
            • (2.3)、文本操作
            • (2.4)、数学运算
            • (2.5)、布尔运算
            • (2.6)、比较运算
            • (2.7)、条件运算
            • (2.8)、特殊操作
            • (2.9)、设置属性值-th:attr
            • (2.10)、迭代
            • (2.11)、条件运算
          • (3).Thymeleaf的使用
            • (3.1)、引入依赖
            • (3.2)、自动配置好了thymeleaf
            • (3.2)、页面开发
          • (4).后台管理系统总结
            • (4.1)、举列子(公共方)
            • (4.2)、配置拦截器
            • (4.3)、文件上传(表单)
        • 6.异常处理
          • (1).错误处理
            • (5.1)、默认规则
            • (5.2)、定制错误处理逻辑 (==三种方法 ==)
            • (5.3)、异常处理自动配置原理
            • (5.4)、异常处理步骤流程
        • 7.Web原生组件注入(Servlet、Filter、Listener)
          • (1).使用Servlet API (第一种方式)
          • (2).使用RegistrationBean (第二种方式)
        • 8.嵌入式Servlet容器
          • (1).切换嵌入式Servlet容器
          • (2).定制Servlet容器
        • 9.定制化原理
          • (1).定制化的常见方式
          • (2).原理分析套路
    • 3.数据访问
      • 1.SQL
        • (1).数据源的自动配置-HikariDataSource
          • (1.1) 、导入JDBC场景
          • (1.2) 、分析自动配置JDBC引入了啥
          • (1.3) 、修改配置项
      • 2.使用Druid数据源
        • (1).druid官方github地址
        • (2).自定义方式 (使用德鲁伊数据源)
          • (2.1)、创建数据源
          • (2.2)、Spring时代配置德鲁伊数据源
          • (2.3)、 配置类SpringBoot时代
        • (3).使用官方starter方式
          • (3.1)、引入druid-starter
          • (3.2)、分析自动配置
          • (3.3)、示列
      • 3.Mybatis 操作数据库
        • (1).原始(Spring) - 配置模式
        • (2).非注解的配置模式
        • (3).纯注解的配置模式
        • (4).非注解和纯注解可以混合使用
      • 4.Mybatis-Plus 操作数据库
        • (1).什么是MybatisPlus
        • (2).整合Mybatisplus
      • 5.整合Redis 非SQL数据库
        • (1).Redis 自动配置
        • (2).RedisTemplate与Lettuce
        • (3).切换至jedis

(三)、SpringBoot核心功能

2.Web

4.数据响应与内容协商

在这里插入图片描述

(1).响应JSON

(1.1)jackson.jar+@ResponseBody

在SpringMVC场景中,并不会自动给我们返回Json字符串的也没有Json字符串的过滤器,在SpringBoot中

只要我们使用了@ResponseBody (就会利用返回值处理器里面的消息转换器进行处理)

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
web场景自动引入了json场景
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>

在这里插入图片描述
给前端自动返回json数据;

package com.jsxs.controller;

import com.jsxs.bean.Person;
import com.jsxs.bean.Pet;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * @Author Jsxs
 * @Date 2023/7/6 10:16
 * @PackageName:com.jsxs.controller
 * @ClassName: ResponseTestController
 * @Description: TODO
 * @Version 1.0
 */

@Controller
@ResponseBody
public class ResponseTestController {

    @GetMapping("/test/person")
    public Person person() throws ParseException {
        Person person = new Person("jsxs", 12, new SimpleDateFormat("yyyy-MM-dd").parse("2012-02-01"), new Pet("哈吉米", 2));
        return person;
    }
}
(1.1.1)、返回值解析器

15个方法返回值解析器
在这里插入图片描述

		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

在这里插入图片描述

RequestResponseBodyMethodProcessor   类。


	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
	// 使用消息转换器进行调用
		// Try even with null return value. ResponseBodyAdvice could get involved.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

(1.1.2)、返回值解析器原理
  • 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
  • 2、返回值处理器调用 handleReturnValue 进行处理
    • 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
        1. 利用 MessageConverters 进行处理 将数据写为json
        • 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型 浏览器接受7种)
          在这里插入图片描述
        • 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据
          在这里插入图片描述
        • 3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter (消息转换器),看谁能处理?
          在这里插入图片描述

            • 1、得到MappingJackson2HttpMessageConverter可以将对象写为json
            • 2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
(1.2).SpringMVC到底支持哪些返回值

这里对应着15种返回值解析器

ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask@ModelAttribute 且为对象类型的


@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor
(1.3)/HTTPMessageConverter原理
(1.3.1)、MessageConverter规范

在这里插入图片描述
HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据

例子:Person对象转为JSON。或者 JSON转为Person

(1.3.2)、默认的MessageConverter

在这里插入图片描述

0 - 只支持Byte类型的
1 - String
2 - String
3 - Resource
4 - ResourceRegion
5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
6 - MultiValueMap
7 - true (不管是谁直接为true,也就是说不管是啥文件直接接受)
8 - true (不管是谁直接为true,也就是说不管是啥文件直接接受)
9 - 支持注解方式 xml处理的。

最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)

·AbstractGenericHttpMessageConverter类种的·

outputMessage.getBody().flush();

在这里插入图片描述

(2).内容协商 (MessageConverter)

根据客户端接收能力不同,返回不同媒体类型的数据。

(2.1)、引入XML文件
 <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

在这里插入图片描述

(2.2)、postman分别测试返回json和xml

只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

在这里插入图片描述

(2.3)、基于请求参数的内容协商 ⭐⭐

为了方便内容协商,开启基于请求参数的内容协商功能

spring:
	mvc:
     contentnegotiation:
      favor-parameter: true  #开启请求参数内容协商模式

通过点开源码我们发现我们需要什么样的类型,我们只需要在路径后面添加上 format='xxx' 格式即可。前提是我们需要什么类型的时候要有依赖在 pom.xml 中。比如我们需要使用xml的,我们要有上面的那个 jackson-dataformat-xml 依赖。

在这里插入图片描述

获取json: http://localhost:8080/test/person?format=json
获取xml格式: http://localhost:8080/test/person?format=xml
在这里插入图片描述

源码中发现相比于以前的请求头的内容协商多了一个参数的请求协商。

在这里插入图片描述

确定客户端接收什么样的内容类型;
1、Parameter策略优先确定是要返回json数据(获取请求头中的format的值

在这里插入图片描述

2、最终进行内容协商返回给客户端xml即可。
在这里插入图片描述

(2.4)、内容协商原理
  • 1.判断当前响应头种是否已经有已经确定的媒体类型。MediateType
  • 2.获取客户端(浏览器或者PostMan)支持的请求的Accept头(浏览器)。通过 contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
    • (1).先得到客户端能够接受的所有媒体类型是什么。
  • 3.获取服务器能够生产的媒体类型。(服务器方)
  • 4.遍历服务器所有支持的媒体类型 进行 与客户端能够接受的类型进行匹配的操作,选择最佳匹配。
  • 5.用支持将对象转为最佳媒体类型的converter,调用它进行转化。

为什么说引入xml的转环包就会被底层接受的原理

WebMvcConfigurationSupport 类下

917行 jackson2XmlPresent

在这里插入图片描述
在这里插入图片描述

(2.5)、自定义 MessageConverter

实现多协议数据兼容。json、xml、x-guigu

0、@ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理
1、Processor 处理方法返回值。通过 MessageConverter 处理
2、所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
3、内容协商找到最终的 messageConverter;

package com.jsxs.controller;

import com.jsxs.bean.Person;
import com.jsxs.bean.Pet;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * @Author Jsxs
 * @Date 2023/7/6 10:16
 * @PackageName:com.jsxs.controller
 * @ClassName: ResponseTestController
 * @Description: TODO
 * @Version 1.0
 */

@Controller
@ResponseBody
public class ResponseTestController {

    /**
     *
     * @return
     * @throws ParseException
     *
     * @TODO: 1.浏览器请求返回xml文件。2.ajax请求返回json文件。3.硅谷app请求返回自定义文件
     *  在以前一个请求完成这项工作这是不可能完成的任务,但是在现在我们有了内容协商我们可以完成这个任务。
     *  步骤: 1.添加自定义的MessageConverter进入系统底层。2.系统底层就会统计出所有MessageConverter
     * 		3.进行内容客户端与服务器内容协商匹配。
     */

    @GetMapping("/test/person")
    public Person person() throws ParseException {
        Person person = new Person("jsxs", 12, new SimpleDateFormat("yyyy-MM-dd").parse("2012-02-01"), new Pet("哈吉米", 2));
        return person;
    }
}

在这里插入图片描述
SpringMVC的什么功能。一个入口给容器中添加一个 WebMvcConfigurer

 @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {

            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

            }
        }
    }

首先配置协议转换器

package com.jsxs.convert;

import com.jsxs.bean.Person;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/7 12:38
 * @PackageName:com.jsxs.convert
 * @ClassName: GguiGuMessageConverter
 * @Description: TODO   自定义的消息转换器
 * @Version 1.0
 */
public class GuiGuMessageConverter implements HttpMessageConverter<Person> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    /**
     *   条件是什么
     * @param clazz
     * @param mediaType
     * @return
     */
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Person.class);  //只有返回的类型是Person类行就能进行读写
    }

    /**
     *   服务器要统计所有的MessageConverter 都能写哪些内容类型
     *
     *   application/x-jsxs
     * @return
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-jsxs");
    }

    @Override
    public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        // 自定义协议的写出: (也就是在返回的格式)
        String data =person.getUserName()+";"+person.getAge()+";"+person.getAge()+";"+person.getPet();

        // 写出去
        OutputStream body = outputMessage.getBody();
        body.write(data.getBytes());
    }
}

package com.jsxs.config;

import com.jsxs.bean.Pet;
import com.jsxs.convert.GuiGuMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/3 11:13
 * @PackageName:com.jsxs.config
 * @ClassName: WebConfig
 * @Description: TODO
 * @Version 1.0
 */
@Configuration(proxyBeanMethods = false)
// 第一种方式 @Configuration + 实现WebMvcConfigurer接口 (因为JDK8允许接口的默认方法和默认实现所以我们不需要将所有方法全部重写)
// 第二种方式: @Configuration +@Bean 重新注入我们的组件
public class WebConfig /*implements WebMvcConfigurer */{

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("aaaa");
        return hiddenHttpMethodFilter;
    }

//    @Override
//    public void configurePathMatch(PathMatchConfigurer configurer) {
//        UrlPathHelper helper = new UrlPathHelper();
//        helper.setRemoveSemicolonContent(false);
//        configurer.setUrlPathHelper(helper);
//    }


    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer(){
            //  配置支持我们的矩阵注解
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper helper = new UrlPathHelper();
                helper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(helper);
            }
            // 配置支持我们的自定义converter转换器

            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String, Pet>() {
                    @Override
                    public Pet convert(String source) {  //source 就是页面提交过来的值。只获得过来的值
                        if (!StringUtils.isEmpty(source)){  // 假如说提交的数据不为空
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);  // 逗号之前的设置成姓名
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }

            // 扩展 内容消息转换器

            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new GuiGuMessageConverter());
            }
        };
    }
}

上面的内容可以通过 PostMan 进行处理。浏览器因为我们自己设置不了请求头,所以目前测试不了我们自定义的内容协商。

(2.6)、运用参数的方式请求自定义内容协商

我们通过debug的方式进入到了我们浏览器接受的内容协议上,并查看到有两种接收方式,并在请求参数的方式上没有看到 自定义的格式,所以我们要进行自定义的操作。
在这里插入图片描述
因为只兼容上面两种 所以我们要进行配置内容协商功能

package com.jsxs.config;

import com.jsxs.bean.Pet;
import com.jsxs.convert.GuiGuMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

import java.util.*;

/**
 * @Author Jsxs
 * @Date 2023/7/3 11:13
 * @PackageName:com.jsxs.config
 * @ClassName: WebConfig
 * @Description: TODO
 * @Version 1.0
 */
@Configuration(proxyBeanMethods = false)
// 第一种方式 @Configuration + 实现WebMvcConfigurer接口 (因为JDK8允许接口的默认方法和默认实现所以我们不需要将所有方法全部重写)
// 第二种方式: @Configuration +@Bean 重新注入我们的组件
public class WebConfig /*implements WebMvcConfigurer */{

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("aaaa");
        return hiddenHttpMethodFilter;
    }

//    @Override
//    public void configurePathMatch(PathMatchConfigurer configurer) {
//        UrlPathHelper helper = new UrlPathHelper();
//        helper.setRemoveSemicolonContent(false);
//        configurer.setUrlPathHelper(helper);
//    }


    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer(){
            //  配置支持我们的矩阵注解
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper helper = new UrlPathHelper();
                helper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(helper);
            }
            // 配置支持我们的自定义converter转换器

            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String, Pet>() {
                    @Override
                    public Pet convert(String source) {  //source 就是页面提交过来的值。只获得过来的值
                        if (!StringUtils.isEmpty(source)){  // 假如说提交的数据不为空
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);  // 逗号之前的设置成姓名
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }

            // 扩展 内容消息转换器
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new GuiGuMessageConverter());
            }

            // 自定义(重写)内容协商  ⭐⭐⭐

            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                // 要求需要为String
                HashMap<String, MediaType> mediaTypeHashMap = new HashMap<>();
                // 配置支持的请求参数
                mediaTypeHashMap.put("json",MediaType.APPLICATION_JSON);
                mediaTypeHashMap.put("xml",MediaType.APPLICATION_XML);
                mediaTypeHashMap.put("jsxs",MediaType.parseMediaType("application/x-jsxs"));
                // 支持解析哪些参数对应的哪些媒体类型 -》 参数内容协商支持
                ParameterContentNegotiationStrategy parameterContentNegotiationStrategy = new ParameterContentNegotiationStrategy(mediaTypeHashMap);
                //  支持解析哪些参数对应的哪些媒体类型 -》 请求头内容协商支持 (这里通过PostMan进行测试)
                HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();
                //    真正执行
                configurer.strategies(Arrays.asList(parameterContentNegotiationStrategy,headerContentNegotiationStrategy));
            }
        };
    }
}

在这里插入图片描述

在这里插入图片描述
有可能我们添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效。

大家考虑,上述功能除了我们完全自定义外?SpringBoot有没有为我们提供基于配置文件的快速修改媒体类型功能?怎么配置呢?【提示:参照SpringBoot官方文档web开发内容协商章节】

5.视图解析与模板引擎

视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染
在这里插入图片描述

(1).视图解析
(1.1)、视图解析原理流程

1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址
2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer
3、任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。
4、processDispatchResult 处理派发结果(页面该如何响应)

DisplatchServlet的 第1078
  • 1、render(mv, request, response); 进行页面渲染逻辑
    • 1、根据方法的String返回值得到 View 对象【定义了页面的渲染逻辑】

      • 1、所有的视图解析器尝试是否能根据当前返回值得到View对象 (for遍历尝试)
      • 2、得到了 redirect:/main.html --> Thymeleaf new RedirectView()
      • 3、ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。
        在这里插入图片描述
      • 4、view.render(mv.getModelInternal(), request, response); 视图对象调用自定义的render进行页面渲染工作
    • view.render(mv.getModelInternal(), request, response); 1393行

        • RedirectView类 如何渲染【重定向到一个页面】
        • 1、获取目标url地址
        • 2、response.sendRedirect(encodedURL);

视图解析:

  • 返回值以 forward: 开始: new InternalResourceView(forwardUrl); --> 转发request.getRequestDispatcher(path).forward(request, response);
  • 返回值以 redirect: 开始: new RedirectView() --》 render就是重定向
(2).Thymeleaf基本语法
(2.1)、表达式

在这里插入图片描述

(2.2)、字面量

文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格

(2.3)、文本操作

字符串拼接: +
变量替换: |The name is ${name}|

(2.4)、数学运算

运算符: + , - , * , / , %

(2.5)、布尔运算

运算符: and , or
一元运算: ! , not

(2.6)、比较运算

比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )

(2.7)、条件运算

If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)

(2.8)、特殊操作

无操作: _

(2.9)、设置属性值-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}" />

以上两个的代替写法 th:xxxx

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

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes
行内写法

1. 假如要写的内容不在标签中而在行内,那么就用这个方式。(非session)

<h1>[[${xxx}]]</h1>

2. 假如是取Session的值
<h1>[[$session.name.xxx}]]</h1>
(2.10)、迭代
<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>
(2.11)、条件运算
<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>
(3).Thymeleaf的使用
(3.1)、引入依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
(3.2)、自动配置好了thymeleaf

ThymeleafAutoConfiguration 类

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration { }

自动配好的策略

  • 1、所有thymeleaf的配置值都在 ThymeleafProperties 类
  • 2、配置好了SpringTemplateEngine
  • 3、配好了ThymeleafViewResolver
  • 4、我们只需要直接开发页面

在这里插入图片描述

(3.2)、页面开发
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Success</h1>
<h1 th:text="${A}"></h1>
<a th:href="${baidu}">点击我去金橘社区 ${baidu}</a>
<br>
<br>
<a th:href="@{baidu}">点击我去金橘社区 @{baidu}</a>
<br>
<br>
<a th:href="@{/baidu}">点击我去金橘社区 @{/baidu}</a>
</body>
</html>
package com.jsxs.controller;

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

/**
 * @Author Jsxs
 * @Date 2023/7/7 17:30
 * @PackageName:com.jsxs.controller
 * @ClassName: ViewTestController
 * @Description: TODO
 * @Version 1.0
 */
@Controller
public class ViewTestController {

    @GetMapping("/toTest")
    public String toTest(Model model){
        // model 会自动放入到请求域中和HttpRequest是一样的,只能接受一次请求的操作
        model.addAttribute("A","a");
        model.addAttribute("baidu","https://www.jsxs1.cn");
        return "success";   // 假如说没有模板解析器的话,这里的路径会报黄。
    }
}

# 给整个服务器添加前缀
server:
  servlet:
    context-path: /jsxs

在这里插入图片描述

(4).后台管理系统总结
1. 假如在template中再新建包的话,我们只需要在 controller 的返回值中添加上新建包路径即可  /新建包名/xxx。

2. controller 页面跳转的实质是转发;不是重定向。

3. return: 的值会默认拼接  templates/xxxx.html; return forward: return redirect 找的都是请求的路径不是页面。

4. 静态资源只要放在四大区域就行,前端调用的时候可以省略掉前面的四大区域路径只写相对路径即可。

5. 抽取公共模板(第一种)
	(1). 公共页(top.html): 在标签中设置  th:fragment="AAAA"
		eg: <div  th:fragment="AAAA"></div>
	(2). 使用公共页方: th:insert="~{公共页的HTML名字 :: AAAA}"
		eg:   1. <div th:insert="~{top :: AAAA}"></div>2.<div th:insert="top :: AAAA"></div>3.<div th:replace="~{top :: utopbar}"></div>4.<div th:include="~{top :: topbar}"></div>
	假如说公共页面和被添加公共页面不再同一个包中那么就要加上路径指定在哪
		eg:<div th:include="~{commons/top :: topbar}"></div>

6.抽取公共模板(第二种 ->选择器方式)
	(1). 公共页(top.html): 在标签中设置  id="BBB"
	(2). 使用方: <div th:replace="top :: #BBB"> (当然以上的几种方法都适用)

公共方:

<footer th:fragment="copy">
  &copy; 2011 The Good Thymes Virtual Grocery
</footer>

使用公共方

<body>

  ...
	会带上div(全部都要)
  <div th:insert="~{footer :: copy}"></div>
	引入的东西不会在div里面
  <div th:replace="~{footer :: copy}"></div>
  	引入的中西会在div里面
  <div th:include="~{footer :: copy}"></div>
  
</body>

实际效果:

假如引入的是css、js。也是一样的只不过把div改为link或script
<body>

  ...

  <div>
    <footer>
      &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
  </div>

  <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
  </footer>
  
  <div>
      &copy; 2011 The Good Thymes Virtual Grocery
  </div>
  
</body>
(4.1)、举列子(公共方)

1.公共方的页面
在这里插入图片描述

2.replace->引入的东西会在link标签里面内嵌link标签

<link th:replace="~{}">

在这里插入图片描述
3.include->引入的东西会在link标签里面内嵌link标签

<link th:include="~{}">

在这里插入图片描述
4.标签改为div,div是万能的。

<div th:replace="~{}"></div>

在这里插入图片描述

(4.2)、配置拦截器

SpringMvc的拦截器配置

1.Filter:Servlet定义的原生组件。好处:脱离Spring也能够使用
2.Interceptor:Spring定义的接口。可以使用Spring的自动装配功能

第一种拦截机制

package com.jsxs.servlet;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @Author Jsxs
 * @Date 2023/7/8 17:58
 * @PackageName:com.jsxs.servlet
 * @ClassName: MyFilter
 * @Description: TODO   配置过滤器
 * @Version 1.0
 */

@Slf4j
//@WebFilter(urlPatterns = {"/css/*","/imag/*"})   //设置拦截我们CSS和imag的所有文件

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       log.info("MyFilter初始化完成.....");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("MyFilter工作中.....");
        chain.doFilter(request,response);
    }

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

第二种拦截机制

在这里进行拦截的操作

package com.jsxs.config;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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
        if (request.getSession().getAttribute("LoginUser")!=null){
            return true;
        }else {
            request.setAttribute("msg","没有权限请先进行登入");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, 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/**"); //放行的请求
    }
}

拦截器原理:

  1. 根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
  2. 先来顺序执行所有拦截器的 preHandle方法
    1. 如果当前拦截器prehandler返回为true(通行)。则执行下一个拦截器的preHandle
    1. 如果当前拦截器返回为false(不通行)。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;
  1. 如果任何一个拦截器返回false。直接跳出不执行目标方法
  2. 所有拦截器都返回True。执行目标方法
  3. 倒序执行所有拦截器的postHandle方法。
  4. 前面的步骤有任何异常都会直接倒序触发 afterCompletion
  5. 页面成功渲染完成以后,也会倒序触发 afterCompletion

如图:
在这里插入图片描述

(4.3)、文件上传(表单)
1. 需要指定 enctype
2. 单个文件上传 什么也不加
3. 多个文件上传 需要加上: multiple 属性

<form method="post" action="/upload" enctype="multipart/form-data">
 单文件   <input type="file" name="file"><br>
多文件    <input type="file" name="file" multiple><br>
    <input type="submit" value="提交">
</form>
1.一定要加上参数注解 @RequestPart

    /**
     * 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)); //这个方法直接传输了,内部封装了InputStrean和OutStream流(需要指定路径+文件名即可)
        }

        if(photos.length > 0){
            for (MultipartFile photo : photos) {
                if(!photo.isEmpty()){
                    String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("H:\\cache\\"+originalFilename)); //遍历传输各个文件
                }
            }
        }


        return "main";
    }

在这里插入图片描述

原理

MultipartAutoConfigurationMultipartProperties

在这里插入图片描述

  1. 自动配置原理

文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties

  • 自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
  • 原理步骤
    • 1、请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求`
    • 2、参数解析器来解析请求中的文件内容封装成`MultipartFile
    • 3、将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>
      FileCopyUtils。实现文件流的拷贝

在这里插入图片描述

6.异常处理

(1).错误处理
(5.1)、默认规则
  • 默认情况下,Spring Boot提供 /error处理所有错误的映射
  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据

在这里插入图片描述
在这里插入图片描述
要对其进行自定义,添加View解析为error

  • 要完全替换默认行为,可以实现 ErrorController 并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。
  • 静态资源或templates目录下放 error/下的4xx,5xx页面会被自动解析

在这里插入图片描述

(5.2)、定制错误处理逻辑 (==三种方法 ==)
  • 自定义错误页 (第一种)
    • error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页
在自定义的4xx页面或者5xx页面的某个提示标签中可以这么写
th:text="${status}"  ->在配置的错误页面中会获得返回的状态码
th:text="${message}"  ->在配置的错误页面中会获得返回的信息
  • @ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的 (第二种⭐)

在这里插入图片描述

  • @ResponseStatus+自定义异常 ;底层是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底层调用 response.sendError(statusCode, resolvedReason);tomcat发送的/error (第三种)

先定义: 编写有参构造和无参构造,并不是继承父类的方法

在这里插入图片描述
后使用

在这里插入图片描述

  • Spring底层的异常,如 参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常。
    • response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());

在这里插入图片描述

  • ErrorViewResolver 实现自定义处理异常;
    • response.sendError 。error请求就会转给controller
    • 你的异常没有任何人能处理。tomcat底层 response.sendError。error请求就会转给controller
    • basicErrorController 要去的页面地址是 ErrorViewResolver ;
(5.3)、异常处理自动配置原理
  • ErrorMvcAutoConfiguration 自动配置异常处理规则
    • 容器中的组件:类型:DefaultErrorAttributes -> id:errorAttributes
      • public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
      • DefaultErrorAttributes:定义错误页面中可以包含哪些数据。

在这里插入图片描述
在这里插入图片描述

  • 容器中的组件:类型:BasicErrorController --> id:basicErrorController(json+白页 适配响应)
      • 处理默认 /error 路径的请求;页面响应 new ModelAndView(“error”, model);
      • 容器中有组件 View->id是error;(响应默认错误页)
      • 容器中放组件 BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。
  • 容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
      • 如果发生错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面
      • error/404、5xx.html
(5.4)、异常处理步骤流程

1、执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException
2、进入视图解析流程(页面渲染?)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
3、mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;

  • 1、遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器

在这里插入图片描述

  1. 系统默认的 异常解析器;
  • 1、DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;
  • 2、默认没有任何人能处理异常,所以异常会被抛出
      • 1、如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理
      • 2、解析错误视图;遍历所有的 ErrorViewResolver 看谁能解析。
      • 3、默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html
      • 4、模板引擎最终响应这个页面 error/500.html

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

(1).使用Servlet API (第一种方式)
首先要在主类中设置需要扫描的servlet包在哪。
1. @ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet组件都放在那里,默认是扫描主类所在包的子包

然后我们在servlet包中配置我们的原生servlet
2. @WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器?

3. @WebFilter(urlPatterns={"/css/*","/images/*"})
4. @WebListener

扩展:DispatchServlet 如何注册进来

  • 容器中自动配置了 DispatcherServlet 属性绑定到 WebMvcProperties;对应的配置文件配置项是 spring.mvc
    在这里插入图片描述

  • 实质上通过 ServletRegistrationBean<DispatcherServlet> 把 DispatcherServlet 配置进来。
    在这里插入图片描述

  • 默认映射的是 / 路径。
    在这里插入图片描述

Tomcat-Servlet

多个Servlet都能处理到同一层路径,精确优选原则
A: /my/ ->(假如说这个路径可以处理my/下的所有路径)
B: /my/1 -> (假如说这个路径可以处理my/1这个准确路径)
在这里插入图片描述

主类

package com.jsxs;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class SpringBootLs02Application {

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

}

servlet

package com.jsxs.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 Jsxs
 * @Date 2023/7/8 17:24
 * @PackageName:com.jsxs.servlet
 * @ClassName: MyServlet
 * @Description: TODO  1.添加上注解.   2.重写get和post方法
 * @Version 1.0
 */
@WebServlet(urlPatterns = "/my")  //标注是原生的API,并指定拦截路径
public class MyServlet extends HttpServlet {

    //  请求后的真实业务
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("666666");  //拦截后展示666
    }
    //  请求后的真实业务
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

Filter

package com.jsxs.servlet;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @Author Jsxs
 * @Date 2023/7/8 17:58
 * @PackageName:com.jsxs.servlet
 * @ClassName: MyFilter
 * @Description: TODO   配置过滤器
 * @Version 1.0
 */

@Slf4j
@WebFilter(urlPatterns = {"/css/*","/imag/*"})   //设置拦截我们CSS和imag的所有文件

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       log.info("MyFilter初始化完成.....");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("MyFilter工作中.....");
        chain.doFilter(request,response);
    }

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

Listener

package com.jsxs.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * @Author Jsxs
 * @Date 2023/7/8 18:05
 * @PackageName:com.jsxs.servlet
 * @ClassName: MyServletContextListener
 * @Description: TODO
 * @Version 1.0
 */

@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("MyServletContextListener监听器初始化完成.....");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyServletContextListener监听器销毁完成.....");
    }
}

在这里插入图片描述

(2).使用RegistrationBean (第二种方式)

去掉以上方法的各个注解(但保留类),并添加一个配置类实现三个原生API。
主类

package com.jsxs;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class SpringBootLs02Application {

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

}

servlet: 将注册的组件删除

package com.jsxs.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 Jsxs
 * @Date 2023/7/8 17:24
 * @PackageName:com.jsxs.servlet
 * @ClassName: MyServlet
 * @Description: TODO  1.添加上注解.   2.重写get和post方法
 * @Version 1.0
 */
//@WebServlet(urlPatterns = "/my")  //标注是原生的API,并指定拦截的路径(就是不放行)
public class MyServlet extends HttpServlet {

    //  请求后的真实业务
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("666666");  //拦截后展示666
    }
    //  请求后的真实业务
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

Filter:将注册的组件删除

package com.jsxs.servlet;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @Author Jsxs
 * @Date 2023/7/8 17:58
 * @PackageName:com.jsxs.servlet
 * @ClassName: MyFilter
 * @Description: TODO   配置过滤器
 * @Version 1.0
 */

@Slf4j
//@WebFilter(urlPatterns = {"/css/*","/imag/*"})   //设置拦截我们CSS和imag的所有文件

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       log.info("MyFilter初始化完成.....");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("MyFilter工作中.....");
        chain.doFilter(request,response);
    }

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

Listener:将注册的组件删除

package com.jsxs.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * @Author Jsxs
 * @Date 2023/7/8 18:05
 * @PackageName:com.jsxs.servlet
 * @ClassName: MyServletContextListener
 * @Description: TODO
 * @Version 1.0
 */

@Slf4j
//@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("MyServletContextListener监听器初始化完成.....");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyServletContextListener监听器销毁完成.....");
    }
}

MyRegisterConfig 配置类实现

package com.jsxs.config;

import com.jsxs.servlet.MyFilter;
import com.jsxs.servlet.MyServlet;
import com.jsxs.servlet.MyServletContextListener;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

/**
 * @Author Jsxs
 * @Date 2023/7/8 18:14
 * @PackageName:com.jsxs.config
 * @ClassName: MyServlet
 * @Description: TODO
 * @Version 1.0
 */
@Configuration
public class MyRegisterConfig {
    @Bean
    public ServletRegistrationBean myServlet() {
        MyServlet myServlet = new MyServlet();  // //这个是我们写在Servlet包中的 MyServlet类

        return new ServletRegistrationBean(myServlet, "/my01", "/my02"); // 第一个参数是myServlet实列对象,第二个参数是映射的路径
    }


    @Bean
    public FilterRegistrationBean myFilter() {

        MyFilter myFilter = new MyFilter();  //这个是我们写在Servlet包中的 MyFilter类

        // 1.第一种

//      return new FilterRegistrationBean(myFilter,myServlet());  //第一个参数是MyFilter实列对象,第二个参数是ServletRegistrationBean对象 ->就是只拦截"/my01","/my02"这两个路径

        // 2.第二种

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);  // MyFilter实列对象
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my", "/css/*"));  // 拦截的路径
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener() {
        MyServletContextListener mySwervletContextListener = new MyServletContextListener(); //这个是我们写在Servlet包中的 MyServletContextListener类
        return new ServletListenerRegistrationBean(mySwervletContextListener); //  MyServletContextListener实列对象
    }
}

在这里插入图片描述

8.嵌入式Servlet容器

(1).切换嵌入式Servlet容器
  • 默认支持的webServer
    • Tomcat, Jetty, or Undertow
    • ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
ServletWebServerApplicationContext: 174private void createWebServer() {
		WebServer webServer = this.webServer;
		// 1.获取servletContext容器
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
		// 2.创建服务器容器
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			ServletWebServerFactory factory = getWebServerFactory();
			createWebServer.tag("factory", factory.getClass().toString());
		// ⭐⭐3.获取Tomcate容器
			this.webServer = factory.getWebServer(getSelfInitializer());
			createWebServer.end();
			getBeanFactory().registerSingleton("webServerGracefulShutdown",
					new WebServerGracefulShutdownLifecycle(this.webServer));
			getBeanFactory().registerSingleton("webServerStartStop",
					new WebServerStartStopLifecycle(this, this.webServer));
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}
  • 切换服务器

在这里插入图片描述

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • 原理
    • SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat

    • web应用会创建一个web版的ioc容器 ServletWebServerApplicationContext

    • ServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFactory(Servlet 的web服务器工厂—> Servlet 的web服务器)

    • SpringBoot底层默认有3个的WebServer工厂TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory

    • 底层直接会有一个自动配置类ServletWebServerFactoryAutoConfiguration类

    • ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfiguration(配置类)

    • ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有 TomcatServletWebServerFactory

    • TomcatServletWebServerFactory 创建出Tomcat服务器并启动TomcatWebServer 的构造器拥有初始化方法initialize—this.tomcat.start();
      在这里插入图片描述

    • 内嵌服务器,就是手动把启动服务器的代码调用(tomcat核心jar包存在)

(2).定制Servlet容器
  • 实现 WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>

    • 把配置文件的值和ServletWebServerFactory 进行绑定
  • 修改配置文件 server.xxx (第一种方式)
    在这里插入图片描述

  • 直接自定义 ConfigurableServletWebServerFactory (第二种)

因为我们项目已启动ServletWebServerFactory就会引导创建服务器,所以我们直接引入子类集大成者即可。

在这里插入图片描述

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

9.定制化原理

(1).定制化的常见方式
  • 修改配置文件
  • xxxxxCustomizer;
  • 编写自定义的配置类 xxxConfiguration;+ @Bean替换、增加容器中默认组件;视图解析器
  • Web应用 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能;+ @Bean给容器中再扩展一些组件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
  • @EnableWebMvc + WebMvcConfigurer + @Configuration —— @Bean 可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能
    • 原理

      • 1、WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页…
      • 2、一旦使用 @EnableWebMvc 、。会 @Import(DelegatingWebMvcConfiguration.class)
      • 3、DelegatingWebMvcConfiguration 的 作用,只保证SpringMVC最基本的使用
        • 把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效 (也就是说手动生效了)
        • 自动配置了一些非常底层的组件。RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
        • public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
      • 4、WebMvcAutoConfiguration 里面的配置要能生效 必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
        在这里插入图片描述
      • 5、@EnableWebMvc 导致了 WebMvcAutoConfiguration 没有生效。
        ● … …
(2).原理分析套路

场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties – 绑定配置文件项

3.数据访问

1.SQL

(1).数据源的自动配置-HikariDataSource

(1.1) 、导入JDBC场景
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>        

在这里插入图片描述

数据库驱动?
为什么导入JDBC场景,官方不导入驱动?官方不知道我们接下要操作什么数据库(MySQL.Orangle)。

数据库版本和驱动版本对应

SpringBoot默认版本:<mysql.version>8.0.22</mysql.version>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
<!--            <version>5.1.49</version>-->
        </dependency>
        
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、重新声明版本(maven的属性的就近优先原则)
    <properties>
        <java.version>1.8</java.version>
        <mysql.version>5.1.6</mysql.version>
    </properties>
(1.2) 、分析自动配置JDBC引入了啥

自动配置的类

  • DataSourceAutoConfiguration : 数据源的自动配置
    • 修改数据源相关的配置spring.datasource
    • 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
    • 底层配置好的连接池是:HikariDataSource
	@Configuration(proxyBeanMethods = false)
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
			DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration
  • DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置

  • JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud

    • 可以修改这个配置项@ConfigurationProperties(prefix = “spring.jdbc”) 来修改JdbcTemplate
      在这里插入图片描述

    • @Bean@Primary JdbcTemplate;容器中有这个组件(所以我们自己使用即可)
      在这里插入图片描述

  • JndiDataSourceAutoConfiguration: jndi的自动配置

  • XADataSourceAutoConfiguration: 分布式事务相关的

(1.3) 、修改配置项
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: ******
    driver-class-name: com.mysql.jdbc.Driver

配置数据源成功且各项信息正确
在这里插入图片描述
引入了JDBC和驱动 对数据源没有进行配置
在这里插入图片描述

2.使用Druid数据源

HikariDataSource: 是世界上性能最好的数据源。
Druid: 是世界上安全属性最高的数据源。

(1).druid官方github地址

德鲁伊帮助文档
https://github.com/alibaba/druid
整合第三方技术的两种方式

  • 自定义
  • 找starter

(2).自定义方式 (使用德鲁伊数据源)

(2.1)、创建数据源
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>
(2.2)、Spring时代配置德鲁伊数据源

1. dataSource

主要作用: 链接数据库和开启监控和防火墙等功能。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="maxActive" value="20" />
		<property name="initialSize" value="1" />
		<property name="maxWait" value="60000" />
		<property name="minIdle" value="1" />
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<property name="minEvictableIdleTimeMillis" value="300000" />
		<property name="testWhileIdle" value="true" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="poolPreparedStatements" value="true" />
		<property name="maxOpenPreparedStatements" value="20" />
    /**
     *
     * @return  主要用途链接数据库和配置数据库信息
     */

    // @ConditionalOnMissingBean(DataSource.class) ->当数据中没有的话,才会使用默认的数据源
    @Bean // 这里有数据源了所以使用我们配置的数据源
//    @ConfigurationProperties("spring.datasource")  // 因为德鲁伊的链接名和Hik的连接名一致,我们可以使用这个
    public DataSource dataSource() throws SQLException {

        DruidDataSource druidDataSource = new DruidDataSource();

        // 1.链接数据库
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/demo1");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("121788");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");

        // 2.打开SQL监控和防火墙
        druidDataSource.setFilters("stat,wall");

        System.out.println(druidDataSource.getClass());
        return druidDataSource;
    }

2. 配置 StatViewServle

StatViewServlet的用途包括:

  • 提供监控信息展示的html页面
  • 提供监控信息的JSON API
	<servlet>
		<servlet-name>DruidStatView</servlet-name>
		<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>DruidStatView</servlet-name>
		<url-pattern>/druid/*</url-pattern>
	</servlet-mapping>
    /**
     *
     * @return :  主要提供监控视图 和 JSON API的操作
     */

    @Bean
    public ServletRegistrationBean StatViewServlet(){
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
        return registrationBean;
    }

3.StatFilter

用于统计监控信息;如WEB监控URI监控

  <filter>
  	<filter-name>DruidWebStatFilter</filter-name>
  	<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
  	<init-param>
  		<param-name>exclusions</param-name>
  		<param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  	<filter-name>DruidWebStatFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
    /**
     *
     * @return  WebStatFilter用于采集web-URL关联监控的数据。
     */

    @Bean
    public FilterRegistrationBean WebStatFilter(){
        WebStatFilter webStatFilter = new WebStatFilter();
        FilterRegistrationBean<WebStatFilter> registrationBean = new FilterRegistrationBean<>(webStatFilter);
        registrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); //排除这些路径
        registrationBean.setUrlPatterns(Arrays.asList("/*"));   // 拦截这个路径

        return registrationBean;
    }
(2.3)、 配置类SpringBoot时代

在这里插入图片描述
在这里插入图片描述

package com.jsxs.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * @Author Jsxs
 * @Date 2023/7/9 14:28
 * @PackageName:com.jsxs.config
 * @ClassName: MyDuridConfig
 * @Description: TODO   德鲁伊数据源配置
 * @Version 1.0
 */

@Configuration
public class MyDruidConfig {
    /**
     *
     * @return  主要用途链接数据库和配置数据库信息
     */

    // @ConditionalOnMissingBean(DataSource.class) ->当数据中没有的话,才会使用默认的数据源
    @Bean // 这里有数据源了所以使用我们配置的数据源
//    @ConfigurationProperties("spring.datasource")  // 因为德鲁伊的链接名和Hik的连接名一致,我们可以使用这个
    public DataSource dataSource() throws SQLException {

        DruidDataSource druidDataSource = new DruidDataSource();

        // 1.链接数据库
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/demo1");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("121788");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");

        // 2.打开SQL监控和防火墙
        druidDataSource.setFilters("stat,wall");

        System.out.println(druidDataSource.getClass());
        return druidDataSource;
    }

    /**
     *
     * @return :  主要提供监控视图 和 JSON API的操作
     */

    @Bean
    public ServletRegistrationBean StatViewServlet(){
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");

        // 1.添加监控的密码和账户
        registrationBean.addInitParameter("loginUsername","1");
        registrationBean.addInitParameter("loginPassword","1");
        return registrationBean;
    }

    /**
     *
     * @return  WebStatFilter用于采集web-URL关联监控的数据。
     */

    @Bean
    public FilterRegistrationBean WebStatFilter(){
        WebStatFilter webStatFilter = new WebStatFilter();
        FilterRegistrationBean<WebStatFilter> registrationBean = new FilterRegistrationBean<>(webStatFilter);
        registrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); //排除这些路径
        registrationBean.setUrlPatterns(Arrays.asList("/*"));   // 拦截这个路径

        return registrationBean;
    }
}

在这里插入图片描述

(3).使用官方starter方式

(3.1)、引入druid-starter
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>
@Deprecated   // 标注已经过时,

在这里插入图片描述

(3.2)、分析自动配置

我们发现德鲁伊的包中自己带了自动配置的包

在这里插入图片描述
并且按需导入的方式进行导入 (即屏蔽掉以前SpringBoot默认的数据源)
在这里插入图片描述

  • 原来的项: spring.datasource 依然存在
  • 扩展配置项 spring.datasource.druid
  • DruidSpringAopConfiguration.class, 监控SpringBean的配置;配置项:spring.datasource.druid.aop-patterns
  • DruidStatViewServletConfiguration.class, 监控页的配置spring.datasource.druid.stat-view-servlet;默认开启
  • DruidWebStatFilterConfiguration.class, web监控配置spring.datasource.druid.web-stat-filter;默认开启
  • DruidFilterConfiguration.class 所有Druid自己filter的配置
    private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
    private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
    private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
    private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
    private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
    private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
    private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
    private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
(3.3)、示列
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

    druid:
      aop-patterns: com.atguigu.admin.*  #监控SpringBean
      filters: stat,wall     # 底层开启功能,stat(sql监控),wall(防火墙)

      stat-view-servlet:   # 配置监控页功能
        enabled: true
        login-username: admin
        login-password: admin
        resetEnable: false

      web-stat-filter:  # 监控web
        enabled: true
        urlPattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'


      filter:
        stat:    # 对上面filters里面的stat的详细配置
          slow-sql-millis: 1000
          logSlowSql: true
          enabled: true
        wall:
          enabled: true
          config:
            drop-table-allow: false

https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

Druid 高级配置属性列表…

3.Mybatis 操作数据库

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

在这里插入图片描述

(1).原始(Spring) - 配置模式

  • 全局配置文件
  • SqlSessionFactory: 自动配置好了
  • SqlSession:自动配置了 SqlSessionTemplate 组合了SqlSession
  • @Import(AutoConfiguredMapperScannerRegistrar.class);
  • Mapper: 只要我们写的操作MyBatis的接口标准了 @Mapper 就会被自动扫描进来
@EnableConfigurationProperties(MybatisProperties.class)MyBatis配置项绑定类。
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration{}


@ConfigurationProperties(prefix = "mybatis")  前缀
public class MybatisProperties

MybatisX 插件。 这个插件可以实现映射跳转

在这里插入图片描述
命名空间指向的是 接口。 返回值结果是 实体类 。
在这里插入图片描述

(2).非注解的配置模式

application.yaml

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml  #指定mybatis的核心文件位置
  mapper-locations: classpath:mybatis/mapper/*.xml #指定mybatis的映射文件位置

在这里插入图片描述

实体类

package com.jsxs.bean;

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

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.bean
 * @ClassName: Admin
 * @Description: TODO
 * @Version 1.0
 */

@Data
@AllArgsConstructor
@NoArgsConstructor

public class Admin {
    Integer id;
    String name;
    String password;
}

mapper层接口: mapper注解是为了让mybatis发现,respoity目的是为了让

package com.jsxs.mapper;

import com.jsxs.bean.Admin;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.mapper
 * @ClassName: AdminMapper
 * @Description: TODO
 * @Version 1.0
 */

@Mapper
@Repository
public interface AdminMapper {

    // 1.查询所有的用户
    public List<Admin> allAdmin();
}

mybatis.xml映射SQL文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jsxs.mapper.AdminMapper">
    <select id="allAdmin" resultType="com.jsxs.bean.Admin">
        select *from admin;
    </select>
</mapper>

接口

package com.jsxs.service;

import com.jsxs.bean.Admin;
import com.jsxs.mapper.AdminMapper;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 13:39
 * @PackageName:com.jsxs.service
 * @ClassName: AdminService
 * @Description: TODO
 * @Version 1.0
 */


public interface AdminService {

    public List<Admin> allAdmin();
}

接口实现类

package com.jsxs.service.impl;

import com.jsxs.bean.Admin;
import com.jsxs.mapper.AdminMapper;
import com.jsxs.service.AdminService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 13:44
 * @PackageName:com.jsxs.service.impl
 * @ClassName: AdminServiceImpl
 * @Description: TODO
 * @Version 1.0
 */

@Service
public class AdminServiceImpl implements AdminService {

    @Resource
    AdminMapper adminMapper;


    @Override
    public List<Admin> allAdmin() {
        return  adminMapper.allAdmin();
    }
}

测试实验

package com.jsxs.service.impl;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @Author Jsxs
 * @Date 2023/7/17 13:47
 * @PackageName:com.jsxs.service.impl
 * @ClassName: AdminServiceImplTest
 * @Description: TODO
 * @Version 1.0
 */
@SpringBootTest
class AdminServiceImplTest {

    @Resource
    AdminServiceImpl adminService;

    @Test
    void test(){
        System.out.println(adminService.allAdmin());
    }
}

Mybatis 默认是不支持我们的数据库字段的驼峰命名的。假如说数据库的字段名的间隔以下划线分割,那么实体类应该写成驼峰命名。那么如果我们没有配置mybatis使用驼峰命名那么我们就会查询到的数据为null。

第一种: 使用全局配置文件配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="multipleResultSetsEnabled" value="true"/>
    </settings>
</configuration>

第二种: 使用application.yaml

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml  #指定mybatis的核心文件位置
  mapper-locations: classpath:mybatis/mapper/*.xml #指定mybatis的映射文件位置
  configuration:
    multiple-result-sets-enabled: true  ⭐

注意: 全局配置文件(mybatis-config)与application.yaml中只能存在一个对mybatis进行配置。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

基本步骤:

  • 导入mybatis官方starter
  • 编写mapper接口。标准@Mapper注解
  • 编写sql映射文件并绑定mapper接口
  • 在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration)

(3).纯注解的配置模式

如果我们使用注解开发的话,我们就可以省略掉xml映射文件而不是xml全局配置文件(mybatis-config.xml)

package com.jsxs.mapper;

import com.jsxs.bean.Admin;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.mapper
 * @ClassName: AdminMapper
 * @Description: TODO
 * @Version 1.0
 */

@Mapper
@Repository
public interface AdminMapper {

    // 1.查询所有的用户
    public List<Admin> allAdmin();

    // 2.根据id进行查找数据  ⭐⭐
    @Select("select *from admin where id = #{id}")
    public Admin getAdminById(Long id);
}

Service 接口实现类

package com.jsxs.service;

import com.jsxs.bean.Admin;
import com.jsxs.mapper.AdminMapper;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 13:39
 * @PackageName:com.jsxs.service
 * @ClassName: AdminService
 * @Description: TODO
 * @Version 1.0
 */


public interface AdminService {

    public List<Admin> allAdmin();

    public Admin getAdminById(Long id);
}

Service接口

package com.jsxs.service.impl;

import com.jsxs.bean.Admin;
import com.jsxs.mapper.AdminMapper;
import com.jsxs.service.AdminService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 13:44
 * @PackageName:com.jsxs.service.impl
 * @ClassName: AdminServiceImpl
 * @Description: TODO
 * @Version 1.0
 */

@Service
public class AdminServiceImpl implements AdminService {

    @Resource
    AdminMapper adminMapper;


    @Override
    public List<Admin> allAdmin() {
        return  adminMapper.allAdmin();
    }

    @Override
    public Admin getAdminById(Long id) {
        return adminMapper.getAdminById(id);
    }
}

测试结果

package com.jsxs.service.impl;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @Author Jsxs
 * @Date 2023/7/17 13:47
 * @PackageName:com.jsxs.service.impl
 * @ClassName: AdminServiceImplTest
 * @Description: TODO
 * @Version 1.0
 */
@SpringBootTest
class AdminServiceImplTest {

    @Resource
    AdminServiceImpl adminService;

    @Test
    void test(){
        System.out.println(adminService.allAdmin());
        System.out.println(adminService.getAdminById(1L));
    }
}

在这里插入图片描述

(4).非注解和纯注解可以混合使用

我们只需要各自写各自的即可,即非注解的要写 xml映射文件和全局配置文件;非注解的不需要xml映射文件但需要全局文件

package com.jsxs.mapper;

import com.jsxs.bean.Admin;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.mapper
 * @ClassName: AdminMapper
 * @Description: TODO
 * @Version 1.0
 */

@Mapper
@Repository
public interface AdminMapper {

    // 1.查询所有的用户
    public List<Admin> allAdmin();

    // 2.根据id进行查找数据
    @Select("select *from admin where id = #{id}")
    public Admin getAdminById(Long id);

    // 3.xinz
    @Insert("insert into admin(name,password) values(#{name},#{password})")
    @Options(useGeneratedKeys = true,keyColumn = "id")  // 指定主键是那个?
    public int add(String name,String password);
}

在这里插入图片描述

最佳实践

  • 引入mybatis-starter
  • 配置application.yaml中,指定mapper-location位置即可;config-location:核心配置文件就不需要了。核心配置文件最好直接再application.yaml中使用。
  • 编写Mapper接口并标注@Mapper注解
  • 简单方法直接注解方式
  • 复杂方法编写mapper.xml进行绑定映射
  • @MapperScan("com.atguigu.admin.mapper") 简化,其他的Mapper接口就可以不用标注@Mapper注解

4.Mybatis-Plus 操作数据库

(1).什么是MybatisPlus

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis plus 官网 https://baomidou.com/
建议安装 MybatisX 插件

(2).整合Mybatisplus

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

在这里插入图片描述
帮助我们自动引入了JDBC和Mybatis的包,所以我们不需要再引入JDBC和Mybatis了

自动配置

  • MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对mybatis-plus的定制
  • SqlSessionFactory 自动配置好。底层是容器中默认的数据源
  • mapperLocations 自动配置好的。有默认值 (3.1.2下才有默认值)classpath*:/mapper/**/*.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下
  • 容器中也自动配置好了 SqlSessionTemplate
  • @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan("com.atguigu.admin.mapper") 批量扫描就行

优点:

  • 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力。
  1. 数据库层继承的是: BaseMapper
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml  #3.1.2版本之后有默认的值了。

实体类

为什么说:我们这里写类名为 Admin ; 然后Mybatisplus是怎么找到数据库的具体表的? 因为默认有一个注解 @TableName(“xxx”) xxx默认是写的是类名,就会通过这个类名去数据库中寻找。如果表名更改了,那么我们可以通过这个注解更改映射的关系。

package com.jsxs.bean;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.bean
 * @ClassName: Admin
 * @Description: TODO
 * @Version 1.0
 */

@Data
@AllArgsConstructor
@NoArgsConstructor

public class Admin {
    @TableField(exist = true)  // 假如我们需要临时的属性,但这个属性不存在我们可以再上面添加一个注解 @TableField(exist = false)表示在表中不存在
    Integer id;
    String name;
    String password;
}

Mapper接口-> 需要继承一个接口

package com.jsxs.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jsxs.bean.Admin;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.mapper
 * @ClassName: AdminMapper
 * @Description: TODO  继承接口之后我们不需要再编写 CRUD 就自带的已经存在了
 * @Version 1.0
 */

@Mapper
@Repository
public interface AdminMapper extends BaseMapper<Admin> {   // 需要继承BaseMapper,因为这个父类有很多已经分装好的方法


}

测试实现

package com.jsxs.mapper;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @Author Jsxs
 * @Date 2023/7/18 11:03
 * @PackageName:com.jsxs.mapper
 * @ClassName: AdminMapperTest
 * @Description: TODO
 * @Version 1.0
 */
@SpringBootTest
class AdminMapperTest {

    @Resource
    AdminMapper adminMapper;

    @Test
    void test(){
        System.out.println(adminMapper.selectById(1));
    }
}

在这里插入图片描述

  1. 业务层接口继承的是 IService<操作的实体类名> ; 业务层实现类继承业务层的实现类要继承ServiceImpl<要实现表的BaseMapper,操作表对应的实体类名> 且 要实现业务层的接口

实体类

package com.jsxs.bean;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.bean
 * @ClassName: Admin
 * @Description: TODO
 * @Version 1.0
 */

@Data
@AllArgsConstructor
@NoArgsConstructor

public class Admin {
    @TableField(exist = true)  // 假如我们需要临时的属性,但这个属性不存在我们可以再上面添加一个注解 @TableField(exist = false)表示在表中不存在
    Integer id;
    String name;
    String password;
}

Mapper接口

package com.jsxs.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jsxs.bean.Admin;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Author Jsxs
 * @Date 2023/7/17 11:01
 * @PackageName:com.jsxs.mapper
 * @ClassName: AdminMapper
 * @Description: TODO  继承接口之后我们不需要再编写 CRUD 就自带的已经存在了
 * @Version 1.0
 */

@Mapper
@Repository
public interface AdminMapper extends BaseMapper<Admin> {   // 需要继承BaseMapper,因为这个父类有很多已经分装好的方法


}

Service 接口

package com.jsxs.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.jsxs.bean.Admin;

/**
 * @Author Jsxs
 * @Date 2023/7/18 11:26
 * @PackageName:com.jsxs.service
 * @ClassName: AdminService
 * @Description: TODO  我们业务层的接口只需要实现IService<Admin>
 * @Version 1.0
 */

public interface AdminService extends IService<Admin> {
}

业务层实现类接口:

package com.jsxs.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jsxs.bean.Admin;
import com.jsxs.mapper.AdminMapper;
import com.jsxs.service.AdminService;
import org.springframework.stereotype.Service;

import java.io.Serializable;

/**
 * @Author Jsxs
 * @Date 2023/7/18 11:27
 * @PackageName:com.jsxs.service.impl
 * @ClassName: AdminServiceImpl
 * @Description: TODO  业务层的实现类要继承ServiceImpl<要实现表的BaseMapper,操作表对应的实体类名> 且 要实现业务层的接口
 * @Version 1.0
 */
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper,Admin> implements AdminService {

}

测试类

package com.jsxs.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jsxs.bean.Admin;
import com.jsxs.service.AdminService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @Author Jsxs
 * @Date 2023/7/18 11:44
 * @PackageName:com.jsxs.service.impl
 * @ClassName: AdminServiceImplTest
 * @Description: TODO
 * @Version 1.0
 */

@SpringBootTest
@Slf4j
class AdminServiceImplTest {

    @Resource
    AdminService adminService;

    @Test
    void test(){
        System.out.println(adminService.getById(1));

        Page<Admin> page = new Page<>(1, 2); //当前页(初始页为1),一页几条

        IPage<Admin> iPage = adminService.page(page, null);  // Page分页的实列
        log.info("全部数据为: {}",iPage.getRecords()); // 分页遍历的话,遍历这个
        log.info("当前业为: {}",iPage.getCurrent());
        log.info("总多少页: {}",iPage.getPages());
        log.info("总多少条: {}",iPage.getTotal());
    }

}

假如说查询到的数据都为0,那么就是没有配置分页插件

package com.jsxs.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author Jsxs
 * @Date 2023/7/18 13:05
 * @PackageName:com.jsxs.config
 * @ClassName: MybatisPlusConfig
 * @Description: TODO
 * @Version 1.0
 */
@Configuration
public class MybatisPlusConfig {
    //  分页插件
    @Bean
    public PaginationInterceptor paginationinterceptor(){
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        return paginationInterceptor;
    }
}

在这里插入图片描述

为何Controller中调用接口就能实现接口实现类里的方法?

  1. 如何在删除之后仍然在原来的页面 ?

我们只需要在前端的删除按钮上绑定上当前页的页码,然后在后端进行页码的重定向跳转。
前端的操作:
在这里插入图片描述
后端的操作
在这里插入图片描述

5.整合Redis 非SQL数据库

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

(1).Redis 自动配置

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

在这里插入图片描述
自动配置:

  • RedisAutoConfiguration 自动配置类。RedisProperties 属性类 --> spring.redis.xxx是对redis的配置
  • 连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration (默认配置好了客户端)
  • 自动注入了RedisTemplate<Object, Object> : xxxTemplate;
  • 自动注入了StringRedisTemplate;k:v都是String
  • key:value
  • 底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis

redis环境搭建

如果我们使用window的话,我们在启动redis服务端的时候需要指定使用哪个配置 redis.windows-service.conf redis.windows.conf。在这里我们使用redis.windows.conf设定了密码。

两种打开方式:

  1. 键入“cmd”切到安装目录后输出redis-server.exe redis.windows.conf,回车,就可以了。

  2. 在redis安装目录下新建文件startup.bat后,右击“编辑”,或者先用记事本建立该文件,再把扩展名改一下,文件里面写上:redis-server.exe redis.windows.conf。保存,以后再运行就直接运行这个文件,不要再直接运行redis-server.exe了,就可以了

在这里插入图片描述
在这里插入图片描述

(2).RedisTemplate与Lettuce

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: xxxxx
package com.jsxs.mapper;

import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import javax.annotation.Resource;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @Author Jsxs
 * @Date 2023/7/18 11:03
 * @PackageName:com.jsxs.mapper
 * @ClassName: AdminMapperTest
 * @Description: TODO
 * @Version 1.0
 */
@SpringBootTest
class AdminMapperTest {


    @Resource
    RedisTemplate redisTemplate;

    @Test
    void test2(){
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();

        opsForValue.set(JSON.toJSONString("hello5"),JSON.toJSONString("hello5"));
        System.out.println(opsForValue.get("hello5"));

    }
}

在这里插入图片描述

需要进行序列化处理

没有对 redisTemplate 进行序列化处理,java 代码会将 key 转化成对应的十六进制的数值进行操作.

注意事项: 我们不管通过 Java代码/Redis客户端 进行设置值还是获取值,我们都需要对其进行JSON(如果不是java会获取不到值)处理并且序列化(序列化可以避免默认的16进制)

package com.jsxs.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.net.UnknownHostException;

/**
 * @Author Jsxs
 * @Date 2023/7/20 13:56
 * @PackageName:com.jsxs.config
 * @ClassName: RedisConfig
 * @Description: TODO
 * @Version 1.0
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        // 将template 泛型设置为 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate();
        // 连接工厂,不必修改
        template.setConnectionFactory(redisConnectionFactory);
        /*
         * 序列化设置
         */
        // key、hash的key 采用 String序列化方式
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // value、hash的value 采用 Jackson 序列化方式
        template.setValueSerializer(RedisSerializer.json());
        template.setHashValueSerializer(RedisSerializer.json());
        template.afterPropertiesSet();

        return template;
    }
}

假如说key键值对的值是用的JOSN字符串,那么Java端获取的值就是JSON字符串。如果键值对的值用的是非JSON字符串,那么Java后端获取的值就是非JSON字符串。
在这里插入图片描述
总结: 以后开发全部使用JSON字符串

(3).切换至jedis

客户端类型有: lettuce 和 jedis 这两种。
在这里插入图片描述

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--        导入jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
  redis:
    host: 127.0.0.1
    port: 6379
    password: 121788
    client-type: jedis  #指定客户端的类型为jedis
    @Test
    void test3(){
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.auth("121788");
        System.out.println(jedis.ping());
        jedis.set("username1","123456");
        System.out.println(jedis.get("username1"));
    }

总结: 不管是 Letture 还是Jedis客户端,这两个客户端都需要用到序列化和 JSON

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/794521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一遍过JavaSE基础知识

文章目录 前言安装Java Development Kit (JDK)安装jdk配置开发环境验证是否安装配置成功 编写第一个Java程序hello world运行Java程序的流程 数据类型和变量数据类型变量 程序逻辑控制条件语句循环语句跳转语句 数组声明和创建数组访问数组元素数组长度遍历数组多维数组 面向对…

OpenAI大模型生态与ChatGLM ||学习大模型我们需要掌握些什么?

首先我们来看OpenAI系列模型&#xff1a; OpenAI 并不只有一个模型&#xff0c;而是提供了涵盖文本、码、对话、语音、图像领域的一系列模型。 语言类大模型 其中语言类大模型包括: GPT-3、GPT-3.5、GPT-4系列模型。 并且&#xff0c;OpenAI在训练GPT-3的同时训练了参数不同、…

Disulfo ICG Amine,二磺酸吲哚菁绿氨基,应用于多种生物大分子以及药物的检测

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ Disulfo-ICG-Amine试剂 | 基础知识概述&#xff08;部分&#xff09;: 中文名称&#xff1a;二磺酸吲哚菁绿氨基 英文名称&#xff1a;Disulfo-ICG-Amine&#xff0c;Disulfo ICG NH2 CAS号&#xff1a;N/A 分子式&#xf…

【Gray Hat Python】构建自己的windows调试器

环境准备 windows10 64bit python3.7 64bit 打开可执行文件&#xff0c;创建进程 定义变量 以下代码用 ctypes 定义了调用 windows API 需要的结构 my_debugger_define.py import ctypesWORD ctypes.c_ushort DWORD ctypes.c_ulong LPBYTE ctypes.POINTER(ctypes.c_uby…

微软5年敏捷转型策略:成功的16个关键

许多管理者怀疑规模化敏捷组织是否可行。微软成功地实现了为期五年的大规模敏捷转型表明&#xff0c;答案是肯定的。微软已不是一艘巨型军舰&#xff0c;而更像是同步行进的快艇组成的舰队&#xff1a;数百个团队中以协调的方式进行敏捷和Scrum。依赖关系如何处理&#xff1f;团…

常用自动化测试工具有哪些?

1、Appium AppUI自动化测试 Appium 是一个移动端自动化测试开源工具&#xff0c;支持iOS 和Android 平台&#xff0c;支持Python、Java 等语言&#xff0c;即同一套Java 或Python 脚本可以同时运行在iOS 和Android平台&#xff0c;Appium 是一个C/S 架构&#xff0c;核心是一…

Java Swing(C/S模式)特效雨滴系统界面

调节不同参数&#xff0c;生成不同特效: ------------------界面截图--------------------- package org.jd.data.netty.big.window.chat.frame.ui.controller.center; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.*;/*** 设计模式: 单例模式* * 自定义线…

Docker 数据管理及网络通信 Dockerfile

一、Docker 的数据管理 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷&#xff08;Data Volumes&#xff09;和数据卷容器&#xff08;DataVolumes Containers&#xff09;。 1、数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目…

华为OD机试真题 Java 实现【阿里巴巴找黄金宝箱(II)】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、题目关键点&#xff1a;2、大白话的意思就是3、比如4、思路这不就来了 五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于…

STM32单片机蓝牙APP语音识别智能记忆汽车按摩座椅加热通风儿童座椅

实践制作DIY- GC00160---智能记忆汽车按摩座椅 一、功能说明&#xff1a; 基于STM32单片机设计---智能记忆汽车按摩座椅 二、功能说明&#xff1a; 电路组成&#xff1a;STM32F103CXT6最小系统LD3322语音识别模块OLED显示3个ULN2003步进电机&#xff08;分别对应 前后距离、座…

小学期笔记——天天酷跑1

文件快照&#xff08;File snapshot&#xff09;通常是指对文件系统中某个特定时间点的文件或文件夹的快照或副本。它记录了文件或文件夹在某一时刻的状态&#xff0c;包括文件的内容、属性、权限、位置等信息。 文件快照通常用于数据备份、恢复和版本控制等目的。通过捕捉文件…

Stable-Diffusion-Webui部署SDXL0.9报错参数shape不匹配解决

问题 已经在model/stable-diffusion文件夹下放进去了sdxl0.9的safetensor文件&#xff0c;但是在切换model的时候&#xff0c;会报错model的shape不一致。 解决方法 git pullupdate一些web-ui项目就可以&#xff0c;因为当前项目太老了&#xff0c;没有使用最新的版本。

Windows10 任务栏图标的控制

文章目录 前言一、任务栏系统图标设置二、任务栏应用软件图标设置总结前言 在windows系统中,有一个常用功能,那就是在任务栏上图标化加载一些应用。为特殊目的,我们可以把一些常用软件启动后以图标形式摆放任务栏的右下角(例如QQ,微信)。不同的Window版本有不同的任务栏…

最受欢迎的12个Python开源框架,还没用过你就OUT了!!!

今天给大家带来了12个在GitHub等开源网站中最受欢迎的Python开源框架。如果你正在学习python&#xff0c;那么这12个开源框架&#xff0c;千万别错过&#xff0c;这些框架包括事件I/O&#xff0c;OLAP&#xff0c;Web开发&#xff0c;高性能网络通信&#xff0c;测试&#xff0…

【Redis】如何实现一个合格的分布式锁

文章目录 参考1、概述2、Redis粗糙实现3、遗留问题3.1、误删情况3.2、原子性保证3.3、超时自动解决3.4、总结 4、Redis实现优缺5、集群问题5.1、主从集群5.2、集群脑裂 6、RedLock7、Redisson7.1、简单实现7.2、看门狗机制 参考 Redisson实现Redis分布式锁的N种姿势 (qq.com)小…

第六章 复合查询

第六章 复合查询 一、前言二、笛卡尔积三、多表查询1、多表查询的理解2、笛卡尔积与多表拼接3、多表查询示例&#xff08;1&#xff09;显示雇员名、雇员工资以及所在部门的名字&#xff08;2&#xff09;显示部门号为10的部门名&#xff0c;员工名和工资&#xff08;3&#xf…

力扣热门100题之缺失的第一个正数【困难】

题目描述 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1…

Linux学习之while循环和until循环

while while的格式如下&#xff1a; while 条件表达式 do指令集 done若是条件表达式为真&#xff0c;那么才能执行do和done之间的指令集。若是第一次都不符合条件&#xff0c;就不会执行指令集。每次循环都会判断条件表达式&#xff0c;只要不符合&#xff0c;就会退出循环。…

【前端学JAVA】java的基础语法

theme: cyanosis 作为一个前端程序员&#xff0c;其发展前途是远不及后端程序员的。因此&#xff0c;只有了解后端&#xff0c;才能让自己更加具备核心竞争力。本系列教程将以一个前端程序员的角度快速学习JAVA。 新建项目 开发JAVA程序&#xff0c;我们第一步是使用IDEA新建…

VAE-根据李宏毅视频总结的最通俗理解

1.VAE的直观理解 先简单了解一下自编码器&#xff0c;也就是常说的Auto-Encoder。Auto-Encoder包括一个编码器&#xff08;Encoder&#xff09;和一个解码器&#xff08;Decoder&#xff09;。其结构如下&#xff1a; 自编码器是一种先把输入数据压缩为某种编码, 后仅通过该编…