springboot入门

SpringBoot入门

参考资料:

1.资源:笔记https://www.yuque.com/atguigu/springboot

3.微服务概念中文版https://blog.cuicc.com/blog/2015/07/22/microservices/

4.springboot文档:https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/

5.配置书写参考https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/appendix-application-properties.html#common-application-properties-web

image-20220714103847327

6.查看版本迭代信息:

image-20220714083551093

7.tomcat是如何启动的源码分析

https://blog.csdn.net/qq_29551611/article/details/119268637

8.史上最全的Jackson框架使用教程

https://blog.csdn.net/weixin_44747933/article/details/108301626

第1章--getting-start

1-1 基础入门

image-20220714100037287

image-20220714100953883

1-2 写一个helloWorld

@ResponseBody作用:

@ResponseBody:标注在类上,代表该类的所有方法返回值直接传给浏览器,而非某一个页面的跳转
@ResponseBody : 标注在方法上,代表该方法的返回值是直接写给浏览器的,而不是某一个页面的跳转

image-20220714102129768

image-20220714102608234

输出结果:(测试的时候直接运行main方法即可)

image-20220714102740109

SpringBoot只为我们准备了一个配置文件:

image-20220714103246471

image-20220714103356608

代码参考:

image-20220714122819176

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springboot-01-helloWorld</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
<!--打包jar-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties:

#修改Tomcat服务器的端口号为8888
#server.port=8888

MainApplication.java:

package com.xu1.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @auther xu
 * @Description
 * 主查程序类
 * @SpringBootApplication:表明是一个SpringBoot应用
 *
 * @date 2022/07/14 - 9:56:48
 * @Modified By:
 */
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class,args);
    }
}

HelloController.java:

package com.xu1.boot.controller;

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

/**
 * @auther xu
 * @Description
 *
 * @ResponseBody:标注在类上,代表该类的所有方法返回值直接传给浏览器,而非某一个页面的跳转
 *
 * @date 2022/07/14 - 10:13:57
 * @Modified By:
 */
//@ResponseBody
//@Controller

@RestController
public class HelloController {

    /**
     *
     * @Description
     * @ResponseBody : 标注在方法上,代表该方法的返回值是直接写给浏览器的,而不是某一个页面的跳转
     * @Date 2022/07/14 10:15:46
     *
     */
//    @ResponseBody
    @RequestMapping(value = "/hello")
    public String handle01() {

        return "hello,this is springboot of page!";
    }
}

1-3 项目部署---详解java -jar命令及SpringBoot通过java -java启动的过程

参考网址https://www.w3cschool.cn/article/49843893.html

操作步骤:

image-20220714121718714

image-20220714121811813

结果:

image-20220714122137059

1-4 maven就近优先原则用于修改版本号

image-20220714141326769

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

解决问题:

对于以上的springboot依赖,spring-boot-starter-web为我们自动导入web开发所需的jar包,但是有可能我们项目开发中需要更改jar包的版本号。

image-20220714141642206

补充:该8.0版本的就是父项目依赖管理的依赖版本。

image-20220714141928377

1-5 开发导入starter场景启动器

官网:https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/using-spring-boot.html#using-boot-starter

image-20220714143947312

说明:对于某一种场景所需要导入的所有jar包

image-20220714142747739

以下是spring-boot-starter-web所导入的依赖图示:

springboot-01-helloWorld

注意:

image-20220714143448715

所谓非版本仲裁:eg:spring-boot-starter-web依赖中,没有开发所需的jar包,在导入外部jar包时,需要写明版本号。(比如:mysql依赖的导入)

1-6 基础入门---自动配置特性

image-20220714153256879

image-20220714153422124

image-20220714153547971

1.默认扫描的包

image-20220714154119176

更正:只要是与Application.java是同一个目录,或者是其子包中的类,都会自动扫描

验证:如果不是与Application.java是同一个目录,或者是其子包中的类,那么就不会自动扫描

image-20220714155532339

得证.

1.对于默认包扫描不到的类,解决办法如下:

法1:将包扫描层级扩大

image-20220714160529238

法2:将包扫描层级扩大(使用注解不同)

image-20220714160718614

image-20220714161114616

总结:(自动配置按需加载,没有使用的类不加载)

image-20220714161712250

image-20220714161800026

第2章 底层注解

2-1 底层注解之@Configuration

image-20220714171045212

1.验证加入容器中的组件是否是单实例:

image-20220714170839109

2.验证:配置类本身也是一个组件

image-20220714171449513

3.bean是一个组件类,调用其中的user01()方法,可以验证返回值都是加载进容器的单实例组件

image-20220714171957388

得证。

4.组件依赖的程度---proxyBeanMethods表明:

image-20220714175133853

1.条件准备:

image-20220714172958113

2.验证User组件中Pet类型的属性与Pet组件依赖性是否很强

image-20220714174248771

image-20220714175051642

总结:

proxyBeanMethods默认值是true,一般将proxyBeanMethods设置为false,如果只是单单给容器中注册组件,各组件之间也不依赖,设置为false时,这样SpringBoot启动也非常快,加载也非常快。反之设置为true.

image-20220714175743836

2-2 底层注解之@Import注解导入组件

image-20220714222853946

image-20220714223211540

代码参考:

package com.xu1.boot.config;

import ch.qos.logback.core.db.DBHelper;
import com.xu1.boot.bean.Pet;
import com.xu1.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @auther xu
 * @Description
 * @date 2022/07/14 - 16:51:10
 * @Modified By:
 */
@Import({User.class, DBHelper.class})
//自动调用组件类型的无参构造器,创建组件所对应的对象,放到容器中,默认组件的名字就是全类名

//告诉SpringBoot这是一个配置类,等同于配置文件(beans.xml)
//@Configuration(proxyBeanMethods = false)
@Configuration(proxyBeanMethods = true)
public class MyConfig {

    @Bean//给容器中添加组件,方法名作为组件的id,返回值类型就是组件的类型,返回值就是组件在容器中的实例
    public User user01() {
        User user = new User("小朱", "21");
        user.setPet(pet01());
        return user;
    }

//    @Bean(value = "dog01")//不使用方法名作为id,给该组件起一个别名
    public Pet pet01() {
        return new Pet("小黄");
    }
}

2-3 底层注解之@Conditional(满足该注解指定的条件,才进行组件注入)

image-20220715045012070

image-20220715044806894

1.@ConditionalOnBean(name = "dog01")标注在方法上

image-20220715045558326

2.@ConditionalOnBean(name = "dog01")标注在类上

image-20220715050053235

3.@ConditionalOnMissingBean(name = "dog01")标注在类上

image-20220715050634142

2-4 底层注解之-@ImportResource

image-20220715052517159

image-20220715052849343

2-5 底层注解之@ConfigurationProperties

image-20220715055119106

数据绑定方式1:@Component+@ConfigurationProperties

image-20220715055630655

image-20220715055852008

数据绑定方式2:@ConfigurationProperties+@EnableConfigurationProperties

1.功能:

1.将Car这个组件自动注册到容器中
2.开启Car自动配置功能

2.解决的问题:

如果Car类引用的是第3方包里面的类,但是没有标@Component,导致该类没有在容器中,没有在容器中,就无法使用@ConfigurationProperties进行数据绑定,该方式解决此问题。

3.结果参考:

注意:没有使用@Component将类注册到容器中,而是@EnableConfigurationProperties(value = Car.class)也具有组件注册功能

image-20220715061055158

image-20220715061159074

4.源码参考:
/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.context.properties;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Indexed;

/**
 * Annotation for externalized configuration. Add this to a class definition or a
 * {@code @Bean} method in a {@code @Configuration} class if you want to bind and validate
 * some external Properties (e.g. from a .properties file).
 * <p>
 * Binding is either performed by calling setters on the annotated class or, if
 * {@link ConstructorBinding @ConstructorBinding} is in use, by binding to the constructor
 * parameters.
 * <p>
 * Note that contrary to {@code @Value}, SpEL expressions are not evaluated since property
 * values are externalized.
 *
 * @author Dave Syer
 * @since 1.0.0
 * @see ConfigurationPropertiesScan
 * @see ConstructorBinding
 * @see ConfigurationPropertiesBindingPostProcessor
 * @see EnableConfigurationProperties
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface ConfigurationProperties {

   /**
    * The prefix of the properties that are valid to bind to this object. Synonym for
    * {@link #prefix()}. A valid prefix is defined by one or more words separated with
    * dots (e.g. {@code "acme.system.feature"}).
    * @return the prefix of the properties to bind
    */
   @AliasFor("prefix")
   String value() default "";

   /**
    * The prefix of the properties that are valid to bind to this object. Synonym for
    * {@link #value()}. A valid prefix is defined by one or more words separated with
    * dots (e.g. {@code "acme.system.feature"}).
    * @return the prefix of the properties to bind
    */
   @AliasFor("value")
   String prefix() default "";

   /**
    * Flag to indicate that when binding to this object invalid fields should be ignored.
    * Invalid means invalid according to the binder that is used, and usually this means
    * fields of the wrong type (or that cannot be coerced into the correct type).
    * @return the flag value (default false)
    */
   boolean ignoreInvalidFields() default false;

   /**
    * Flag to indicate that when binding to this object unknown fields should be ignored.
    * An unknown field could be a sign of a mistake in the Properties.
    * @return the flag value (default true)
    */
   boolean ignoreUnknownFields() default true;

}

第3章 自动配置原理入门

3-1 自动扫描包规则的原理

@SpringBootApplication

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

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

@Configuration:代表当前是一个配置类

image-20220715103713614

2.@ComponentScan--指定扫描那些,spring注解

3.@EnableAutoConfiguration(重点)

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
3-1 @AutoConfigurationPackage:
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

@AutoConfigurationPackage注解利用Registrar类给容器中导入一系列组件

image-20220715105939291

补充:注解指的是@AutoConfigurationPackage这一个注解

image-20220715111159471

源码参考:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }

   @Override
   public Set<Object> determineImports(AnnotationMetadata metadata) {
      return Collections.singleton(new PackageImports(metadata));
   }

}

3-2 初始加载自动配置类

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

1.@Import(AutoConfigurationImportSelector.class)

image-20220717112455878

image-20220717113007654

第2步:分析getCandidateConfigurations()方法

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;
}

image-20220717113501785

image-20220717114307553

image-20220717114355364

Spring.factories():

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

第3步:@Conditional...条件注解

虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional),最终会按需配置,也就是说,使用哪一个场景,就会按需配置哪一个jar包。

image-20220717115624512

总结:

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

3-3 修改默认配置(需要仔细看(学完spring注解开发):https://www.bilibili.com/video/BV19K4y1L7MT?p=15&spm_id_from=pageDriver&vd_source=7d15f545f2dd3742f9567eefbadf3aaa)

1.image-20220718062033778

    @Bean
	@ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
	@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
	public MultipartResolver multipartResolver(MultipartResolver resolver) {
        //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
        //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器名字不符合规范
		// Detect if the user has created a MultipartResolver but named it incorrectly
		return resolver;
	}
//给容器中加入了文件上传解析器;

SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

	@Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
    }

3.总结(方式1:查看文档有那些配置文件;方式2:直接查底层)

image-20220718062305545

eg:采用方式2(直接查底层)---修改编码方式

image-20220718063624232

image-20220718063814274

第4章 最佳实践

4-1 springboot应用如何编写

1.debug=true---查看自动配置报告

image-20220718064414768

image-20220718064952485

image-20220718065211864

2.实践---修改banner图像:

image-20220718081026007

image-20220718075549182

image-20220718075833983

image-20220718080859793

3.总结:

image-20220718080203569

4-2 Lombok简化开发

解决的问题:在编译时,自动帮我们生成get和set方法,达到简化开发的目的

image-20220718083151839

image-20220718084049561

第三步:自动装配

image-20220718084912750

image-20220718085046443

image-20220718085519481

image-20220718085740037

image-20220718090154167

4-3 Developer Tools

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

解决问题:

就是当前项目在运行期间进行了代码修改,可以使用(Ctrl+F9)帮你重新加载该项目,还有一个好处是,如果是静态页面修改,不需要重启,只是将页面替换了。

如果想使用热加载(eg:JRebel),需要付费购买该插件。( 什么是热加载:热加载是指可以在不重启服务的情况下让更改的代码生效,热加载可以显著的提升开发以及调试的效率,它是基于 Java 的类加载器实现的,但是由于热加载的不安全性,一般不会用于正式的生产环境。)

image-20220718090405744

image-20220718092129857

eg:

image-20220718092541698

image-20220718092723210

4-4 Spring Initializr简化开发

解决的问题:不需要再看springboot--starter--xx官方文档,使用场景直接勾选即可。

image-20220718093401014

image-20220718093705983

image-20220718093730301

image-20220718093815726

创建好的视图如下:

1.帮我们自动导入了依赖

image-20220718094415011

2.帮我们自动创建了项目结构

image-20220718094444225

3.帮我们自动编写好了主配置类

image-20220718094513772

image-20220718101712739

Spring Initializr初始化慢解决办法:见https://www.cnblogs.com/rong-xu-drum/p/16492769.html

-----------------------------------------以上是springboot入门,接下来为核心功能片的学习-----------------------------------

第5章 (springboot核心1)配置文件

5-1 properties

同以前的properties用法

5-2 yaml

5-2-1 简介

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

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

5-2-2 基本语法

  • key: value;kv之间有空格

  • 大小写敏感

  • 使用缩进表示层级关系

  • 缩进不允许使用tab,只允许空格

  • 缩进的空格数不重要,只要相同层级的元素左对齐即可

  • '#'表示注释

  • 字符串无需加引号,如果要加,''与""表示字符串内容 会被 转义/不转义

5-2-3 数据类型

  • 字面量:单个的、不可再分的值。date、boolean、string、number、null

    k: v
    
  • 对象:键值对的集合。map、hash、set、object

    行内写法:  k: {k1:v1,k2:v2,k3:v3}
    #或
    k: 
      k1: v1
      k2: v2
      k3: v3
    
  • 数组:一组按次序排列的值。array、list、queue

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

5-2-4 示例

使用application.yml完成对Person对象的数据绑定

#  private String userName;
#  private Boolean boss;
#  private Date birth;
#  private Integer age;
#  private Pet pet;
#  private String[] interests;
#  private List<String> animal;
#  private Map<String, Object> score;
#  private Set<Double> salarys;
#  private Map<String, List<Pet>> allPets;
person:
  userName: zhang3
  boss: true
  birth: 2022/7/18
  age: 21

#  interests: [篮球,乒乓球]
  interests:
    - 篮球
    - 乒乓球
  animal: [小猫,小狗]

#  score:
#    English: 110
#    math: 90
  score: {English: 110,math: 90}

  salarys: [40000,4100]

  pet:
    name: 阿文
    weight: 45

  #  分为生病的宠物和健康的宠物
  allPets:
    sick:
      - {name: 阿狗,weight: 20}
      - name: 阿猫
        weight: 30
      - {name: 阿虫,weight: 40.25}
    health: [{name: 阿朱,weight: 55.55},{name: 阿丹,weight: 65.89}]

# 推荐使用yml进行数据绑定
spring.c
spring:
  banner:
    image:
      location: title.jpeg
  cache:
    type: redis
    redis:
      time-to-live: 11000

image-20220718154700555

结果:

image-20220718155142232

注意:

image-20220718155007244

image-20220718155728445

image-20220718155900548

image-20220718155959607

image-20220718160243036

5-3 自定义类绑定的配置提示

5-3-1 使用

image-20220718162112754

image-20220718160902496

image-20220718161124097

注意:

image-20220718161315464

<!--        对于写Person类自定义绑定数据的application.yml时有对应属性(eg:userName)提示,需要导入以下依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>

5-3-2 打包的时候排除处理器

image-20220718162431145

代码参考:注意(spring-boot-configuration-processor)有“-”

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

结果参考:

image-20220718163604031

image-20220718163829665

第6章 (springboot核心)web开发

image-20220719082344823

image-20220722101006606

image-20220722101023537

6-1 简单功能分析

1、静态资源目录

1-1 直接访问静态资源

只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources

访问 : 当前项目根路径/ + 静态资源名

image-20220719094323556

更正:图中是:这4个目录下的静态资源可以直接访问。

1-2 关于动态资源处理器(Controller)与静态资源处理器处理请求的优先原则

image-20220719095254215

说明:虽然有bug.jpg图片,这是一个静态资源,但是Controller处理器能够处理,所以返回的是字符串aaaa.

image-20220719100144232

1-3 将静态资源访问路径重新定位

image-20220719095944727

image-20220719100643277

image-20220719100753236

1-4 修改静态资源路径

image-20220719112250169

image-20220719112713689

2.欢迎页面

image-20220719113418477

image-20220719113600734

image-20220719113738716

image-20220719114023632

image-20220719114157399

3.自定义网站图标

image-20220719121420401

image-20220719120656734

image-20220719120746516

image-20220719122909839

注意:需要发起新的会话才能有效;静态路径的访问前缀有影响(如下)。

image-20220719123225193

4.静态资源管理之源码分析

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

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

  • image-20220720035234682

  • 给容器中配了什么。

image-20220720040009609

image-20220720040247364

image-20220720040411237

  • 配置文件的相关属性和xxx进行了绑定。WebMvcPropertiesspring.mvc、ResourcePropertiesspring.resources

image-20220720040508925

1、配置类只有一个有参构造器

image-20220720041206112

//有参构造器所有参数的值都会从容器中确定
//ResourceProperties resourceProperties:获取和spring.resources绑定的所有的值的对象
//WebMvcProperties mvcProperties :获取和spring.mvc绑定的所有的值的对象
//ListableBeanFactory beanFactory : Spring的beanFactory
//HttpMessageConverters :找到所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer : 找到 资源处理器的自定义器。=========
//DispatcherServletPath  
//ServletRegistrationBean :  给应用注册Servlet、Filter....
	public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
				ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
				ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
				ObjectProvider<DispatcherServletPath> dispatcherServletPath,
				ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
			this.resourceProperties = resourceProperties;
			this.mvcProperties = mvcProperties;
			this.beanFactory = beanFactory;
			this.messageConvertersProvider = messageConvertersProvider;
			this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
			this.dispatcherServletPath = dispatcherServletPath;
			this.servletRegistrations = servletRegistrations;
		}
2、资源处理的默认规则
规则1:静态资源的禁用
image-20220720043102415

image-20220720043343122

image-20220720043629620

规则2:设置缓存时间

image-20220720044311439

规则3:对webjars请求的设置

image-20220720044752340

image-20220720044912166

补充:这个方法是用来处理静态资源webjars请求的,webjars也是静态资源,是封装为jar包的静态资源,如jquery等,官网:webjars.io

规则4:静态资源的默认路径

image-20220720045304035

补充:也就是相当于重定位静态资源的路径

image-20220720045629832

image-20220720045732849

补充:类似,这是静态资源的类路径

规则5:欢迎页处理规则

image-20220720050800845

image-20220720051431359

规则5:favicon

说明:跟代码没有关系,浏览器默认静态资源重定位路径为/**,当改变该路径时,自然无法找到。

6-2 请求处理---Rest风格

1.基本使用

image-20220720144719859

image-20220720150555683

image-20220720151110117

代码参考:

package com.xu1.boot.controller;

import org.springframework.web.bind.annotation.*;

/**
 * @auther xu
 * @Description
 * @date 2022/07/19 - 9:50:03
 * @Modified By:
 */
@RestController
public class HelloController {
    @RequestMapping(value = "bug.jpg")
    public String hallo01() {
        return "aaaaa";
    }

    /**
     *
     * @Description
     * 使用rest风格---处理get请求
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.GET)
    @GetMapping(value = "/user")
    public String getUser(){
        return "GET-张三";
    }

    /**
     *
     * @Description
     * 使用rest风格---处理post请求
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.POST)
    @PostMapping(value = "/user")
    public String saveUser(){
        return "POST-张三";
    }


    /**
     *
     * @Description
     * 使用rest风格---处理put请求
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    @PutMapping(value = "/user")
    public String putUser(){
        return "PUT-张三";
    }

    /**
     *
     * @Description
     * 使用rest风格---处理delete请求(删除)
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.DELETE)
    @DeleteMapping(value = "/user")
    public String deleteUser(){
        return "DELETE-张三";
    }
}

结果测试:

image-20220720151319704

image-20220720151403797

image-20220720151434989

2.源码探究原理

1.表单

image-20220720160722977

index.html代码参考:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>welcome to index.html</h1>

<h2>测试rest风格</h2>
<form action="/user" method="get">
    <input value="Rest-get" type="submit">
</form>
<form action="/user" method="post">
    <input value="Rest-post" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT">
    <input value="Rest-put" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="delete">
    <input value="Rest-delete" type="submit">
</form>
</body>
</html>

image-20220720160922244

image-20220720161426922

注意:图中只有一个断点进行调试。

image-20220720164130971

image-20220720165405369

补充:也就是说,以后调用getMethod()方法都是使用重写的该方法,返回的值(eg:DELETE)

总结:

image-20220720165858589

2.非表单

image-20220720171143613

image-20220720170832627

image-20220720170945868

3.springboot简化Rest风格的开发

image-20220720171509644

结果:

image-20220720171628355

代码参考:

package com.xu1.boot.controller;

import org.springframework.web.bind.annotation.*;

/**
 * @auther xu
 * @Description
 * @date 2022/07/19 - 9:50:03
 * @Modified By:
 */
@RestController
public class HelloController {
    @RequestMapping(value = "bug.jpg")
    public String hallo01() {
        return "aaaaa";
    }

    /**
     *
     * @Description
     * 使用rest风格---处理get请求
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.GET)
    @GetMapping(value = "/user")
    public String getUser(){
        return "GET-张三";
    }

    /**
     *
     * @Description
     * 使用rest风格---处理post请求
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.POST)
    @PostMapping(value = "/user")
    public String saveUser(){
        return "POST-张三";
    }


    /**
     *
     * @Description
     * 使用rest风格---处理put请求
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    @PutMapping(value = "/user")
    public String putUser(){
        return "PUT-张三";
    }

    /**
     *
     * @Description
     * 使用rest风格---处理delete请求(删除)
     * @auther xu
     */
//    @RequestMapping(value = "/user",method = RequestMethod.DELETE)
    @DeleteMapping(value = "/user")
    public String deleteUser(){
        return "DELETE-张三";
    }
}

4改变Rest请求参数_method

不使用默认的HiddenHttpMethodFilter,自定义一个HiddenHttpMethodFilter,然后将其"_method"变为"_m"

package com.xu1.boot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;

/**
 * @auther xu
 * @Description
 * @date 2022/07/21 - 5:55:51
 * @Modified By:
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }
}

image-20220721061943035

结果:

image-20220721062203730

image-20220721062257998

image-20220721062550652

源码调试分析,可见参数变为了"_m"而不是原来的"_method":

image-20220721061454629

5.请求映射原理(源码分析)

DispatcherServlet

image-20220721080936785

image-20220721081050056

image-20220721081358752

image-20220721081806682

开始分析:

image-20220721083737985

image-20220721083634307

接下里分析,如何找到对应的处理器类的处理器方法处理对应的请求:

image-20220721090141678

image-20220721090230461

image-20220721090307932

image-20220721090521088

image-20220721090621175

image-20220721090730405

image-20220721090808449

image-20220721090837512

image-20220721091128939

image-20220721091420438

image-20220721091944230

以上源码还是自己多调试几遍,原理于SpringMVC中的doDispatcher方法类似。

老师的总结:

image-20220721092505718

6-3 请求处理--常用参数注解的使用

1.@PathVariable

image-20220721140514344

代码参考:

//汽车编号为2号,车主为Tom
@GetMapping(value = "/car/{carId}/owner/{username}")
public Map<String,Object> getCar(@PathVariable("carId") Integer id,
                                @PathVariable("username") String host,
                                 @PathVariable Map<String,String> allMap) {
    Map<String,Object> map = new HashMap<>();
    map.put("carId",id);
    map.put("username",host);
    map.put("allMap",allMap);
    return map;
}
<li><a href="car/2/owner/Tom">@PathVariable</a></li>

输出结果:

image-20220721142319508

image-20220721142704007

2.@RequestHeader

image-20220721143943108

image-20220721142816361

代码参考:

//汽车编号为2号,车主为Tom
@GetMapping(value = "/car/{carId}/owner/{username}")
public Map<String,Object> getCar(@PathVariable("carId") Integer id,
                                 @PathVariable("username") String host,
                                 @PathVariable Map<String,String> allMap,
                                 @RequestHeader("User-Agent") String userAgent,
                                 @RequestHeader Map<String,String> headersMap) {
    //获取请求路径上的值Test
    Map<String,Object> map = new HashMap<>();
    map.put("carId",id);
    map.put("username",host);
    map.put("allMap",allMap);
    //获取请求头信息Test
    map.put("User-Agent",userAgent);
    map.put("headers",headersMap);
    return map;
}
<a href="car/2/owner/Tom">getCar测试</a>

输出结果:

image-20220721143808088

3.@RequestParam

image-20220721145011899

代码参考:

 //汽车编号为2号,车主为Tom
   @GetMapping(value = "/car/{carId}/owner/{username}")
    public Map<String,Object> getCar(
                                     @RequestParam("age") String age,
                                     @RequestParam("inters") List<String> insList,
                                     @RequestParam Map<String,String> parasMap) {
        Map<String,Object> map = new HashMap<>();
        //请求参数值Test
        map.put("age",age);
        map.put("inters",insList);
        map.put("parasMap",parasMap);
        return map;
    }
 <a href="car/2/owner/Tom?age=21&inters=baskaetball&inters=pin-pong">getCar测试</a>

补充:该传值可以使用多选框传值

输出结果:

image-20220721150357775

4.@CookieValue

image-20220721151327271

image-20220721151459623

5.@RequestBody

获取请求体的信息,表单有请求体

代码参考:

<h2>表单提交</h2>
<form action="/save" method="post">
    测试@RequestBody获取数据<br/>
    用户名:<input name="userName"/><br/>
    邮箱:<input name="email"/>
    <input type="submit" value="提交">
</form>
@PostMapping("/save")
public Map<String,Object> postMethod(@RequestBody String formContent) {
    Map<String,Object> map = new HashMap<>();
    map.put("formContent",formContent);
    return map;
}

结果测试:

image-20220721152739256

image-20220721152843391

6.@RequestAttribut--获取请求域中的参数值

package com.xu1.boot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @auther xu
 * @Description
 * @date 2022/07/21 - 17:04:51
 * @Modified By:
 */
@Controller
public class RequestController {

    @GetMapping("/goto")
    public String goToPage(HttpServletRequest request) {
        request.setAttribute("msg","成功了。。。");
        request.setAttribute("code",200);
        return "forward:/success";//转发到 /success请求

    }
    @ResponseBody
    @GetMapping("/success")
    public Map success(@RequestAttribute("msg") String msg,
                       @RequestAttribute("code") Integer code,
                       HttpServletRequest request) {

        //因为是转发,所以还是同一个请求域request
        Object msg1 = request.getAttribute("msg");

        Map<Object, Object> map = new HashMap<>();
        map.put("msg1",msg1);
        map.put("msg",msg);
        return map;
    }
}
<h2>@RequestAttribute获取请求域中参数的值</h2>
<a href="goto">@RequestAttribute</a>

结果参考:

image-20220721171913826

6-4 请求处理之@MatrixVariable注解(矩阵变量)

image-20220721172746881

1.源码分析

image-20220722095633250

image-20220722101535024

image-20220722101756119

补充:我们自定义的配置类优先于springboot默认的配置类,自然方法也优先于重写的方法。

2.代码实践:

开启矩阵变量功能--写法1:
package com.xu1.boot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

/**
 * @auther xu
 * @Description
 * @date 2022/07/21 - 5:55:51
 * @Modified By:
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);//默认是true,禁用掉了矩阵变量功能
        configurer.setUrlPathHelper(urlPathHelper);
    }
}
开启矩阵变量功能--写法2:
@Configuration(proxyBeanMethods = false)
public class WebConfig{
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }

   @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                urlPathHelper.setRemoveSemicolonContent(false);//默认是true,禁用掉了矩阵变量功能
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
   }
}

说明:该配置类实现了WebMvcConfigurer,并重写了configurePathMatch方法,将其矩阵变量功能开启。

3.代码测试

测试1:

image-20220722103950277

    //cars/sell;low=34;brand=Nike,palyboy,LiNi

@GetMapping("/cars/{sell}")//注意将路径上的变量用{}括起来
public Map<String,Object> carSell(@MatrixVariable("low") Integer low,
                                  @MatrixVariable("brand") List<String> brands,
                                  @MatrixVariable Map<String,String> allMatrixMap) {
    Map<String,Object> map = new HashMap<>();
    map.put("low",low);
    map.put("brands",brands);
    map.put("allMatrixMap",allMatrixMap);
    return map;
}
<h2>矩阵变量测试</h2>
<a href="/cars/sell;low=34;brand=Nike,palyboy,LiNi">矩阵变量测试</a>

注意:sell具有矩阵值(分号后面的值),在请求映射中需要用大括号将sell括起来。

输出结果:

image-20220722105227754

测试2:
/**
 *
 * @Description
 * 使用pathVar指定路径名,从而消除路径名中相同矩阵变量名歧义问题
 * @Date 2022/07/22 11:01:29
 *
 */
//boss/1;age=20/2;age=22
@GetMapping("/boss/{bossId}/{emplId}")
public Map<String,Object> bossAndEmpl(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossId,
                                  @MatrixVariable(value = "age",pathVar = "emplId") Integer emplId) {
    Map<String,Object> map = new HashMap<>();
   map.put("bossId",bossId);
   map.put("emplId",emplId);
    return map;
}
<a href="/boss/1;age=20/2;age=22">矩阵变量测试2--pathVar</a>

输出结果:

image-20220722110944704

6-5 对于目标方法中参数解析器----源码分析

调试前提:

 //汽车编号为2号,车主为Tom
    @GetMapping(value = "/car/{carId}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("carId") Integer id,
                                     @PathVariable("username") String host,
                                     @PathVariable Map<String,String> allMap,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> headersMap,
                                     @RequestParam("age") String age,
                                     @RequestParam("inters") List<String> insList,
                                     @RequestParam Map<String,String> parasMap
//                                     @CookieValue("cookieName") String cookieName,
//                                     @CookieValue("cookieName") Cookie cookie) {
                                                                                ){
//        //获取请求路径上的值Test
        Map<String,Object> map = new HashMap<>();
//        map.put("carId",id);
//        map.put("username",host);
//        map.put("allMap",allMap);
//        //获取请求头信息Test
//        map.put("User-Agent",userAgent);
//        map.put("headers",headersMap);

        //请求参数值Test
        map.put("age",age);
        map.put("inters",insList);
        map.put("parasMap",parasMap);

        return map;
    }
<a href="car/2/owner/Tom?age=21&inters=baskaetball&inters=pin-pong">getCar测试</a>

正式调试:

image-20220722144242212

image-20220722143653446

image-20220722144728108

真正执行目标方法:

image-20220722145406031

image-20220722145432447

image-20220722145606753

image-20220722145937226

image-20220722150237152

image-20220722150605881

image-20220725143653244

image-20220726092345416

image-20220726092755291

image-20220726092954377

image-20220726093201126

接下来重点分析获取目标方法参数的值:

image-20220726093354821

image-20220726093609320

源码分析:

/**
 * Get the method argument values for the current request, checking the provided
 * argument values and falling back to the configured argument resolvers.
 * <p>The resulting array will be passed into {@link #doInvoke}.
 * @since 5.1.2
 */
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   MethodParameter[] parameters = getMethodParameters();
   if (ObjectUtils.isEmpty(parameters)) {
      return EMPTY_ARGS;
   }

   Object[] args = new Object[parameters.length];
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      args[i] = findProvidedArgument(parameter, providedArgs);
      if (args[i] != null) {
         continue;
      }
      if (!this.resolvers.supportsParameter(parameter)) {
         throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
      }
      try {
         args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
      }
      catch (Exception ex) {
         // Leave stack trace for later, exception may actually be resolved and handled...
         if (logger.isDebugEnabled()) {
            String exMsg = ex.getMessage();
            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
               logger.debug(formatArgumentError(parameter, exMsg));
            }
         }
         throw ex;
      }
   }
   return args;
}

image-20220726095015851

查看26中解析器是否支持解析该方法参数:

image-20220726095150474

image-20220726095212492

image-20220726095510224

image-20220726102802888

image-20220726103341968

image-20220726103442506

调用解析器的方法去解析方法参数:

image-20220726103745524

image-20220726104005627

image-20220726104836185

image-20220726105033477

image-20220726105901528

image-20220726110048709

image-20220726110129474

image-20220726110218207

image-20220726110254230

image-20220726110330973

image-20220726110358484

6-6 Servlet API参数(方法中原生的servlet参数)解析原理---源码分析

Servlet API参数:

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

image-20220727052351735

image-20220727052959922

image-20220727053839057

6-7 复杂参数(以Map、Model分析为例)

MapModel(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

1.初步实验

 @GetMapping("/params")
    public String testParam(Map<String,Object> map,
                            Model model,
                            HttpServletRequest request,
                            HttpServletResponse response) {
        map.put("hello","world666");
        model.addAttribute("world","hello666");
        request.setAttribute("message","HelloWorld");

        Cookie cookie = new Cookie("c1", "v1");
        cookie.setDomain("localhost");
        response.addCookie(cookie);
        return "forward:/success";
    }
    @ResponseBody
    @GetMapping("/success")
    public Map success(@RequestAttribute(value = "msg",required = false) String msg,
                       @RequestAttribute(value = "code",required = false) Integer code,
                       HttpServletRequest request) {
        //因为是转发,所以还是同一个请求域request
        Object msg1 = request.getAttribute("msg");

        Map<Object, Object> map = new HashMap<>();
        map.put("msg1",msg1);
        map.put("msg",msg);


        //获取请求域中的值
        Object hello = request.getAttribute("hello");
        Object world = request.getAttribute("world");
        Object message = request.getAttribute("message");
        //将请求域中的值放入到map中
        map.put("hello",hello);
        map.put("world",world);
        map.put("message",message);

        return map;
    }

结果:

image-20220727110636879

image-20220727110901595

2.源码分析

1.第一个参数---解析Map类型参数的解析:
testParam(Map<String,Object> map,
                            Model model,
                            HttpServletRequest request,
                            HttpServletResponse response)

image-20220727112020865

image-20220727112901155

image-20220727113206288

image-20220727120008369

image-20220727120122920

image-20220727120332757

image-20220727120531441

image-20220727120449512

2.第2个参数---解析Model类型参数的解析器:
testParam(Map<String,Object> map,
                            Model model,
                            HttpServletRequest request,
                            HttpServletResponse response)

image-20220727120806284

image-20220727120823852

image-20220727121000565

image-20220727121235682

image-20220727121310349

image-20220727121606137

补充:BindingAwareModelMap

image-20220727122332268

image-20220727154343382

接下来查看数据如何存放到请求域中的:

image-20220727155210420

image-20220727154849622

image-20220727155641932

image-20220727155823214

image-20220727160353219

image-20220727160612105

image-20220727161657180

image-20220727161848985

image-20220727162009381

image-20220727162151565

image-20220727162400878

image-20220727162850633

image-20220727163104131

image-20220727163843321

image-20220727164048472

image-20220727164331743

image-20220727164722778

image-20220727164952498

image-20220729043708996

image-20220729043920578

image-20220729044124991

image-20220729044422126

image-20220729044707465

6-8 自定义参数绑定原理分析

1.条件准备:

<h2>测试封装POJO</h2>
<form action="/saveuser" method="post">
    姓名:<input name="usrName" value="zhang3"><br/>
    年龄:<input name="age" value="22"><br/>
    生日:<input name="birth" value="2022/12/12"><br/>
    宠物姓名:<input name="pet.name" value="阿猫"><br/>
    宠物年龄:<input name="pet.age" value="5"><br/>
    <input type="submit" value="保存"/>
</form>
/**
 *
 * @Description
 * 用于测试自定义类型的参数封装
 * @Date 2022/07/29 5:13:31
 *
 */
@PostMapping(value = "/saveuser")
public Person saveuser(Person person) {
    return person;
}

输出结果:

image-20220729051512689

image-20220729051623360

2.源码分析

image-20220729052011215

image-20220729052136716

image-20220729052249473

image-20220729052524452

image-20220729052745479

image-20220729052817242

image-20220729052938819

image-20220729053206203

image-20220729053546840

image-20220729054445149

image-20220729054512299

image-20220729054827313

image-20220729055115340

image-20220729055331737

image-20220729055526093

image-20220729083059502

以下为核心重要代码分析:

源码参考:

@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
   Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

   String name = ModelFactory.getNameForParameter(parameter);
   ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
   if (ann != null) {
      mavContainer.setBinding(name, ann.binding());
   }

   Object attribute = null;
   BindingResult bindingResult = null;

   if (mavContainer.containsAttribute(name)) {
      attribute = mavContainer.getModel().get(name);
   }
   else {
      // Create attribute instance
      try {
         attribute = createAttribute(name, parameter, binderFactory, webRequest);
      }
      catch (BindException ex) {
         if (isBindExceptionRequired(parameter)) {
            // No BindingResult parameter -> fail with BindException
            throw ex;
         }
         // Otherwise, expose null/empty value and associated BindingResult
         if (parameter.getParameterType() == Optional.class) {
            attribute = Optional.empty();
         }
         bindingResult = ex.getBindingResult();
      }
   }

   if (bindingResult == null) {
      // Bean property binding and validation;
      // skipped in case of binding failure on construction.
      WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
      if (binder.getTarget() != null) {
         if (!mavContainer.isBindingDisabled(name)) {
            bindRequestParameters(binder, webRequest);
         }
         validateIfApplicable(binder, parameter);
         if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
         }
      }
      // Value type adaptation, also covering java.util.Optional
      if (!parameter.getParameterType().isInstance(attribute)) {
         attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
      }
      bindingResult = binder.getBindingResult();
   }

   // Add resolved attribute and BindingResult at the end of the model
   Map<String, Object> bindingResultModel = bindingResult.getModel();
   mavContainer.removeAttributes(bindingResultModel);
   mavContainer.addAllAttributes(bindingResultModel);

   return attribute;
}

image-20220729083439784

image-20220729084316533

image-20220729084536317

总结:WebDataBinder利用其中的124个转换器(converters)将HTTP协议请求发送过来的文本转变为指定的类型数据,然后利用反射将数据封装到空参的attribute对象中。

image-20220729085146391

image-20220729085633508

补充:

image-20220729090941909

6-9 自定义Converter原理

1.使用

未来我们可以给WebDataBinder里面放自己的Converter;
private static final class StringToNumber<T extends Number> implements Converter<String, T>

image-20220729095548101

image-20220729100905133

重写WebMvcConfigurer接口的方法,增添一个自定义的转换器:

package com.xu1.boot.config;

import com.xu1.boot.bean.Pet;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.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;

/**
 * @auther xu
 * @Description
 * @date 2022/07/21 - 5:55:51
 * @Modified By:
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }

    /**
     *
     * @Description
     * 开启矩阵变量的功能
     * @Date 2022/07/29 9:49:15
     *
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);//默认是true,禁用掉了矩阵变量功能
        configurer.setUrlPathHelper(urlPathHelper);
    }

    /**
     *
     * @Description
     * 增加类型转换器
     * 将字符串---"阿猫,5",转变为Pet类型
     * @Date 2022/07/29 9:53:03
     *
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter<String, Pet>() {

            @Override
            public Pet convert(String source) {

                //org.springframework.util public abstract class StringUtils
                //已利用spring的字符串工具类判断被转换的类不是空,方可进行数据所对应类型的转换

                if (!StringUtils.isEmpty(source)) {
                    Pet pet = new Pet();

                    String[] split = source.split(",");
                    //将"阿猫,5"利用正则表达式分割存入到数组中,数组中第一个值为猫的name,第2个值为猫的age
                    pet.setName(split[0]);
                    pet.setAge(split[1]);
                    return pet;
                }
                return null;
            }
        });

    }

}
//@Configuration(proxyBeanMethods = false)
//public class WebConfig{
//    @Bean
//    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
//        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
//        hiddenHttpMethodFilter.setMethodParam("_m");
//        return hiddenHttpMethodFilter;
//    }
//
//   @Bean
//    public WebMvcConfigurer webMvcConfigurer() {
//        return new WebMvcConfigurer() {
//            @Override
//            public void configurePathMatch(PathMatchConfigurer configurer) {
//                UrlPathHelper urlPathHelper = new UrlPathHelper();
//                urlPathHelper.setRemoveSemicolonContent(false);//默认是true,禁用掉了矩阵变量功能
//                configurer.setUrlPathHelper(urlPathHelper);
//            }
//        };
//   }
//}

输出结果:

image-20220729101203219

image-20220729101428510

2.源码分析

image-20220729102508800

image-20220729102640271

image-20220729102831322

image-20220729103137088

总结:页面传送过来参数,需要用对应的解析器进行解析方法参数,在解析时,需要将HTTP传送过来的文本转换为对应的目标类型,此时需要用到对应的转换器,没有对应的转换器就需要自己定义一个转换器。

第6章 (Springboot核心)web开发--第2部分

6-10 响应数据

6-10-1 ReturnValueHandler原理分析--源码

1.使用

返回json数据所需依赖,在jar包中:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-json</artifactId>
  <version>2.3.7.RELEASE</version>
  <scope>compile</scope>
</dependency>

注意:如果使用了页面返回xml的依赖,那么页面将会以xml的形式返回,不会返回json

上述依赖中使用:

image-20220801090859980

测试结果:

image-20220801093139038

注意:还需要标注@ResponseBody注解,即可返回json数据

2源码探究返回json数据:

image-20220801093350497

发送请求,开始调试:

image-20220801093530625

image-20220801093600053

image-20220801093622828

image-20220801093702264

image-20220801093933852

image-20220801094128613

image-20220801094350449

image-20220801094239761

image-20220801094501887

image-20220801094733200

image-20220801094944216

image-20220801095153253

image-20220801095640771

image-20220801095853705

image-20220801100126178

image-20220801101303933

image-20220801102346005

3.找到处理器,接下来使用返回值处理器处理返回值

image-20220806101546479

image-20220806101757173

image-20220806102542441

image-20220806102408376

1.内容协商:

image-20220806102933102

image-20220806103228455

image-20220807064339025

image-20220807065406656

image-20220807065910735

image-20220807084221081

2.HTTPMessageConverter原理:

image-20220807090347128

image-20220807085901159

image-20220807090158843

image-20220807090545971

image-20220807111244254

image-20220807111524390

image-20220807112037084

image-20220807112306410

补充:每一个转换器都有支持特定的媒体类型。服务器能够响应json,而浏览器能够接受json类型,那么就判断该转换器ok.

image-20220808043040299

image-20220808043421823

image-20220808043830234

image-20220808044129503

(相应改为响应)

image-20220808050404665

总结:

image-20220808050558922

image-20220808052539465

6-11 相应处理--内容协商原理

1.将数据以xml形式返回

image-20220808074050232

image-20220808074240913

使用Apifox:

image-20220808074809324

image-20220808075000440

2.分析xml形式返回数据的原理

image-20220808075355031

image-20220808075516776

image-20220808080135047

image-20220808080306645

image-20220808080355107

image-20220808080452211

image-20220808080824863

image-20220808080902328

image-20220808094704548

image-20220808094807747

image-20220808095725781

image-20220808100238446

image-20220808100336956

image-20220808100356859

image-20220808100447892

image-20220808100528314

image-20220808100756727

image-20220808101214186

补充说明:

image-20220808101853873

image-20220808102046059

image-20220808102648330

image-20220808103100338

image-20220808103225949

image-20220808104415573

image-20220808104806695

image-20220808104945363

补充说明:

image-20220808105822083

老师总结:

image-20220808105956766

image-20220808110024446

image-20220808110035526

6-12 基于请求参数的内容协商(也就是说再请求参数中指定客户端接受哪种媒体类型)

1.基本使用

image-20220809060439192

结果测试:

image-20220809061206206

image-20220809061348177

2.源码分析

image-20220809075728254

image-20220809075920004

image-20220809080107269

image-20220809080200025

image-20220809080449886

image-20220809080537687

image-20220809080857687

image-20220809081403752

image-20220809081811389

image-20220809081913742

image-20220809082352207

6-13 自定义MessageConverter

image-20220809104414545

image-20220809110045586

查看源码,底层自定义的converter:

image-20220809110353290

image-20220914134710324

image-20220914134839114

image-20220914134946005

image-20220914135312222

image-20220914135432289

image-20220914135530712

image-20220914135655435

image-20220914140053979

image-20220914140312315

image-20220914140505071

自定义MessageConverter:

image-20220914144151194

package com.xu1.boot.converter;

import com.xu1.boot.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 xu
 * @Description
 * @date 2022/09/14 - 14:08:42
 * @Modified By:
 */
public class GuiguMessageConverter implements HttpMessageConverter<Person> {

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

    @Override
    public boolean canWrite(Class clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Person.class);//只要是Person类型,就能够进行读写
    }

    /**
     *
     * @Description
     * 服务器要统计所有MessageConverter都能写出哪些内容类型(eg:application/x-guigu)
     * @param
     * @Author xu
     * @Date 2022/09/14 14:13:22
     *
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-guigu");
    }

    @Override
    public Person read(Class 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.getBirth();//响应的数据以“;”分割

        //将得到数据以流的形式响应出去
        OutputStream body = outputMessage.getBody();
        body.write(data.getBytes());
    }
}

image-20220914144342930

结果:

image-20220914144905082

源码调试参考6-12中,可以看到多了自己自定义的一个转换器--GuiguMessageConverter

6-14 以请求参数的方式指定返回按一种媒体类型

回顾:浏览器确定接收哪一种数据类型2种方式:一种是基于请求头,另一种是基于请求参数(所谓请求参数,也就是url携带的参数)

image-20220915141906511

image-20220915141637819

image-20220915142957968

源码参考:

package com.xu1.boot.config;

import com.xu1.boot.bean.Person;
import com.xu1.boot.bean.Pet;
import com.xu1.boot.converter.GuiguMessageConverter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @auther xu
 * @Description
 * @date 2022/07/21 - 5:55:51
 * @Modified By:
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_m");//支持Rest风格,将其“_method”改为“_m“
        return hiddenHttpMethodFilter;
    }

    /**
     *
     * @Description
     * 开启矩阵变量的功能
     * @Date 2022/07/29 9:49:15
     *
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);//默认是true,禁用掉了矩阵变量功能
        configurer.setUrlPathHelper(urlPathHelper);
    }

    /**
     *
     * @Description
     * 增加类型转换器
     * 将字符串---"阿猫,5",转变为Pet类型
     * @Date 2022/07/29 9:53:03
     *
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter<String, Pet>() {

            @Override
            public Pet convert(String source) {

                //org.springframework.util public abstract class StringUtils
                //已利用spring的字符串工具类判断被转换的类不是空,方可进行数据所对应类型的转换

                if (!StringUtils.isEmpty(source)) {
                    Pet pet = new Pet();

                    String[] split = source.split(",");
                    //将"阿猫,5"利用正则表达式分割存入到数组中,数组中第一个值为猫的name,第2个值为猫的age
                    pet.setName(split[0]);
                    pet.setAge(split[1]);
                    return pet;
                }
                return null;
            }
        });

    }

    /**
     *
     * @Description
     * 扩展自定义的转换器
     * @param
     * @Author xu
     * @Date 2022/09/14 14:28:35
     *
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new GuiguMessageConverter());
    }

    /**
     *
     * @Description 基于请求参数返回对应的媒体类型
     * 自定义内容协商策略
     * @param
     * @Author xu
     * @Date 2022/09/15 13:54:49
     *
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        Map<String, MediaType> mediaTypes = new HashMap<>();
        //http://localhost:8080/test/person?format=json
        //如果浏览器以参数format形式传过来json,说明浏览器只接收json类型的数据--"json",那么服务器就将数据以json的媒体类型返回--MediaType.APPLICATION_JSON
        mediaTypes.put("json",MediaType.APPLICATION_JSON);
        mediaTypes.put("xml",MediaType.APPLICATION_XML);
        //如果浏览器发送的是format=gg这个参数,服务器就给它返回x-guigu媒体类型的数据
        mediaTypes.put("gg",MediaType.parseMediaType("application/x-guigu"));

        //指定支持解析那些参数对应的那些媒体类型
        ParameterContentNegotiationStrategy parameterContentNegotiationStrategy = new ParameterContentNegotiationStrategy(mediaTypes);
        //基于请求头的对应的媒体类型
        HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();

        configurer.strategies(Arrays.asList(parameterContentNegotiationStrategy,headerContentNegotiationStrategy));
    }

}
//@Configuration(proxyBeanMethods = false)
//public class WebConfig{
//    @Bean
//    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
//        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
//        hiddenHttpMethodFilter.setMethodParam("_m");
//        return hiddenHttpMethodFilter;
//    }
//
//   @Bean
//    public WebMvcConfigurer webMvcConfigurer() {
//        return new WebMvcConfigurer() {
//            @Override
//            public void configurePathMatch(PathMatchConfigurer configurer) {
//                UrlPathHelper urlPathHelper = new UrlPathHelper();
//                urlPathHelper.setRemoveSemicolonContent(false);//默认是true,禁用掉了矩阵变量功能
//                configurer.setUrlPathHelper(urlPathHelper);
//            }
//        };
//   }
//}

第7章 视图解析与模板引擎

7-1 使用thymeleaf作为视图的渲染

image-20220915150443386

缺点:对于高并发的项目,不要使用该模板渲染,使用别的模板渲染

7-2 使用thymeleaf

第一步:引入依赖:

<!-- 官方引入thymeleaf的场景依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

image-20220915151308306

第二步:在templates路径下创建.html文件

image-20220915151702200

image-20220915152044832

更正:名称空间

第3步:测试

image-20220915154703935

image-20220915154731465

结果:

image-20220915154628615

补充:@取值的另一个妙用

image-20220915155153639

第8章--web实验

8-1:后台管理的基本功能

8-2:抽取公共页面

引用公共页面方式1:

image-20220916092554173

引用公共页面方式2:

image-20220916093030448

不同方式将页面引入将会导致不同的结果:

image-20220916101052306

8-3:遍历数据与页面bug修改

8-4 视图解析器与视图源码分析

第1部分:

image-20220916131314427

image-20220916131421212

image-20220916131715904

image-20220916131803086

image-20220916131955372

image-20220916132140874

image-20220916132258697

分析ha.handle()方法:

image-20220916132400563

image-20220916132451593

image-20220916132911860

image-20220916133201028

image-20220916133342669

image-20220916133437100

image-20220916133623252

image-20220916133809428

image-20220916134004122

image-20220916134139415

image-20220916135055877

image-20220916135154475

image-20220916140235730

image-20220916140315066

image-20220916140423861

image-20220916140825811

image-20220916141139295

image-20220916141307909

image-20220916141536310

image-20220916141656230

image-20220916143221834

image-20220916144113675

补充:都封装到ModelAndView对象中。

image-20220916144718112

image-20220916145108690

image-20220916145401669

image-20220916145610150

image-20220916151914040

image-20220916152034517

视图解析器解析得到视图对象:

image-20220916152540489

image-20220916152758623

image-20220916153523416

image-20220916154009668

image-20220916154618272

image-20220916162059904

image-20220916162429500

image-20220916162615756

如此完成了重定向的操作。

第2部分:探究dynamic_table目标方法如何进行数据填充的(不是很懂---源码太难)

image-20220916163452967

image-20220916163804420

image-20220917132607258

image-20220917132823047

image-20220917132924011

image-20220917133240412

image-20220917133558316

补充:

image-20220916165730198

省略步骤见第一部分。。。。。

image-20220917135931852

上图补充:如果是普通字符串,会返回一个Thymeleaf视图对象。

image-20220917140347211

image-20220917140957266

image-20220917141145091

第8章 拦截器

8-1 登录检查与静态资源放行

image-20220917142820765

第一步:实现一个拦截器

package com.xu.admin.interceptor;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.swing.*;

/**
 * @author xu
 * @Description
 *
 * 使用拦截器的步骤:
 * 1.编写一个拦截器实现HandlerInterceptor接口
 * 2.将拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3.指定拦截器规则,如果是拦截所有,静态资源也会被拦截
 *
 * @date 2022/09/17 - 14:36:14
 * @Modified By:
 */
public class LoginInterceptor implements HandlerInterceptor {


    /**
     *
     * @Description 目标方法执行之前
     * @param
     * @Author xu
     * @Date 2022/09/17 14:36:55
     *
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        //只要进行了登录,session中就会存放一个user对象,只要该对象不为空,不必进行登录拦截,否则就需要进行登录拦截
        if (user != null) {
            return true;
        }
        //拦截住了,表示没有登录,需要返回登录页面,可以重定向到登录页面
        session.setAttribute("message","请先登录");

        //使用重定向到当前的登录页面
//        response.sendRedirect("/");

        //使用转发,便于取到信息
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     *
     * @Description 目标方法执行之后
     * @param
     * @Author xu
     * @Date 2022/09/17 14:37:27
     *
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {


    }

    /**
     *
     * @Description 页面渲染以后
     * @param
     * @Author xu
     * @Date 2022/09/17 14:38:15
     *
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

第2步:将拦截器注册到容器中:

package com.xu.admin.config;

import com.xu.admin.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author xu
 * @Description
 * @date 2022/09/17 - 14:44:56
 * @Modified By:
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {


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

实验结果:

没有登录,导致被拦截,转发到登录页面:

image-20220917152639974

登录之后,成功进入主页面:

image-20220917152905212

8-2 拦截器原理:

image-20220917160343087

image-20220917160405501

image-20220917160440788

image-20220917160746830

拦截器源码分析:

image-20220917161336483

image-20220917161504600

image-20220917161623283

image-20220917163216389

image-20220917163449648

补充:由于我没有登录,导致拦截器拦截成功,preHandle()方法返回true,也就导致无法进入到该if语句中。

image-20220917162950176

image-20220917163844209

image-20220917164029716

image-20220917164227282

image-20220917164314718

补充:页面渲染完成之后,也会出发拦截器的afterCompletion方法

image-20220917164616374

image-20220917164748588

image-20220917164829742

image-20220917165006765

总结:

image-20220917165043241

第9章:文件上传

9-1 单文件与多文件上传的使用

关于文件上传的限制:

image-20220920151149143

image-20220920150111638

image-20220920163213580

关于异步上传文件所报异常及解决办法参考:

https://www.cnblogs.com/rong-xu-drum/p/16711478.html

输出结果:

image-20220920162706543

源码参考:

@PostMapping(value = {"/upload"})
public String upLoad(@RequestParam("email") String email,
                     @RequestParam("username") String username,
                     @RequestPart("headerImg")MultipartFile headerImg,
                     @RequestPart("photos") MultipartFile[] photos) throws Exception {
    log.info("上传的信息为:email={},username={},headerImg={},photos={}",
             email,username,headerImg.getSize(),photos.length);

    if (!headerImg.isEmpty()) {
        String originalFilename = headerImg.getOriginalFilename();
        File file = new File("D:\\Java\\springBoot\\imgFileTest\\" + originalFilename);
        // headerImg.transferTo(file); 使用该方法,会由于异步上传导致文件上传出错
        FileUtils.copyInputStreamToFile(headerImg.getInputStream(),file);
    }
    for (MultipartFile imgFile: photos
        ) {

        if (!imgFile.isEmpty()) {
            String originalFilename = imgFile.getOriginalFilename();
            File file = new File("D:\\Java\\springBoot\\imgFileTest\\" + originalFilename);
            //imgFile.transferTo(file); 使用该方法,会由于异步上传导致文件上传出错
            FileUtils.copyInputStreamToFile(imgFile.getInputStream(),file);
        }
    }
    return "main";
}

9-2 上传文件源码分析

image-20220920164539280

image-20220920164744172

image-20220920165037344

image-20220920165519377

image-20220920170409300

image-20220920190714357

image-20220920191212171

image-20220920191355902

image-20220920191636279

image-20220920191817376

image-20220920191841273

image-20220920191918535

image-20220920192655532

image-20220920192804514

image-20220920192945088

image-20220920193213274

image-20220920193846601

接下来就是遍历所有的参数解析器,看看支不支持解析该文件上传类型参数:

image-20220920194031688

image-20220920194057439

image-20220920194756111

image-20220920194843010

image-20220920194952803

image-20220920195043219

image-20220920200118749

image-20220920200226216

image-20220920200433590

image-20220920200702811

image-20220920200859827

image-20220920201136666

image-20220920201807417

老师总结:

image-20220920202408533

第10章--错误处理

10-1 错误处理时:对于浏览器客户端,发送一个错误请求,会返回一个错误页面;对于非浏览器客户端,发送一个错误请求,会响应json数据。

如下:浏览器响应错误页面

image-20220921143847816

如下:非浏览器响应json数据

image-20220921145200644

image-20220921151455831

image-20220921151321057

10-2 响应自定义的错误页面

image-20220921160429862

结果测试:

image-20220921160547087

10-3底层组件功能的源码分析

image-20220921161521080

image-20220921161954550

image-20220921162353542

image-20220921163001696

更正:响应的媒体数据类型是text/html

image-20220921163155984

image-20220921164212562

image-20220924093852019

image-20220921164740906

容器中的组件DefaultErrorViewResolver:

image-20220924100159911

image-20220924100514064

image-20220924100916364

image-20220924101038137

接下来分析DefaultErrorAttribute:

image-20220924101231868

image-20220924101633121

image-20220924101859018

总结:

image-20220924102828817

image-20220924102926663

10-4 异常处理的源码流程分析

image-20220924105214493

image-20220924105055075

image-20220924105517383

image-20220924105715776

image-20220924105959978

image-20220924110302328

各个异常解析器解析异常的原理探索:

第一个异常处理器:

image-20220924110457373

image-20220924110810285

image-20220924110941504

第二个异常处理器:HandleExceptionResolverComprosite(该解析器有包含3个异常解析器,见上图)

第一个解析器:

image-20220924132702569

image-20220924133019334

image-20220924133108086

image-20220924133148189

image-20220924133247476

image-20220924133403601

image-20220924133603154

image-20220924133653815

image-20220924133838784

更正:应该是@ExceptionHandle注

第二个解析器:

image-20220924134131667

第三个解析器:

image-20220924134442646

image-20220924134829027

综上:遍历默认的异常解析器,没有一个异常解析器可以解析异常,就会将异常抛出去。

image-20220924135657628

image-20220924135943854

image-20220924140206973

image-20220924140554538

image-20220924141131781

image-20220924142626489

image-20220924142732596

image-20220924142958559

image-20220924143319624

image-20220924143458562

image-20220924143700710

补充:也就是模板引擎响应对应的页面。

总结:

image-20220924144200730

image-20220924144218396

10-5:几种异常处理原理

image-20220924153835691

1.自定义错误页:

image-20220924154957421

2.@ControllerAdvice+@ExceptionHandler处理异常:

image-20220924161420654

image-20220924161328431

image-20220924161542399

image-20220924161630187

image-20220924161748291

image-20220924161944189

image-20220924162110601

image-20220924162131514

image-20220924162552447

image-20220924162831273

代码参考:

package com.xu.admin.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * @author xu
 * @Description
 * @date 2022/09/24 - 15:59:15
 * @Modified By:
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
    public String handleArithException(Exception e) {

        log.error("异常是:{}",e);
        //也可以返回一个ModelAndView对象,此处直接返回一个视图地址
        return "login";
    }
}

3.@ResponseStatus+自定义异常

image-20220924164916198

image-20220924165127764

image-20220924165159787

image-20220924165304116

image-20220924165450889

image-20220924165513463

image-20220924165840841

image-20220924165935533

4.Spring底层的异常,eg:请求传参所引发的异常

image-20220924170805530

image-20220924171152942

image-20220924172349462

image-20220924171457604

image-20220924171552777

image-20220924171725593

渲染即可:

image-20220924171828275

image-20220924172918674

image-20220924173634333

image-20220924173712277

image-20220924173759334

至此:异常处理分析结束。

第11章 原生组件注入

11-1方式1:

image-20220926134646419

image-20220926134743869

image-20220926134818518

image-20220926134853019

输出结果参考:

image-20220926135017161

image-20220926134928225

11-2 方式2:

说明:注意实现将@WebServlet,@FilterServlet,@WebListener注释的情况下来探索方式2如何进行原生组件注入

image-20220926140703468

代码实现:

原生实体类:

MyServlet.java

package com.xu.admin.servlet;

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 xu
 * @Description
 * @WebServlet:该注解相当于告诉springboot,这个类相当于原先的web.xml,好像不需要登录就可以发送该请求,也就是没有受拦截器控制
 * @date 2022/09/26 - 13:21:10
 * @Modified By:
 */
//@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.getWriter().write("6666");
    }
}

MyFilter.java:

package com.xu.admin.servlet;


import lombok.extern.slf4j.Slf4j;

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

/**
 * @author xu
 * @Description
 * @date 2022/09/26 - 13:33:07
 * @Modified By:
 */
@Slf4j
//@WebFilter(urlPatterns = {"/css/*","/images/*"})
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销毁");
    }
}

MyServletContentListener.java:

package com.xu.admin.servlet;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author xu
 * @Description
 * @date 2022/09/26 - 13:38:40
 * @Modified By:
 */
@Slf4j
//@WebListener
public class MyServletContentListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("MyServletContentListener监听到项目初始化完成");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyServletContentListener监听得到项目销毁");
    }
}

配置类实现上述实体类的组件注册:

package com.xu.admin.servlet;

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;
import java.util.EventListener;

/**
 * @author xu
 * @Description
 * @date 2022/09/26 - 13:59:38
 * @Modified By:
 */
@Configuration
public class MyRegistConfig {
    @Bean
    public ServletRegistrationBean myServlet() {
        MyServlet myServlet = new MyServlet();
        //一个Servlet可以有多条映射路径
        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }

    @Bean
    public FilterRegistrationBean myFilter() {
        MyFilter myFilter = new MyFilter();
        //如果这样写的话,该拦截器拦截的路径和Servlet中映射的路径一样
//        return new FilterRegistrationBean(myFilter,myServlet());

        FilterRegistrationBean<MyFilter> myFilterFilterRegistrationBean = new FilterRegistrationBean<>(myFilter);
        //现在过滤的路径就是指定的("/my","/css/*")路径
        myFilterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
        return myFilterFilterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener() {
        MyServletContentListener myServletContentListener = new MyServletContentListener();
        ServletListenerRegistrationBean<EventListener> myServletListenerRegistrationBean = new ServletListenerRegistrationBean<>(myServletContentListener);
        return myServletListenerRegistrationBean;

    }
}

一样达到原始组件注册的目的:

image-20220926143215694

补充:

image-20220926143630314

11-3 源码分析:以DispatchServlet为例

image-20220926145735679

image-20220926145920937

image-20220926150124988

image-20220926150254356

image-20220926144830911

image-20220926145048988

第12章--嵌入式Servlet容器,切换web服务器与定制化(源码分析)

12-1 tomcat启动源码探究

image-20220926150922501

image-20220926153337432

image-20220926153713059

image-20220926154818128

image-20220926155049804

image-20220926155710065

创建web服务器断点调试:

image-20220926160208589

image-20220926161217265

image-20220926161247046

image-20220926161327904

image-20220926161459017

image-20220926161648749

image-20220926161915394

image-20220926162211973

image-20220926163404242

注意:TomcatWebServer实现了WebServer接口

image-20220926163208202

从tomcat服务器切换为undertow服务器:

image-20220926195237879

image-20220926195539162

结果:

image-20220926195633915

补充:一般还是使用tomcat服务器。

12-2 定制化容器:

image-20220926201110053

image-20220926201145726

image-20220926200512945

12-3 定制化原理--springboot定制化组件的几种方式:

1.对于特定的请求响应特定路径下的资源:

image-20220929151733081

image-20221005151717866

image-20221005151424309

代码参考:

package com.xu.admin.config;

import com.xu.admin.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author xu
 * @Description
 *
 * @EnableWebMvc:
 * 如果使用该注解,那么底层的默认规则(对资源处理的规则)就会失效(一定要慎用),可以自定义自己的访问规则
 * 1.静态资源?视图解析器?欢迎页。。。全部失效
 * @date 2022/09/17 - 14:44:56
 * @Modified By:
 */
@EnableWebMvc
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        /**
         *
         * @Description 定义了静态资源规则如下
         * 访问    /aa/**所有请求都去classpath:/static/下面进行匹配
         *
         */
        registry.addResourceHandler("/aa/**").addResourceLocations("classpath:/static/");
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                //表示拦截所有请求
                .addPathPatterns("/**")
                //不要拦截的请求
                //将css,fonts,images,js静态资源放行(不拦截),将"/aa/**"所发的所有请求也不拦截
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**","/aa/**");
    }
}

2.源码探索:

image-20221005151244953

image-20221005151916220

image-20221005152959992

image-20221005153240054

总结:

image-20221005152841202

第13章 数据访问

13-1 数据库场景的自动配置分析与整合测试

第一步:导入场景

image-20221005155756093

image-20221005162614111

image-20221005163406362

<!--        导入springbooot的jdbc依赖,实现对数据库的访问-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
            <scope>test</scope>
        </dependency>
<!--        由于不知道使用哪一家厂商的数据库,所以上面的依赖并没有导入数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
<!--            <version>8.0.27</version>-->
        </dependency>

第二步:自动配(连向哪一个数据库,用户名是哪个)

image-20221005165722462

image-20221005170323945

测试:结果参考

image-20221005172600565

自动配置类老师总结:

image-20221005171632961

13-2自定义方式整合Druid数据源

1.整合第3方技术方式1:自定义方式(纯手工)

第一步:导入数据源依赖

<!--  导入Druid数据源  -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.9</version>
</dependency>

image-20221006144634131

image-20221006145608475

2.Druid监控页面与统计功能

image-20221006151724498

实验测试:

image-20221006152514178

image-20221006152654879

结果参考:

image-20221006152825064

3.配置_配置WebStatFilter

image-20221006153341151

由于每一次发送"/sql"请求我都需要登录,先到拦截器将/sql请求放行:

image-20221006153525128

image-20221006154418230

image-20221006161501690

4.配置 wallfilter

image-20221006154703402

image-20221006154857361

实验结果:

image-20221006155143979

5.配置监控页面访问密码

image-20221006164900398

image-20221006165934457

image-20221006165902953

补充的小技巧:

image-20221006170532625

结果:

image-20221006170501456

13-3 druid数据源start整合方式:

image-20221007143147835

image-20221007142930021

image-20221007143235275

image-20221007144859852

image-20221007145333792

分析导入了那些类:

image-20221007145554395

@Import({DruidSpringAopConfiguration.class,
    DruidStatViewServletConfiguration.class,
    DruidWebStatFilterConfiguration.class,
    DruidFilterConfiguration.class})

第1,DruidSpringAopConfiguration.class:

image-20221007145840904

第2,DruidStatViewServletConfiguration.class:

image-20221007150144192

image-20221007151913778

第3,DruidWebStatFilterConfiguration.class:

image-20221007150441489

image-20221007152639607

第4,DruidFilterConfiguration.class:

image-20221007150816544

stat:web监控属性

image-20221007154226561

image-20221007153938658

wall:防火墙的配置

image-20221007154540618

image-20221007154640251

5.结果

进行上述配置之后,运行程序,可见监控页面的数据

image-20221007155213139

配置文件代码参考:

#导入数据库的场景
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
    username: root
    password: draf19
    driver-class-name: com.mysql.cj.jdbc.Driver

#    druid监控页的配置
    druid:
      stat-view-servlet:
        login-username: xurong
        login-password: 123
        #使该监控页处于打开状态
        enabled: true
        #不允许重置
        reset-enable: false

#     开启web监控允许的路径,以及排除的路径
      web-stat-filter:
        enabled: true
        # 监控的路径
        url-pattern: /*
        # 监控之外排除的路径,无需监控
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"

#     开启防火墙,注意是filters,不是filter,因为filter只能用于配置单个属性值
      filters: stat,wall,slf4j
#      监控该包下的所有组件
      aop-patterns: com.xu.admin.*
      
#      这是对filters中的属性的详细配置
      filter:
        # web监控
        stat:
          # 只要sql查询超过1000毫秒的,都是属于慢查询
          slow-sql-millis: 1000
          # 是否要记录慢查询
          log-slow-sql: true

        # 防火墙的配置
        wall:
          enabled: true
          config:
            # 对所有的删除表操作进行拦截
            drop-table-allow: true
    #            update-allow: true

    max-active: 12
#    type: com.zaxxer.hikari.HikariDataSource
#  jdbc:
#    template:
##      设置查询的超时时间
#      query-timeout: 3

总结:直接使用start整合的方式,只需要配置配置文件,就可以实现上一节的自定义方式整合Druid数据源

13-4 整合mybatis-配置版

image-20221007161817177

image-20221007162101717

关于快照版和稳定版区别:

image-20221007162353046

image-20221008144835260

image-20221008144953664

image-20221008145418427

第2步,首先对配置文件类进行分析:

image-20221008145636295

第2步,mybatis绑定的类分析

image-20221008145945675

image-20221008150621704

image-20221008151116707

image-20221008150821002

老师总结:

image-20221008151158356

1.配置模式

image-20221008164026806

image-20221008200746893

mybatis-config.xml:

<?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>
<!--    数据源场景已经由springboot中的application.yaml配置文件配置好,无需再配置-->
<!--    <environments default="development">-->
<!--        <environment id="development">-->
<!--            <transactionManager type="JDBC"/>-->
<!--            <dataSource type="POOLED">-->
<!--                <property name="driver" value="${driver}"/>-->
<!--                <property name="url" value="${url}"/>-->
<!--                <property name="username" value="${username}"/>-->
<!--                <property name="password" value="${password}"/>-->
<!--            </dataSource>-->
<!--        </environment>-->
<!--    </environments>-->
<!--    <mappers>-->
<!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
<!--    </mappers>-->


</configuration>

RoleMapper.xml:

<?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">
<!--
该文件就是对应RoleMapper接口实现类,也就是实现接口中操作数据库的方法
-->
<mapper namespace="com.xu.admin.RoleMapper">

<!--
实现接口中通过id
public Role getRoleById(Long id);

-->
    <select id="getRoleById" resultType="com.xu.admin.bean.Role">
        select * from role where id = #{id}
    </select>
</mapper>

代码参考:

image-20221008164107915

image-20221008164622864

image-20221008164139195

image-20221008164247433

测试:

image-20221008165711370

image-20221008200645679

全局配置文件中的属性值可以被springboot配置文件中的属性值取代:

image-20221008201336974

image-20221008201530754

image-20221008203458137

image-20221008204040324

总结一下使用配置模式的开发步骤:

image-20221008204455231

2.注解配置混合版

image-20221008211440421

image-20221008214241444

测试结果:

image-20221008214515729

注意:如果拦截器没有放行,需要给请求头传入携带密码的cookie

image-20221008214606769

最佳实战:

image-20221008214356134

13-5 mybatis-plus操作数据库

1.引入依赖,源码分析

image-20221027091147792

引入使用mybatis-plus所需要的依赖,如果没有该版本的依赖,可能是镜像版本过老旧,可以在控制台使用mvn install进行镜像依赖的跟新

image-20221027095420889

image-20221027102759003

image-20221027103438337

源码分析:

image-20221027103930960

image-20221027104221592

对于自动配置类---MybatisPlusAutoConfiguration.java 的一些简单分析:看看自动配置了那些东西

image-20221027105710621

image-20221027110209334

image-20221027110452673

image-20221027110702148

源码分析整合:

image-20221027110727173

补充:默认数据源是使用的druid

2.基本的使用

创建了用户表:

image-20221027112723766

image-20221027111524453

补充:@TableField(exist = false)表示表中不存在该属性值,自然也不会因为查询不到这两个属性值而导致报错

image-20221027112832289

image-20221027111757120

image-20221027111838222

结果:

image-20221027114533036

13-6 crud实验_数据列表展示

1.使用@TableName指定表的名称

image-20221027141306501

2.关于UserService接口的定义,以及其实现类的定义

image-20221027143307218

image-20221027143446895

image-20221027144015016

测试结果:

image-20221027161917753

image-20221027174820975

结果:

image-20221027162216992

接下来,安装一个分页插件,解决上诉显示0条记录的问题:

image-20221027174905799

结果:

image-20221027175433907

增添删除按钮:

image-20221029094729429

删除某一个用户:

image-20221029101032937

image-20221029101118016

结果:

image-20221029101240940

image-20221029101339359

改进:删除之后还停留在当前页,而不是跳转到第一页,只需在重定向时,携带需要重定向的页码即可。

image-20221029102106067

image-20221029111036015

实验结果:

测试1:

image-20221029111135860

image-20221029111302307

测试2:

image-20221029111522435

image-20221029111456848

image-20221029111606590

自己改进思路方向:

image-20221029111856902

13-7 准备阿里云redis环境---redis操作与统计小实验(需要重听)

源码简要分析:

image-20221029142450508

image-20221029143132643

image-20221029143243833

image-20221029143407529

image-20221029143902158

image-20221029143720021

注意:需要重听,大体就是在阿里云上购买云服务器并部署redis,然后通过后台引入的redis的依赖,通过实体类(也就是客户端)实现对部署的redis的数据存储以及访问。

image-20221029162853599

第14章 单元测试

14-1 junit4与junit5对比,以及依赖的使用

image-20221029164441775

image-20221029164527913

image-20221029165546423

以下就是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>

springboot引入使用junit5的依赖:

image-20221029170100024

image-20221029170129495

14-2 Junit5常用测试注解

官网:[常用注解参考](JUnit 5 用户指南)

image-20221029224134410

常用代码参考:

package com.xu.admin;

import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.annotation.Repeat;

import java.util.concurrent.TimeUnit;

/**
 * @author xu
 * @Description
 * @date 2022/10/29 - 22:42:50
 * @Modified By:
 */
@SpringBootTest
@DisplayName("junits功能测试类")
public class Junit5Test {

    @Autowired
    JdbcTemplate jdbcTemplate;

    // @RepeatedTest(4)用于反复执行测试

    @DisplayName("测试displayname注解")
    @Test
    void testDisplayName1() {
        System.out.println(1111);
        // 需要使用该@SpringBootTest,jdbcTemplate才不是null
        System.out.println(jdbcTemplate);
    }
    @RepeatedTest(4)
    void testRepeatedTest() {
        System.out.println("测试@RepeatedTest(4)");
    }
    // 将这个测试类禁用掉
//    @Disabled
    @DisplayName("测试displayname注解")
    @Test
    void testDisplayName2() {
        System.out.println(2222);
    }
    @BeforeEach
    void testBeforeEach() {
        System.out.println("测试方法将要开始了");
    }

    @AfterEach
    void testAfterEach() {
        System.out.println("测试方法结束了");
    }

    @BeforeAll
    static void testBeforeAll() {
        System.out.println("所有测试方法就要开始了。。。");
    }

    @AfterAll
    static void testAfterAll() {
        System.out.println("所有测试方法已经结束了");
    }

    @Timeout(value = 500,unit = TimeUnit.MILLISECONDS)
    @Test
    void testTimeOUt() throws InterruptedException {
        //将该函数线程挂起505毫秒,肯定超过了500毫秒.会报超时异常:java.util.concurrent.TimeoutException:
        // testTimeOUt() timed out after 500 milliseconds
        Thread.sleep(505);
    }
}

输出结果:

所有测试方法就要开始了。。。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.1)

2022-10-29 23:27:17.198  INFO 9768 --- [           main] com.xu.admin.Junit5Test                  : Starting Junit5Test using Java 1.8.0_311 on DESKTOP-SOE9MG1 with PID 9768 (started by 许荣 in D:\Java\springBoot\boot-05-web-admin)
2022-10-29 23:27:17.200  INFO 9768 --- [           main] com.xu.admin.Junit5Test                  : No active profile set, falling back to default profiles: default
2022-10-29 23:27:17.370  WARN 9768 --- [kground-preinit] o.s.h.c.j.Jackson2ObjectMapperBuilder    : For Jackson Kotlin classes support please add "com.fasterxml.jackson.module:jackson-module-kotlin" to the classpath
2022-10-29 23:27:17.900  INFO 9768 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2022-10-29 23:27:17.904  INFO 9768 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2022-10-29 23:27:17.940  INFO 9768 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 16 ms. Found 0 Redis repository interfaces.
2022-10-29 23:27:18.328  INFO 9768 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration' of type [com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-10-29 23:27:18.451  INFO 9768 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'spring.datasource.druid-com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties' of type [com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-10-29 23:27:18.463  INFO 9768 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'advisor' of type [org.springframework.aop.support.RegexpMethodPointcutAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-10-29 23:27:18.696  INFO 9768 --- [           main] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
2022-10-29 23:27:19.158  INFO 9768 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
 _ _   |_  _ _|_. ___ _ |    _ 
| | |\/|_)(_| | |_\  |_)||_|_\ 
     /               |         
                        3.5.2 
2022-10-29 23:27:20.726  INFO 9768 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2022-10-29 23:27:21.065  WARN 9768 --- [           main] org.thymeleaf.templatemode.TemplateMode  : [THYMELEAF][main] Template Mode 'HTML5' is deprecated. Using Template Mode 'HTML' instead.
2022-10-29 23:27:21.922  INFO 9768 --- [           main] com.xu.admin.Junit5Test                  : Started Junit5Test in 5.207 seconds (JVM running for 6.724)

测试方法将要开始了
测试方法结束了


java.util.concurrent.TimeoutException: testTimeOUt() timed out after 500 milliseconds

	at org.junit.jupiter.engine.extension.TimeoutInvocation.createTimeoutException(TimeoutInvocation.java:70)
	at org.junit.jupiter.engine.extension.TimeoutInvocation.proceed(TimeoutInvocation.java:59)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
	Suppressed: java.lang.InterruptedException: sleep interrupted
		at java.lang.Thread.sleep(Native Method)
		at com.xu.admin.Junit5Test.testTimeOUt(Junit5Test.java:69)
		at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
		at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
		at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
		at java.lang.reflect.Method.invoke(Method.java:498)
		at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
		at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
		at org.junit.jupiter.engine.extension.TimeoutInvocation.proceed(TimeoutInvocation.java:46)
		... 58 more



测试方法将要开始了
1111
org.springframework.jdbc.core.JdbcTemplate@424f02b8
测试方法结束了


测试方法将要开始了
2222
测试方法结束了





测试方法将要开始了
测试@RepeatedTest(4)
测试方法结束了

测试方法将要开始了
测试@RepeatedTest(4)
测试方法结束了

测试方法将要开始了
测试@RepeatedTest(4)
测试方法结束了

测试方法将要开始了
测试@RepeatedTest(4)
测试方法结束了

所有测试方法已经结束了
2022-10-29 23:27:22.797  INFO 9768 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2022-10-29 23:27:22.798  INFO 9768 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closing ...
2022-10-29 23:27:22.800  INFO 9768 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closed

Process finished with exit code -1

14-3 断言

image-20221030081920990

常用的断言参考:

package com.xu.admin;

import org.junit.jupiter.api.*;

/**
 * @author xu
 * @Description
 * @date 2022/10/30 - 8:12:40
 * @Modified By:
 */
public class AssertTest {

    @BeforeEach
    void testBefore() {
        System.out.println("开始测试");
    }
    @AfterEach
    void testAfter() {
        System.out.println("测试结束");
    }

    /**
     *
     * @Description
     * 如果前面的断言失败,后面的断言就不会执行
     * @param
     * @Author xu
     * @Date 2022/10/30 8:27:44
     *
     */
    @DisplayName("简单断言测试")
    @Test
    void testSimpleAssertion() {
        int calc = calc(1, 2);
        // 判断两个对象是否相等
        Assertions.assertEquals(3,calc);

        // 判断2个对象地址是否一致
        Object o1 = new Object();
        Object o2 = new Object();
        Assertions.assertSame(o1, o2);

    }

    int calc(int a, int b) {
        return a + b;
    }

    @DisplayName("组合断言")
    @Test
    void allAssertion() {
        /**
         *
         * @Description
         * 所有断言全部需要成功方可称之为成功
         * ()-> Assertions.assertTrue(true && true): 采用函数式编程的写法(我不知道)
         * @param
         * @Author xu
         * @Date 2022/10/30 8:35:03
         *
         */
        Assertions.assertAll("test",
                ()-> Assertions.assertTrue(true && true,"结果不是true"),
                ()-> Assertions.assertEquals(3,3,"结果不是3"));
        System.out.println("==============================");
    }

    // 数组断言
    @Test
    @DisplayName("array assertion")
    public void array() {
        Assertions.assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
    }

    @DisplayName("异常断言")
    @Test
    void testException() {
        Assertions.assertThrows(ArithmeticException.class,() -> {
            int i = 2 / 1;
        },"断言不成功抛出的信息,也就是没有出现算术异常");
    }
}

老师笔记参考:

断言参考

Junit5官网参考:Junit5官网参考

image-20221030095629541

14-4 前置条件

image-20221030100927272

实验测试:

image-20221030101040371

image-20221030101222412

14-5 嵌套测试

官网参考:

package com.xu.admin;

/**
 * @author xu
 * @Description
 * @date 2022/10/30 - 10:14:43
 * @Modified By:
 */
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.EmptyStackException;
import java.util.Stack;

import org.junit.jupiter.api.*;

@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
        // 断言stack是否为空,因为内层的@BeforeEach对stack进行了实例化,验证内层的@BeforeEach/All等方法在外层的测试方法不起作用
        Assertions.assertNull(stack);
        // 没有输出结果,确实为null
        // 总结:嵌套测试情况下,外层的Test不能驱动内层的@BeforeEach/All之类的方法提前/之后 执行
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, stack::pop);
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, stack::peek);
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}


/*
总结:
内层的单元测试方法可以驱动外层的@BeforeEach,@AfterEach等方法,
但是外层的单元测试方法无法驱动内层的@BeforeEach,@AfterEach等方法
 *///:~


14-6 参数化测试

image-20221030104431020

测试:

package com.xu.admin;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.stream.Stream;

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

/**
 * @author xu
 * @Description
 * @date 2022/10/30 - 10:46:00
 * @Modified By:
 */
public class ParameterizedTests {


    @BeforeEach
    void testBefore() {
        System.out.println("开始测试");
    }
    @AfterEach
    void testAfter() {
        System.out.println("测试结束");
    }

    /**
     *
     * @Description
     * 参数从String类型的数组来
     * @param
     * @Author xu
     * @Date 2022/10/30 10:49:47
     *
     */
    @ParameterizedTest
    @ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
    void palindromes(String candidate) {
        System.out.println(candidate);
    }




    /**
     *
     * @Description
     * 参数测试的参数也可以通过方法名得到
     * @param
     * @Author xu
     * @Date 2022/10/30 10:53:10
     *
     */
    @ParameterizedTest
    @MethodSource("stringProvider")
    void testWithExplicitLocalMethodSource(String argument) {
        System.out.println(argument);
    }

    static Stream<String> stringProvider() {
        return Stream.of("apple", "banana");
    }




}

测试结果:

开始测试
apple
测试结束


开始测试
banana
测试结束

补充:从junit4迁移到junit5注解的变化

直接参考官网:

image-20221030105947029

第15章 指标监控

15-1 SpringBoot Actuator 与 Endpoint

1.官网:指标监控

image-20221030112550761

使用java自带的控制台查看容器中的bean:

image-20221030113332679

image-20221030113453783

image-20221030114001533

2.直接使用客户端的方式查看容器中的bean:

image-20221030114628130

image-20221030115452087

结果:通过客户端访问容器中有多少个bean成功

image-20221030121806512

image-20221030121751057

拿到当前系统的配置报告:

image-20221030144817510

image-20221030144751735

查看指标信息:

image-20221030145524397

image-20221030145440559

image-20221030145716824

补充:也可以借用以下可视化平台将数据更好的显示出来

15-2 开启与禁用

常用的几个指标:

1.常用指标之health:

image-20221030151323691

image-20221030152006962

image-20221030152100899

引出问题:以上只是显示该项目是健康的或者是不是健康的,我引入了很多依赖,就是可以具体显示是哪一个部分是健康的或不是健康的

解决:

image-20221030153253166

结果:

image-20221030153406729

2.常用指标之metrics

image-20221030153811749

3.禁用掉部分指标,开启部分指标

image-20221030154530834

image-20221030154752405

image-20221030154941996

15-3 订制Endpoint

1.定义我自己的一个组件用于health指标监控

image-20221030155340690

image-20221030155403166

代码实现:

image-20221030160721457

package com.xu.admin.health;

import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;

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

/**
 * @author xu
 * @Description
 * @date 2022/10/30 - 15:54:51
 * @Modified By:
 */
@Component
public class MyComponentHealthIndicator extends AbstractHealthIndicator {

    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        // 假设对连接mongodb,获取连接进行测试
        Map<String, Object> map = new HashMap<>();
        if (1 == 1) {
            // 健康
            builder.status(Status.UP);
            map.put("count",1);
            map.put("ms",100);
        } else {
            // 不健康
            builder.status(Status.DOWN);
            map.put("error","连接超时");
            map.put("ms",3000);
        }
        builder.withDetail("code",100)
                .withDetails(map);
    }
}

结果:

image-20221030160819768

2.制定info信息以及获取本项目中pom.xml中的信息

方式1:

image-20221030161521603

无法显示在浏览器中,需要在pom.xml文件中声明使用@@方式获取maven的取值,而不是${}:

问题以及博客地址

结果:成功显示:

image-20221030202647223

方式2:通过实体类为info增加信息

image-20221030203739178

3.定制metrics中的指标

image-20221030213630116

image-20221030213749245

image-20221030213922471

image-20221030214020780

image-20221030214104563

使用刚刚自定义的metrics指标,统计方法调用的次数结果:

image-20221030214242369

代码参考:

package com.xu.admin.services.Impl;

import com.xu.admin.bean.City;
import com.xu.admin.mapper.CityMapper;
import com.xu.admin.services.CityService;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author xu
 * @Description
 * @date 2022/10/08 - 21:07:47
 * @Modified By:
 */
@Service
public class CityServiceImpl implements CityService {

    @Autowired
    CityMapper cityMapper;

    Counter counter;
    /**
     *
     * @Description
     * 该方法可以在actuator指标中直接查看insertCity方法调用的次数
     * @param
     * @Author xu
     * @Date 2022/10/30 20:46:37
     *
     */
    public CityServiceImpl(MeterRegistry meterRegistry) {
        // 给metrics增添一个指标,随便取一个名字为cityServiceImpl.insertCity.count
        counter = meterRegistry.counter("cityServiceImpl.insertCity.count");

    }
    /**
     *
     * @Description
     * 根据城市的id返回对应的城市数据
     * @param
     * @Author xu
     * @Date 2022/10/08 21:06:45
     *
     */
    @Override
    public City getCityById(Long id) {
        return cityMapper.getCityById(id);
    }

    /**
     *
     * @Description
     * 往表中插入数据
     * @param
     * @Author xu
     * @Date 2022/10/08 21:21:00
     *
     */
    @Override
    public void insertCity(City city) {
        // counter.increment();用于在metric自定义的指标中显示该方法被调用的次数
        counter.increment();
        cityMapper.insert(city);
    }


}

补充:

image-20221030214441404

4.定制Endpoint

官网参考:官网参考

image-20221031090629218

image-20221031085620026

结果:

image-20221031085716959

或者直接通过jconsole客户端进行自定义节点的访问:

image-20221031085914544

image-20221031085940164

image-20221031090001264

15-4 视图化actuator---boot admin server(一个开源项目)

官网介绍

引入依赖:

image-20221031105635066

image-20221031105819409

image-20221031105934433

结果:

image-20221031110024077

image-20221031110137822

进行实验:

客户端进行配置:

image-20221031111741275

image-20221031141059281

image-20221031140856452

结果:

image-20221031141301568

image-20221031141643637

image-20221031141736871

image-20221031142408825

image-20221031142830621

image-20221031143134673

image-20221031142945536

image-20221031143044843

第16章 高级特性

16-1 profile环境切换:

官网参考:

image-20221031164447741

application-prod.yaml:该配置文件是用于上线的的环境配置

application-test.yaml:该配置文件是用于测试的环境配置

准备:

image-20221031150253296

image-20221031150502424

实验结果:

image-20221031145817222

image-20221031150743268

image-20221031150905827

如果默认配置环境与application-prod.yaml配置文件中有相同的配置变量,有限使用精确的application-prod.yaml配置文件中的变量:

image-20221031151529803

访问当然也只能通过8000了:

image-20221031151601302

即使配置文件修改了,也可以通过命令行的方式改变:

只运行打包的jar包:

image-20221031153219716

1.运行jar包时,切换配置环境

image-20221031153756653

运行jar包时,不仅可以切换配置环境,还可以修改其中的配置变量的值:

image-20221031154316412

image-20221031154417651

2.实验2:@Profile注解的使用

image-20221031155638287

image-20221031155953893

结果:

image-20221031160027784

使用@Profile可以针对某一个类或者方法生效,只有绑定@Profile指定的配置文件,该类或者方法才可以生效:

image-20221031162326937

image-20221031162445730

上图也是同样道理:只有在application-prod.yaml配置文件时,才将Boss类加载进容器中

对方法也是一样:

image-20221031164648929

进行实验1:

image-20221031163521982

结果:

image-20221031163833353

进行实验2:

image-20221031164048115

结果:

image-20221031164220158

3.配置文件组

image-20221031194203398

实验开始:

image-20221031200357517

注意:直接加载的是命名的组名,并不是某一个配置文件

运行结果:

image-20221031211858509

老师总结:

image-20221031212020571

16-2 外部化配置

使用外部化配置好处举例:

连接数据库需要用户名和密码,如果改为使用另一个数据库,又需要到源码中重新设置一遍用户名和密码,过于麻烦,如果维护一个外部的配置文件用于维护连接数据库的用户名和密码,只需要改外部文件就可以实现对数据库的切换。这就是外部化配置。

实验1:

获取maven环境变量的值:

image-20221031212737224

image-20221031214048207

可见:windows也是用了外部化配置

posted @ 2022-11-15 23:36  远道而重任  阅读(7)  评论(0编辑  收藏  举报