springboot

第十章:SpringBoot

第1节:概述

1.1 微服务 SpringCloud
要搭建一个微服务,运维和部署都变得非常复杂,spring提供了一套解决方案:
springBoot:快速构建单个服务;
springcloud:是一系列有序框架的集合,其主要的设施有,服务发现与注册,配置中心,消息总线,负载均衡,断路器,数据监控等,通过Spring Boot的方式,可以实现一键启动,和部署。
Spring cloud data flow: 为基于微服务的分布式流处理和批处理数据通道提供了一系列模型和最佳实践.

在这里插入图片描述

1.2 简介
官方网站: 
https://spring.io/projects/spring-boot

Spring-Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。个人理解来说Spring-Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring-Boot整合了其他相关联框架。

总结: springboot 全新框架,底层是spring springmvc, 并且提供了很多关联框架。   简化spring项目的开发。
1.3 优势
1、快速构建项目。
2、对主流开发框架的无配置集成。 内部集成了优秀的框架
3、项目可独立运行,无须外部依赖Servlet容器。内置了 tomcat jetty
4、提供运行时的应用监控。
5、极大的提高了开发、部署效率。
6、与云计算的天然集成。 
云原生~ 
1.4 核心功能
1、独立运行Spring项目
	Spring Boot 可以以jar包形式独立运行,运行一个Spring Boot项目只需要通过java -jar xx.jar来运行。
2、内嵌servlet容器
    Spring Boot可以选择内嵌Tomcat、jetty或者Undertow,这样我们无须以war包形式部署项目。
3、提供starter简化Maven配置[重点]
    spring提供了一系列的start pom来简化Maven的依赖加载,例如,当你使用了spring-boot-starter-web,会自动加入如图5-1所示的依赖包。
4、自动装配Spring【重点】
    Spring Boot会根据在类路径中的jar包,类、为jar包里面的类自动配置Bean,这样会极大地减少我们要使用的配置。当然,Spring Boot只考虑大多数的开发场景,并不是所有的场景,若在实际开发中我们需要配置Bean,而Spring Boot灭有提供支持,则可以自定义自动配置。
5、准生产的应用监控
    Spring Boot提供基于http ssh telnet对运行时的项目进行监控。
6、无代码生产和xml配置  
    Spring Boot不是借助与代码生成来实现的,而是通过条件注解来实现的,这是Spring4.x提供的新特性。

第2节:入门程序

2.1 创建工程导入依赖
  <!--继承指定的父模块pom.xml文件:对项目进行整体的管理维护,如统一版本管理-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
    </parent>
    <!--spring提供的web项目启动器模块-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter 相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器 。
2.2 编写主程序
2.2.1 项目结构

在这里插入图片描述

2.2.2 主程序代码
@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        /**
         * 启动springboot
         */
        SpringApplication.run(Example.class,args);
    }
}
@SpringBootApplication: 注解说明这个类是SpringBoot的主配置类,SpringBoot 就应该运行这个类的main方法来启动SpringBoot应用;并将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器; 
2.3 编写Controller代码
@Controller
public class TestController {

    @RequestMapping("/hello")
    @ResponseBody
    public String helloWorld(){
        return "Hello world!";
    }
}
2.4 运行主程序进行测试

在这里插入图片描述

第3节:简化部署

3.1 添加maven插件
   <!--执行maven命令插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
3.2 执行项目打包

在这里插入图片描述

执行打包命令后,会在target目录下生成项目对应的jar包。
3.3 执行命令运行程序

在这里插入图片描述

第4节:快速创建项目

4.1 创建Spring Boot项目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2 项目结构解析

在这里插入图片描述

1、java文件夹目录结构中自动创建好指定包和Spring Boot启动主程序SpringbootApplication.class2、resources文件夹中目录结构 
static:保存所有的静态资源; js css images; 
templates:保存所有的模板页面,(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面),可以使用模板引擎(freemarker、thymeleaf); 
application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
该配置文件能够被自动读取。 

第5节:配置文件

5.1 配置文件的作用及规范
Spring Boot使用一个全局的配置文件,配置文件名是固定的;默认使用以下两种格式: 
•application.properties 
•application.yml 
配置文件的作用:修改SpringBoot自动配置的默认值;Spring Boot启动时会根据配置文件自动注册相关的应用组件;
5.2 yaml配置文件
YAML:以数据为中心,比json、xml等更适合做配置文件;
5.2.1 基本语法
– 使用缩进表示层级关系
– 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可 – 大小写敏感
- 键值对中间必须要有空格k:(空格)v
5.2.2 值的写法
YAML 支持的三种数据结构:
1. 字面量:普通的值(数字,字符串,布尔)
server: 
  port: 8081
注意:字符串默认不用加上单引号或者双引号; 
双引号:特殊符号表示转义符本身;
name: "zhangsan \n lisi":输出;zhangsan 换行 lisi
单引号;特殊字符就表示字符本身;
name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
2. 对象、Map(属性和值)(键值对)
person:
  name: zhangsan
  age: 12
另一种行内写法:
person: {name: zhangsan,age: 12}
3. 数组(List,Set)
hobbies:
 - singing
 - dancing
 - running
用-(空格)值表示数组中的一个元素
另一种行内写法:
hobbies: [singing,dancing,running]

yaml参考文档

5.2.3 配置文件值的注入
1. 构建bean对象
//只有spring容器中的对象才能自动进行数据绑定
@Component
//将本类中的所有属性和配置文件中相关的配置进行绑定;
// prefix指定要绑定的配置文件中的属性前缀;
@ConfigurationProperties(prefix = "person")
public class Person {
    private String pname;
    private int age;
    private boolean success;
    private Date birth;

    private Car car;
    private Map<String,Object> maps;
    private List<Object> lists;
}

总结: @ConfigurationProperties(prefix = "person") + @Component 结合使用
         或是
 @ConfigurationProperties(prefix = "person")+ @EnableConfigurationProperties(Person.class) 注解共同使用
    注:@EnableConfigurationProperties(Person.class)加在启动类或测试类上
           @ConfigurationProperties(prefix = "person") 加在要获取数据的类上 
2. 构建配置文件
person:
  pname: zhangsan
  age: 12
  birth: 2020/12/12
  success: true
  car:
    cname: 奔驰
    cprice: 200.0
  lists: [唱歌,跳舞]
  maps: {key1: v1,key2: v2}
3. 执行单元测试
@SpringBootTest
class SpringbootApplicationTests {
    @Autowired
    private Person person;
    @Test
    void contextLoads() {
        System.out.println(person);
    }

}
4. 引入配置文件处理器插件
<!--作用:编辑配置文件时会有相关提示信息--> 
<dependency>
            <groupId> org.springframework.boot </groupId>
            <artifactId> spring-boot-configuration-processor </artifactId>
            <optional> true </optional>
 </dependency>
总结: 
(1)解决实体当中报错问题
(2)yml配置文件当中, 可以根据前缀提示~~
5.3 properties配置文件
5.3.1 properties语法
以KEY=VALue键值对的方式设置值
1. 字面量:普通的值(数字,字符串,布尔)
name=张三
2. 对象、Map(属性和值)(键值对)
person.name=张三
person.age=12
maps.key1=value1
maps.key2=value2
3. 数组(List,Set)
hobbies=singing,dancing,running
5.3.2 配置文件值的注入
1. 构建bean对象
//只有spring容器中的对象才能自动进行数据绑定
@Component
//将本类中的所有属性和配置文件中相关的配置进行绑定;
// prefix指定要绑定的配置文件中的属性前缀;
@ConfigurationProperties(prefix = "person")
public class Person {
    private String pname;
    private int age;
    private boolean success;
    private Date birth;

    private Car car;
    private Map<String,Object> maps;
    private List<Object> lists;
}
2. 构建配置文件
person.pname=张三
person.age=12
person.birth=2019/12/12
person.success=true
person.car.cname=宝马
person.car.cprice=100.0
person.lists=唱歌,跳舞,跑步
person.maps.k1=value1
person.maps.k2=value2
3. 执行单元测试
@SpringBootTest
class SpringbootApplicationTests {
    @Autowired
    private Person person;
    @Test
    void contextLoads() {
        System.out.println(person);
    }
}
5.4 说明
5.4.1 注解区别
@ConfigurationProperties注解和@Value注解的区别
1@ConfigurationProperties注解用于根据配置文件批量注入值,springboot默认配置文件application.yml/application.properties;
2@Value注解用于根据配置文件单个注入值;
区别@ConfigurationProperties@Value
SpEL不支持支持:@Value(“${jdbc.url}”)
复杂类型封装支持不支持
案例:
@Component
//@ConfigurationProperties(prefix = "person")
public class Person {
    @Value("${person.pname}")
    private String pname;
    @Value("#{12*2}")
    private int age;
    private boolean success;
    private Date birth;
    private Car car;
    private Map<String,Object> map;
    private List<Object> hobbies;
}
5.4.2 配置文件中的占位符
1、springboot全局配置文件中可以使用随机数
		${random.value}、${random.int}、${random.long}
		${random.int(10)}、${random.int[1024,65536]}
2、springboot全局配置文件中可以使用占位符
   通过${属性名}获取已经在配置文件中加载过的属性值;
   通过${属性名:默认值}来指定找不到属性时的默认值;
案例:
person.pname=张三
person.age=${random.int}
person.success=true
person.birth=2012/12/12
person.car.cname=${car.cname:奔驰}
person.car.cprice=200.0
person.map.key1=value1
person.map.key2=value2
person.hobbies=唱歌,跳舞,跑步
5.4.3 多环境支持
Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境;
1、多文件多环境形式:
		格式:application-{profile}.properties/yml
例如:可以在项目中创建如下主配置文件:application-dev.properties、application-test.properties、application-prod.properties、application.properties,默认使用application.properties,可以通过配置spring.profiles.active=profile指定使用某个环境的配置文件。
2、yaml支持单文件多环境形式:
spring:
  profiles:
    active: test    
    
    
    
    
---
spring:
  profiles: dev     #这种方式在新版本中过时,建议使用下面的方式
server:
  port: 8081
  
 spring:
   config:
     activate:
        on-profile: dev   #新版本中的方式
    server:
      port: 8081 
  
---
spring:
  profiles: test
server:
  port: 8082
通过---来区分不同的profile环境。
3、激活方式:
  在配置文件中指定 spring.profiles.active=dev
  命令行  java -jar springboot-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
  jvm参数 -Dspring.profiles.active=dev

在这里插入图片描述

5.4.4 配置文件加载位置
springboot默认会从以下位置加载配置文件:application.properties/application.yml
项目所在磁盘路径file:./config/ 
项目所在磁盘路径file:./ 
项目类路径classpath:/config/ 
项目类路径classpath:/
优先级由高到底,高优先级的配置会覆盖低优先级的配置;SpringBoot会从这四个位置全部加载主配置文件,互补配置;
如果要加载其他位置的配置文件,可以通过--spring.config.location(只能加到命令行)来指定要加载的配置文件的位置。

第6节:Spring Boot的web开发

6.1 静态资源的处理
springboot启动时会根据配置文件自动配置相应场景的组件xxxAutoConfiguration,web项目启动时会初始化WebMvcAutoConfiguration组件处理请求相关的操作,其中有默认处理静态资源的方法:
@Override
		protected void addResourceHandlers(ResourceHandlerRegistry registry) {
			super.addResourceHandlers(registry);
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			ServletContext servletContext = getServletContext();
			addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
			addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
				registration.addResourceLocations(this.resourceProperties.getStaticLocations());
				if (servletContext != null) {
					registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
				}
			});
		}

从该方法中可以发现:
1、默认情况下:
(1)匹配/webjars/** 的请求,都去 classpath:/META-INF/resources/webjars/ 找资源;
<script src ="/webjars/jquery/3.6.0/jquery.js">
(2)匹配 "/**" 的请求,都去(静态资源的文件夹)找映射,静态资源文件夹路径如下:
"classpath:/META‐INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/" 
"/":当前项目的根路径
2、自定义配置静态资源路径:
spring.web.resources.static-locations=自定义路径
6.1.1 webjars的使用
我们可以通过webjars以jar包的方式引入静态资源。

js: js库文件下载, copy当中的工程: js vue elementUI:

静态资源坐标参考

1. 引入依赖
 <!--jquery的jar包引入-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.6.0</version>
        </dependency>
2. 启动服务访问资源
浏览器访问路径:http://localhost:8080/webjars/jquery/3.6.0/jquery.min.js获取相应的静态资源。
6.1.2 静态资源的存放位置
1. 默认存放位置查看
默认静态资源路径:
"classpath:/META‐INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/" 
"/":当前项目的根路径
案例:在上述任意路径下创建静态资源hello.html,浏览器访问路径:http://localhost:8080/hello.html获取相应的静态资源。
2. 自定义位置存放
在配置文件中通过属性spring.web.resources.static-locations=自定义路径,设置自定义静态资源存放路径;
案例:
a、配置文件中设置:spring.web.resources.static-locations=classpath:/hello/
 yml文件:
  spring:
  web:
    resources:
      static-locations: [classpath:/hello/,classpath:/static/]
b、在自定义路径下添加静态资源hello.html,浏览器访问路径:http://localhost:8080/hello.html获取相应的静态资源。
注意:自定义静态资源路径后,默认静态资源路径失效!
6.2 springboot的自动配置
6.2.1 自动配置原理
1. springboot启动时加载主配值类,解析注解@SpringBootApplication
@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}
2. 在@SpringBootApplication组合注解中解析@EnableAutoConfiguration获取自动配置信息
//在@EnableAutoConfiguration注解中,为容器导入自动配置的组件:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration{
    ...
}
//1、第一步:通过AutoConfigurationImportSelector类的selectImports方法获取要导入的自动配置组件
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
        //获取自动配置的内容
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	} 
//2、第二步:
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //获取配置信息
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
//3、第三步:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    	//获取配置信息
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
//4、第四步:
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
        //获取要自动注入的组件信息
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}
//5、第五步:
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
            //获取要自动注入的组件信息
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}
//第六步:获取自动配置信息spring.factories
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
获取到配置文件中的自动配置类xxxAutoConfiguration的信息,并将自动配置类中的组件初始化到容器中。
初始化的过程中会加载xxxProperties对象,根据xxxProperties对象中的属性值初始化组件,xxxProperties对象的值可以通过全局配置文件指定。
3. 初始化自动配置组件
Spring Boot 自动配置SpringMVC为例,Spring Boot启动时会做以下操作:
//根据以上步骤找到自动配置类WebMvcAutoConfiguration,并根据自动配置类进行以下初始化操作:
@Configuration(proxyBeanMethods = false)//表示这是一个配置类,和以前编写的配置文件一样
@ConditionalOnWebApplication(type = Type.SERVLET)//@Conditional判断满足指定的条件,整个配置类里面的配置就会生效,否则不生效; 这里是判断当前应用是否是web应用,如果是当前配置类生效
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })//判断当前项目有没有以上指定类
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)//判断容器中是否没有指定组件
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)//自动配置顺序
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })//在指定自动配置之后配置
public class WebMvcAutoConfiguration {
    	/**
         * a、自动配置视图解析器
         */
    	@Bean//给容器中添加组件
		@ConditionalOnBean(ViewResolver.class)
		@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
		public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
			ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
			resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
			// ContentNegotiatingViewResolver uses all the other view resolvers to locate
			// a view so it should have a high precedence
			resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
			return resolver;
		}
    	/**
         * 自动配置视图解析器
         */
        @Bean
		@ConditionalOnBean(View.class)
		@ConditionalOnMissingBean
		public BeanNameViewResolver beanNameViewResolver() {
			BeanNameViewResolver resolver = new BeanNameViewResolver();
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
			return resolver;
		}
    	
    	/**
         * b、提供静态资源访问路径和webjars访问路径
         */
    	@Override
		protected void addResourceHandlers(ResourceHandlerRegistry registry) {
			super.addResourceHandlers(registry);
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			ServletContext servletContext = getServletContext();
			addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
			addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
				registration.addResourceLocations(this.resourceProperties.getStaticLocations());
				if (servletContext != null) {
					registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
				}
			});
		}
    
    
    -----------------内部类--------------------
    @Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class,
			org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

		private final WebMvcProperties mvcProperties;

		private final ListableBeanFactory beanFactory;

		private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;

		private final ObjectProvider<DispatcherServletPath> dispatcherServletPath;

		private final ObjectProvider<ServletRegistrationBean<?>> servletRegistrations;

		final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
		//该内部类只有一个带参构造方法,那么spring在创建该类实例时,只能从容器中获取参数;
		public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties,
				ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
				ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
				ObjectProvider<DispatcherServletPath> dispatcherServletPath,
				ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
			this.mvcProperties = mvcProperties;
			this.beanFactory = beanFactory;
			this.messageConvertersProvider = messageConvertersProvider;
			this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
			this.dispatcherServletPath = dispatcherServletPath;
			this.servletRegistrations = servletRegistrations;
			this.mvcProperties.checkConfiguration();
		}
		/**
         * c、自动注册Converter类型转换器
         */
		@Override
		public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
			this.messageConvertersProvider
					.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
		}

		@Override
		public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
			if (this.beanFactory.containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)) {
				Object taskExecutor = this.beanFactory
						.getBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
				if (taskExecutor instanceof AsyncTaskExecutor) {
					configurer.setTaskExecutor(((AsyncTaskExecutor) taskExecutor));
				}
			}
			Duration timeout = this.mvcProperties.getAsync().getRequestTimeout();
			if (timeout != null) {
				configurer.setDefaultTimeout(timeout.toMillis());
			}
		}

		@Override
		public void configurePathMatch(PathMatchConfigurer configurer) {
			if (this.mvcProperties.getPathmatch()
					.getMatchingStrategy() == WebMvcProperties.MatchingStrategy.PATH_PATTERN_PARSER) {
				configurer.setPatternParser(new PathPatternParser());
			}
			configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
			configurer.setUseRegisteredSuffixPatternMatch(
					this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
			this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
				String servletUrlMapping = dispatcherPath.getServletUrlMapping();
				if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
					UrlPathHelper urlPathHelper = new UrlPathHelper();
					urlPathHelper.setAlwaysUseFullPath(true);
					configurer.setUrlPathHelper(urlPathHelper);
				}
			});
		}

		private boolean singleDispatcherServlet() {
			return this.servletRegistrations.stream().map(ServletRegistrationBean::getServlet)
					.filter(DispatcherServlet.class::isInstance).count() == 1;
		}

		@Override
		public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
			WebMvcProperties.Contentnegotiation contentnegotiation = this.mvcProperties.getContentnegotiation();
			configurer.favorPathExtension(contentnegotiation.isFavorPathExtension());
			configurer.favorParameter(contentnegotiation.isFavorParameter());
			if (contentnegotiation.getParameterName() != null) {
				configurer.parameterName(contentnegotiation.getParameterName());
			}
			Map<String, MediaType> mediaTypes = this.mvcProperties.getContentnegotiation().getMediaTypes();
			mediaTypes.forEach(configurer::mediaType);
		}

		@Bean
		@ConditionalOnMissingBean
		public InternalResourceViewResolver defaultViewResolver() {
			InternalResourceViewResolver resolver = new InternalResourceViewResolver();
			resolver.setPrefix(this.mvcProperties.getView().getPrefix());
			resolver.setSuffix(this.mvcProperties.getView().getSuffix());
			return resolver;
		}

		@Bean
		@ConditionalOnBean(View.class)
		@ConditionalOnMissingBean
		public BeanNameViewResolver beanNameViewResolver() {
			BeanNameViewResolver resolver = new BeanNameViewResolver();
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
			return resolver;
		}

		@Bean
		@ConditionalOnBean(ViewResolver.class)
		@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
		public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
			ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
			resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
			// ContentNegotiatingViewResolver uses all the other view resolvers to locate
			// a view so it should have a high precedence
			resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
			return resolver;
		}
		//e、自动注册消息代码提示处理器MessageCodesResolver
		@Override
		public MessageCodesResolver getMessageCodesResolver() {
			if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
				DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
				resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
				return resolver;
			}
			return null;
		}
		//获取所有的格式化器和转换器
		@Override
		public void addFormatters(FormatterRegistry registry) {
			ApplicationConversionService.addBeans(registry, this.beanFactory);
		}

---------------------------------------------------------  
//EnableWebMvcConfiguration内部类:
       // c、自动注册Formatter格式化器;
        @Bean
		@Override
		public FormattingConversionService mvcConversionService() {
			Format format = this.mvcProperties.getFormat();
			WebConversionService conversionService = new WebConversionService(new DateTimeFormatters()
					.dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
			addFormatters(conversionService);
			return conversionService;
		}
        //f、支持静态首页index.html访问
        private Resource getWelcomePage() {
			for (String location : this.resourceProperties.getStaticLocations()) {
				Resource indexHtml = getIndexHtml(location);
				if (indexHtml != null) {
					return indexHtml;
				}
			}
			ServletContext servletContext = getServletContext();
			if (servletContext != null) {
				return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
			}
			return null;
		}
        
-------------------------------------------------------
		@Bean
		@ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
		@ConditionalOnMissingFilterBean(RequestContextFilter.class)
		public static RequestContextFilter requestContextFilter() {
			return new OrderedRequestContextFilter();
		}

	}

    
}
a、自动配置视图解析器ViewResolverContentNegotiatingViewResolverBeanNameViewResolver,作用:根据方法返回值得到视图对象,由视图对象决定请求转发或者重定向到指定视图。(注意:此处会创建获取容器中的所有视图解析器,包括自定义的视图解析器)
b、提供静态资源访问路径和webjars访问路径;
c、自动注册Converter类型转换器,Formatter格式化器;
d、提供HttpMessageConverters,用于Http请求和响应之间的转换,将json或者xml格式字符串和java类型之间做转换;
(注意:如果要自定义类型转换器,也可以通过该接口注册)
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }

}
内容了解~ 
e、自动注册消息代码提示处理器MessageCodesResolver
f、支持静态首页index.html访问
g、自动注册web数据参数绑定对象ConfigurableWebBindingInitializer,如果我们自己注册了该类型对象,那springboot会自动调用我们注册的对象,替换默认对象将请求参数绑定到javabean(了解)。
h、如果要自定义拦截器interceptors,格式化器formatters,视图控制器view controllers等,可以通过创建类型为WebMvcConfigurer的配置类来实现,不要加注解@EnableWebMvc。
i、如果要自定义处理器映射器RequestMappingHandlerMapping, 处理器适配器RequestMappingHandlerAdapter, 或者异常处理器ExceptionHandlerExceptionResolver,可以在容器中创建类型为WebMvcRegistrations的组件对象来实现。
j、如果想要完全使用自定义配置,不使用springboot默认配置,可以在配置类上加注解@EnableWebMvc。(不推荐)

参考官方文档:https://docs.spring.io/spring-boot/docs/2.4.3/reference/html/spring-boot-features.html#boot-features-spring-mvc
4. 小结
a、SpringBoot启动会加载大量的自动配置类 
b、根据配置文件和xxxProperties的默认设置初始化自动配置类中的组件
c、可以在自动配置类中查看我们需要的功能是否已经自动配置,如果配置,我们就不用再做配置了;
6.2.2 自定义配置实现访问拦截
1. 自定义拦截器
public class MyIntercepter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        String username=(String)session.getAttribute("username");
        if(username!=null){
            System.out.println("登录成功不拦截");
            return true;
        }else{
            request.getRequestDispatcher("/index.html").forward(request,response);
            System.out.println("未登录拦截");
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
2. 注册拦截器
@Configuration
public class MyConfig implements WebMvcConfigurer {
   
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyIntercepter()).addPathPatterns("/**").excludePathPatterns("/","/index.html","/user/login");
    }
}
3. 自定义配置视图控制器
//springboot默认不支持jsp,而是使用模板引擎thymeleaf作为视图
//引入thymeleaf
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
//配置视图控制器
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyIntercepter()).addPathPatterns("/**").excludePathPatterns("/","/index.html","/user/login");
    }
	//配置视图控制器
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/main").setViewName("main");
    }
}

第7节:thymeleaf

7.1 模板引擎概述
thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似JSPVelocityFreeMaker等,它也可以轻易的与Spring MVCWeb框架进行集成作为Web应用的模板引擎。与其它模板引擎相比,Thymeleaf最大的特点是能够 
直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用 

Spring Boot推荐使用ThymeleafFreemarker等后现代的模板引擎技术;一但导入相 
关依赖,会自动配置ThymeleafAutoConfigurationFreeMarkerAutoConfiguration
模板引擎工作原理图:

在这里插入图片描述

7.2 入门案例
7.2.1 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.4.3</version>
</dependency>
7.2.2 添加配置
spring:   
   #开始thymeleaf设置
   thymeleaf:   
   #禁用模板缓存 
     cache: false
7.2.3 编写Controller文件
package com.offcn.demo.controller;
@Controller
public class FirstThymeleafController {
  /**
       * 访问http://localhost:8080/first 
       * 将数据message填充到templates/index.html
    * @param model
    * @return
    */
    @GetMapping("/first")
    public String indexPage(Model model) {
        String message = "Hello, Thymeleaf!";
        model.addAttribute("message", message);
        return "index";
    }
}
7.2.4 编写模板文件
在resources/templates 下新建 index.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1 th:text="${message}"></h1>
</body>
</html>
通过 类似EL 表达式将 Model 中的数据填充到 h1 标签中
去掉idea中 对thymeleaf模板格式的检查  setting -- editer --- inpection --- thymeleaf 去掉对勾
7.2.5 运行访问

在这里插入图片描述

7.3 语法简介

在这里插入图片描述

7.4 数据交互
7.4.1 新建一个实体
package com.offcn.demo.bean;
public class User {
    private Integer id;
    private String name;
    private int age;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
7.4.2 新建Controller
package com.offcn.demo.controller;

import java.util.HashMap;
import java.util.Map;

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

import com.offcn.demo.bean.User;
@Controller
public class SecondThymeleafController {
	/**
     * 访问localhost:8080 页面
     * 将数据message填充到templates/index2.html
  * @param model
  * @return
  */
  @GetMapping("/second")
  public String indexPage(Model model) {
      String message = "Hello, Thymeleaf!";
      User u = new User();
      u.setId(1);
      u.setName("优就业");
      u.setAge(18);
      
      Map<String,Object> map=new HashMap<>();
      map.put("src1","1.jpg");
      map.put("src2","2.jpg");
      model.addAttribute("message", message);
      model.addAttribute("user", u);
      model.addAttribute("src", map);
      return "index2";
  }
}
7.4.3 新增模板文件
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1 th:text="${message}"></h1> 
    <img th:src="${src.src1}"/>
    </br>
    <img th:src="${src.src2}"/>
    </br>
    <span th:text="${user.id}"></span>
    <span th:text="${user.name}"></span>
    <span th:text="${user.age}"></span>
</body>
</html>
7.4.4 访问地址

在这里插入图片描述

7.5 循环遍历集合
7.5.1 新建一个Controller,
package com.offcn.demo.controller;
@Controller
public class ThreeThymeleafController {
	/**
     * 访问localhost:8080/java003 页面
     * 将数据message填充到templates/index3.html
  * @param model
  * @return
  */
  @GetMapping("/three")
  public String indexPage(Model model) {
      List<User> list=new ArrayList<User>();      
      User u1 = new User();
      u1.setId(1);
      u1.setName("优就业");
      u1.setAge(18);
      list.add(u1);
      
      User u2 = new User();
      u2.setId(2);
      u2.setName("中公教育");
      u2.setAge(28);
      list.add(u2);      
      User u3 = new User();
      u3.setId(3);
      u3.setName("IT先锋");
      u3.setAge(88);
      list.add(u3);
      
      User u4 = new User();
      u4.setId(4);
      u4.setName("JAVA第一");
      u4.setAge(888);
      list.add(u4);
      
      model.addAttribute("userList", list);
      return "index3";
  }
}
7.5.2 新增模板文件
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
   <table width="200" style="text-align: center;">
        <tr>
            <th>编号</th>
            <th>姓名</th>
            <th>年龄</th>
<th>index</th>
        </tr>
        <tr th:each="user,iterStat : ${userList}">
            <td th:text="${user.id}"></td>
            <td th:text="${user.name}"></td>
            <td th:text="${user.age}"></td>
<td th:text="${iterStat.index}">index</td>
        </tr>
    </table>
</body>
</html>
iterStat 称作状态变量,属性有:

index:当前迭代对象的 index(从 0 开始计算)

count:当前迭代对象的 index(从 1 开始计算)

size:被迭代对象的大小

current:当前迭代变量

even/odd:布尔值,当前循环是否是偶数/奇数(从 0 开始计算)

first:布尔值,当前循环是否是第一个

last:布尔值,当前循环是否是最后一个
7.5.3 访问地址

在这里插入图片描述

7.6 赋值、字符串拼接
7.6.1 新建一个Controller
package com.offcn.demo.controller;
@Controller
public class FourThymeleafController {
    /**
     * 访问localhost:8080/java003 页面
     * 将数据message填充到templates/index4.html
  * @param model
  * @return
  */
    @GetMapping("/four")
    public String indexPage(Model model) {
        model.addAttribute("userName", "优就业");
        model.addAttribute("href", "http://www.ujiuye.com");
        return "index4";
    }
}
7.6.2 新增模板文件
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<!-- 给标签赋值 th:text -->
    <h1 th:text="${userName}"></h1>
<!-- 给属性赋值 th:value、th:属性名称 -->    
     <input type="text" name="names" th:value="${userName}"/>     
     </br>
     <em th:size="${userName}"></em>
<!-- 字符串拼接 -->    
    <span th:text="'欢迎来:'+${userName}+'学习!'"></span>
    </br>
<!-- 字符串拼接,方式2 -->  
    <span th:text="|欢迎来:${userName}学习!|"></span>    
</body>
</html>
7.6.3 访问地址

在这里插入图片描述

7.7 条件判断、选择语句
7.7.1 新建一个Controller
package com.offcn.demo.controller;
@Controller
public class FiveThymeleafController {
    /**
     * 访问localhost:8080/ 页面
     * 将数据message填充到templates/index4.html
  * @param model 4
  * @return
  */
    @GetMapping("/five")
    public String indexPage(Model model) {
        model.addAttribute("flag", "yes");   
        model.addAttribute("menu", "admin");
        model.addAttribute("manager", "manager"); 
        return "index5";
    }
} 
7.7.2 新增模板文件
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <!-- th:if 条件成立就显示 -->
        <h1 th:if="${flag=='yes'}" >中公教育</h1>   
        <!-- th:unless 条件不成立就显示 --> 
        <h1 th:unless="${flag=='no'}" >优就业</h1>    
        <!-- switch选择语句 -->
        <div th:switch="${menu}">
            <p th:case="'admin'">User is an administrator</p>
            <p th:case="${manager}">User is a manager</p>  
        </div>
    </body>
</html>
7.7.3 访问地址

在这里插入图片描述

7.8 片段fragment定义使用
thymeleaf也提供了类似import的东西,可以将很多代码块抽象成模块,然后在需要的时候引用,非常方便。

fragment介绍

fragment类似于JSP的tag,在html中文件中,可以将多个地方出现的元素块用fragment包起来使用。

fragment使用

定义fragment,所有的fragment可以写在一个文件里面,也可以单独存在,例如:
7.8.1 新增模板文件 footer.html
<body>
  <h1 th:fragment="copy">
      &copy; 1999-2022 Offcn.All Rights Reserved 
    </h1>
</body>
注意: 在Springboot中,默认读取thymeleaf文件的路径是:src/main/resource/templates 
7.8.2 编写Controller
package com.offcn.demo.controller;
@Controller
public class SixThymeleafController {
    /**
     * 访问localhost:8080/java003 页面
     * 将数据message填充到templates/index6.html
  * @param model
  * @return
  */
    @GetMapping("/six")
    public String indexPage(Model model) {      
        return "index6";
    }
}
7.8.3 新增视图文件
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <!-- 把片段的内容插入到当前位置 -->
        <div style="color: #82ff6c"  th:insert="~{footer :: copy}"></div>
        </br>
        <!-- 使用片段的内容替换当前标签 -->
        <div style="color: #82ff6c"  th:replace="~{footer :: copy}"></div>
        </br>
        <!-- 保留自己的主标签,不要片段的主标签 -->
        <div style="color: #82ff6c"   th:include="~{footer :: copy}"></div>
</body>
</html>
fragment的引用

th:insert:保留自己的主标签,保留th:fragment的主标签。

th:replace:不要自己的主标签,保留th:fragment的主标签。

th:include:保留自己的主标签,不要th:fragment的主标签。(官方3.0后不推荐)
7.8.4 访问地址运行

在这里插入图片描述

7.9 内置对象使用
7.9.1 常见内置工具对象
#dates 与java.util.Date对象的方法对应,格式化、日期组件抽取等等
#numbers 格式化数字对象的工具方法
#strings 与java.lang.String对应的工具方法
7.9.2 编写Controller
package com.offcn.demo.controller;
@Controller
public class SevenThymeleafController {
    /**
* 访问localhost:8080/java003 页面
* 将数据message填充到templates/index7.html
* @param model
* @return
*/
    @GetMapping("/seven")
    public String indexPage(Model model) {      
        //日期时间
        Date date = new Date();
        model.addAttribute("date", date);
        //小数的金额
        double price=128.5678D;
        model.addAttribute("price", price);
        //定义大文本数据
        String str="Thymeleaf是Web和独立环境的现代服务器端Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚至纯文本。\r\n" + 
            "Thymeleaf的主要目标是提供一种优雅和高度可维护的创建模板的方式。为了实现这一点,它建立在自然模板的概念上,将其逻辑注入到模板文件中,不会影响模板被用作设计原型。这改善了设计的沟通,弥补了设计和开发团队之间的差距。\r\n" + 
            "Thymeleaf也从一开始就设计了Web标准 - 特别是HTML5 - 允许您创建完全验证的模板,如果这是您需要的\r\n" ;
        model.addAttribute("strText", str);	
        //定义字符串
        String str2="JAVA-offcn";
        model.addAttribute("str2", str2);
        return "index7";
    }
}
7.9.3 新增模板文件
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
时间:<span th:text="${#dates.format(date,'yyyy-MM-dd HH:mm:ss')}">4564546</span></br>     
金额:<span th:text="'¥'+${#numbers.formatDecimal(price, 1, 2)}">180</span> </br>
<!-- # 这里的含义是 如果 atc.text 这个变量多余200个字符,后面显示... -->
<p th:text="${#strings.abbreviate(strText,60)}">内容内容内容</p>   

<!-- 判断字符串是否为空 -->
<span th:if="${!#strings.isEmpty(str2)}">字符串str2不为空</span></br>
<!-- 截取字符串,指定长度 -->
<span th:text="${#strings.substring(str2,0,4)}">字符串str2的值</span>
</body>
</html>
7.9.4 运行访问地址

在这里插入图片描述

第8节:SpringBoot整合Junit

8.1 junit5 介绍
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库

作为最新版本的JUnit框架,JUnit5与之前版本的Junit框架有很大的不同。由三个不同子项目的几个不同模块组成。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。

JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。

JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。

在这里插入图片描述

8.2 整合Junit
8.2.1 构建工程添加依赖
<!--junit5版本,不兼容Junit4 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

<!--如果要继续兼容 junit4,自行引入Vintage-->
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
8.2.2 创建测试类
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;


class TestThymeleafApplicationTests {

    @Test
    void contextLoads() {
    }

}

8.2.3 测试类上添加注解
@SpringBootTest springboot整合junit5 的注解。 
8.2.4 测试类注入测试对象
@SpringBootTest
class TestThymeleafApplicationTests {
    @Autowired
    private ApplicationContext applicationContext;
    @Test
    void contextLoads() {
        System.out.println(applicationContext);
    }
}

第9节:SpringBoot整合Mybatis

9.1 创建工程添加依赖
<!--SpringBoot整合Mybatis的启动器-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
<!--mysql驱动包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<!--web模块-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
9.2 建数据库数据库表
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `money` double(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1005 DEFAULT CHARSET=utf8

9.3 创建数据模型
/**
 * @author offcn
 **/
public class Account implements Serializable {
    private Integer id;
    private String name;
    private double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
9.4 创建接口和mapper文件
@Mapper springboot就能够帮助创建接口的代理类对象。 
public interface AccountMapper {
    Account findById(Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.offcn.AccountMapper">
    <!-- Account findById(Integer id);-->
    <select id="findById" resultType="com.offcn.pojo.Account">
        select * from account where id=#{id}
    </select>
</mapper>
9.5 配置application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver

# 配置mybatis规则
mybatis:
  #config-location: classpath:mybatis/mybatis-config.xml  #全局配置文件位置
  mapper-locations: classpath:mybatis/mapper/*.xml  #sql映射文件位置【必须】
  type-aliases-package: com.offcn.pojo #配置了实体的别名
  configuration:
    map-underscore-to-camel-case: true #开启 驼峰式命名法 不写全局,局配置文件的配置都放在configuration对象当中
    
  总结: 
  boot整合mybatis , mapper映射文件的地址必须要指定~ ,其他都是可选择~
9.6 创建业务层接口和实现类
public interface AccountService {
    Account findByIdService(Integer  id);
}

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;
    @Override
    public Account findByIdService(Integer id) {
        return accountMapper.findById(id);
    }
}
9.7 创建控制器controller
@Controller
@RequestMapping("account")
public class AccountController {
    @Autowired
    private AccountService accountService;

    @GetMapping("findById")
    @ResponseBody
    public Account findById(Integer id){
        return accountService.findByIdService(id);
    }
}
9.8 启动服务测试结果
访问地址: http://localhost:8080/account/findById?id=1001
测试结果: 

在这里插入图片描述

第10节:SpringBoot整合Redis

spring-data-redis针对jedis提供了如下功能:
	1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类
	2.针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
	ValueOperations:简单K-V操作
	SetOperations:set类型数据操作
	ZSetOperations:zset类型数据操作
	HashOperations:针对map类型的数据操作
	ListOperations:针对list类型的数据操作
10.1 创建程序引入启动器

在这里插入图片描述

或者是pom.xml文件当中引入启动器依赖
<!--整合redis的启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
10.2 配置连接信息
spring:
  redis:
      host: 192.168.126.10 
      port: 6379
     # password: 123456
10.3 RedisTemplate操作Redis
10.3.1 值类型操作
package com.offcn;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
public class BootRedisDemoApplicationTests {
   @Autowired
   private RedisTemplate<String,String> redisTemplate;
   
    @Test
   void test1() {
       ValueOperations<String, String> ops = redisTemplate.opsForValue();
       //ops 存
       ops.set("username","jack");
       // ops取
       String username = ops.get("username");
       System.out.println(username);
       //移除
       redisTemplate.delete("username");
   }
}

10.3.2 Set类型操作
package com.offcn;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Set;

@SpringBootTest
public class BootRedisDemoApplicationTests {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    // set集合类型
    @Test
    void test2() {
        SetOperations<String, String> ops = redisTemplate.opsForSet();
        //存:
        ops.add("set","jack","marray","george");
        //取:
        Set<String> set = ops.members("set");
        System.out.println(set);

        //移除:移除单个元素
        ops.remove("set","jack");

        //删除key
        redisTemplate.delete("set");

    }
}

10.3.3 List集合操作
1. 右压栈
//  右压栈
@Test
void test3() {
    ListOperations<String, String> ops = redisTemplate.opsForList();
    //右压栈
    ops.rightPush("myList","a");
    ops.rightPush("myList","b");
    ops.rightPush("myList","c");

    //取值:
    List<String> myList = ops.range("myList", 0, -1);
    System.out.println(myList);
}
2. 左压栈
//  左压栈
@Test
void test4() {
    ListOperations<String, String> ops = redisTemplate.opsForList();
    //左压栈
    ops.leftPush("myList","A");
    ops.leftPush("myList","B");
    ops.leftPush("myList","C");

    //取值:
    List<String> myList = ops.range("myList", 0, -1);// CBAabc
    System.out.println(myList);

}
3. 根据索引查询元素
//  根据索引查询元素
@Test
void test5() {
    ListOperations<String, String> ops = redisTemplate.opsForList();
    String value = ops.index("myList", 1);
    System.out.println(value);

}
4. 移除某个元素的值
//  移除某个元素的值
@Test
void test6() {
    ListOperations<String, String> ops = redisTemplate.opsForList();
    ops.remove("myList",1,"A");
    List<String> myList = ops.range("myList", 0, -1);//
    System.out.println(myList);// CBabc
}

10.3.4 Hash类型操作
1. 存入值
@Test
void test7() {
    HashOperations<String, Object, Object> ops = redisTemplate.opsForHash();
    // 存入值
    ops.put("user","username","mrzhang");
    ops.put("user","address","beijing");
    ops.put("user","age","18");
}
2. 提取所有的KEY
// 提取所有的KEY
@Test
void test8() {
    HashOperations<String, Object, Object> ops = redisTemplate.opsForHash();
    // 提取所有的KEY[field]
    Set<Object> user = ops.keys("user");
    System.out.println(user);// username address age

}
3. 提取所有的值
// 提取所有的值
@Test
void test9() {
    HashOperations<String, Object, Object> ops = redisTemplate.opsForHash();
    // 提取所有的KEY[field]
    List<Object> user = ops.values("user");
    System.out.println(user);

}
4. 根据KEY提取值
// 根据KEY提取值
@Test
void test10() {
    HashOperations<String, Object, Object> ops = redisTemplate.opsForHash();
    // 提取所有的KEY[field]
    String o = (String) ops.get("user", "username");
    System.out.println(o);

}
5. 根据KEY移除值
// 根据KEY移除值
@Test
void test11() {
    HashOperations<String, Object, Object> ops = redisTemplate.opsForHash();
    // 提取所有的KEY[field]
    ops.delete("user", "username");
}
10.4 SpringBoot整合Redis序列化操作
10.4.1 Spring提供的序列化器介绍
1. Spring默认序列化器
RedisTemplate操作时,默认会采用jdkSerializable序列化机制,使得插入的值在redis客户端看来会有乱码,若想解决这一问题,需要手动指定序列化方式。
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        boolean defaultUsed = false;
        if (this.defaultSerializer == null) {
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        }
        ...
    }
    
    默认序列化器: JdkSerializationRedisSerializer存对象的时候, 将对象转换成二进制信息存值。
2. 常见的序列化器

在这里插入图片描述

10.4.2 序列化案例实现
1. 创建实体实现序列化接口
/**
 * @author offcn
 * 实现序列化接口Serializable
 **/
public class Account implements Serializable {
    private Integer id;
    private String name;
    private double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
2. 在配置类中设置序列化器
/**
 * 指定自己的RedisTemplate 模板对象,模板对象设定给了序列化器
 * @param redisConnectionFactory
 * @return 返回值装在在IOC容器当中
 */
@Configuration
public class RedisConfigurer{
    @Bean
    public RedisTemplate<String, Account> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // LettuceConnectionFactory也可以使用---替代RedisConnectionFactory(因为springboot2.0开始使用的就是LettuceConnectionFactory)
        RedisTemplate<String, Account> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //创建一个序列化器:
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Account.class);
        //设置默认的序列化器
        template.setDefaultSerializer(serializer);
        return template;
    }
}

使用Jackson2JsonRedisSerializer,需要引入jackson的依赖
如果使用了自定义容器,原有的RedisTemplate使用将受影响,需要将这个@Bean上添加一个bean的名字,在使用注入时使用这个名字
    @Bean("accountTemplate")
    
    @Resource("accountTemplate")
    RedisTemplate redisTemplate;

3. 测试类对象序列化
@SpringBootTest
public class RedisDemo02ApplicationTests {

    @Autowired
    private RedisTemplate<String,Account> redisTemplate;

    /**
     * 序列化对象到redis当中
     */
    @Test
    public void serializableAccount() {
        Account account = new Account();
        account.setId(1001);
        account.setName("张三丰");
        account.setId(98765);
        redisTemplate.opsForValue().set("account",account);
        System.out.println("序列化成功");
    }

}

第11节:SpringBoot整合Dubbo

11.1 启动zookeeper注册中心服务

在这里插入图片描述

注意: 启动dubbo-admin ,方便服务的查询~ 
11.2 构建工程添加依赖
 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.6.3</version>
        </dependency>

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

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>2.7.6</version>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--springboot整合dubbo-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.6</version>
        </dependency>


        <dependency>
            <groupId>com.offcn</groupId>
            <artifactId>sb_service_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
</dependencies>
11.3 创建服务提供者
11.3.1 定义服务接口
/**
 * @author offcn
 **/
public interface OrderService {
    /***
     * 查询某个订单对象
     */
    public Order loadOrdersService(Integer id);
    /***
     * 查询所有订单集合
     */
    public List<Order> loadOrdersListService();
}
11.3.2 定义实现类并暴露服务
/**
 * @author offcn
 * 注意: @Service 注解式dubbo的注解,不是Spring的service注解。
 **/
@Component
@Service
public class OrderServiceImpl implements OrderService {
    @Override
    public Order loadOrdersService(Integer id) {
        // TODO Auto-generated method stub
        Order o=new Order();
        o.setId(id);
        o.setRemark("备注....");
        o.setTotal(123);
        return o;
    }

    @Override
    public List<Order> loadOrdersListService() {
        Order o=new Order();
        o.setId(1001);
        o.setRemark("新疆大盘鸡一份");
        o.setTotal(12);

        Order o1=new Order();
        o1.setId(1002);
        o1.setRemark("东北杀猪菜一份");
        o1.setTotal(123);

        List<Order> results=new ArrayList<Order>();
        results.add(o);
        results.add(o1);
        return results;
    }
}

11.3.3 配置application.properties整合dubbo
server:
 port: 8088
spring:
 application:
    name: sb8
#dubbo端口和名称
dubbo:
  protocol:
    name: dubbo
    port: 20880
  registry:
    address: zookeeper://192.168.1.151:2181
    timeout: 60000
  scan:
    base-packages: com.offcn.service.impl
11.3.4 创建启动类并启动服务
@SpringBootApplication
public class DemoProvederApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoProvederApplication.class, args);
    }
}
11.4 创建服务器消费者
11.4.1 环境准备
创建boot工程, 引入相关依赖
 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.6.3</version>
        </dependency>

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

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>2.7.6</version>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--springboot整合dubbo-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.6</version>
        </dependency>


        <dependency>
            <groupId>com.offcn</groupId>
            <artifactId>sb_service_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
</dependencies>
11.4.2 定义消费端业务接口
public interface OrderService {
    /***
     * 查询某个订单对象
     */
    public Order loadOrdersService(Integer id);
    /***
     * 查询所有订单集合
     */
    public List<Order> loadOrdersListService();
}
11.4.3 定义消费端
@Controller
public class OrderController {

   // @Autowired spring容器当中自动装配
    @Reference//dubbo容器当中获得一个远程对象,注入。
    private OrderService orderService;


    @RequestMapping("findById")
    @ResponseBody
    public Order findById(Integer id){
        Order order = orderService.loadOrdersService(id);
        return  order;
    }

    @RequestMapping("findAll")
    @ResponseBody
    public List<Order> findAll(Integer id){
      return orderService.loadOrdersListService();
    }
}
11.4.4 配置application.yml整合dubbo
server:
  port: 9000
  servlet:
    context-path: /

dubbo:
  protocol:
    name: dubbo
    port: 20881
  registry:
    address: zookeeper://192.168.1.151:2181
    timeout: 60000
11.4.5 创建启动类并启动服务
@SpringBootApplication
public class DemoProvederApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoProvederApplication.class, args);
    }
}
11.4.6 启动浏览器进行测试

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值