编程知识 cdmana.com

Springboot learning notes

This note is compiled from B standing UP Lord Madness theory :https://www.bilibili.com/video/BV1PE411i7CV

Related codes :https://gitee.com/antia11/MyStudy/tree/master/Java/SpringBoot-Study

1、SpringBoot brief introduction

1.1、 Review what is Spring

Spring It's an open source framework ,2003 A lightweight that emerged in 1997 Java Development framework , author :Rod Johnson .

Spring It is created to solve the complexity of enterprise application development , Simplify the development .

1.2、Spring How to simplify Java Developed

In order to reduce Java The complexity of development ,Spring The following 4 Key strategies :

1、 be based on POJO Lightweight and minimally invasive programming , Everything is bean;

2、 adopt IOC, Dependency injection (DI) Loose coupling with interface oriented ;

3、 Based on the section (AOP) Declarative programming with conventions ;

4、 Reduce style code by faceting and stenciling ,RedisTemplate,xxxTemplate;

1.3、 What is? SpringBoot

Did you learn javaweb My classmates know , To develop a web application , Contact... From the beginning Servlet combination Tomcat, Run out of one Hello Wolrld Program , It's going to take a lot of steps ; Later, I used the frame Struts, And then it was SpringMVC, To the present SpringBoot, There will be others in a year or two web The frame appears ; You've seen frameworks evolve , Then we develop our own projects, and all the technologies are constantly changing 、 Transformation ? Suggestions can be experienced once ;

Get down to business , What is? SpringBoot Well , It's just one. javaweb Development framework of , and SpringMVC similar , Compare with others javaweb The benefits of the framework , Officially, it's about simplifying development , Convention over configuration , you can "just run", Can develop quickly web application , A few lines of code to develop a http Interface .

The development of all technical frameworks seems to follow a main line : From a complex application scenario derivative A normative framework , People only need to make various configurations and don't need to implement it by themselves , At this time, the powerful configuration function becomes the advantage ; To a certain extent , People according to the actual production and Application , Select the practical function and design essence , Refactoring out some lightweight frameworks ; Then in order to improve development efficiency , It's too troublesome to abandon the original configuration , So I started to advocate “ Convention over configuration ”, And then derive some one-stop solutions .

Yes, this is Java Enterprise application ->J2EE->spring->springboot The process of .

With Spring Continuous development , More and more fields are involved , The project integration development needs to cooperate with various documents , It's not so easy to use , Against the original idea , It's even called configuration hell .Spring Boot It is in such a context that the development framework is abstracted , Purpose in order to make it easier for everyone to use Spring 、 It's easier to integrate all kinds of common middleware 、 Open source software ;

Spring Boot be based on Spring Development ,Spirng Boot It does not provide Spring The core features of the framework and its extended functions , Just for fast 、 Agile development of a new generation based on Spring Framework applications . in other words , It's not a substitute for Spring Solutions for , But and Spring Frame tight for lifting Spring Tools for developer experience .Spring Boot With Convention is greater than configuration , Default helps us to make a lot of settings , Most of the Spring Boot Applications require very little Spring To configure . At the same time, it integrates a large number of commonly used third-party library configurations ( for example Redis、MongoDB、Jpa、RabbitMQ、Quartz wait ),Spring Boot These third-party libraries in the application can be used out of the box with almost zero configuration .

In a nutshell SpringBoot Not really a new framework , By default, it configures many ways to use the framework , It's like maven It integrates all of them jar package ,spring boot Integrated all the frameworks .

Spring Boot Born noble , From the beginning, I stood at a higher starting point , After several years of development , The ecology is perfect enough ,Spring Boot Has become worthy of Java The hottest technology in the field .

Spring Boot Main advantages of :

  • For all Spring Developers get started faster
  • Open the box , Various default configurations are provided to simplify project configuration
  • Built in containers simplify Web project
  • There is no redundant code generation and XML Configuration requirements

Really good , Let's quickly experience the feeling of developing an interface !

2、Hello,World

2.1、 preparation

We're going to learn how to quickly create a Spring Boot application , And implement a simple Http Request processing . Let's take this example to Spring Boot Have a preliminary understanding , And experience its simple structure 、 Develop fast features .

My environmental preparation :

  • java version "1.8.0_181"
  • Maven-3.6.1
  • SpringBoot 2.x The latest version

development tool :

  • IDEA

2.2、 Create basic project description

Spring The official provides a very convenient tool for us to quickly build applications

Spring Initializr:https://start.spring.io/

Project creation method 1 : Use Spring Initializr Of Web Page creation project

1、 open https://start.spring.io/

2、 Fill in project information

3、 Click on ”Generate Project“ Button build project ; Download this project

4、 Unzip the project package , And use IDEA With Maven Import the project , All the way to the next step , Until the project is imported .

5、 If it's the first time , Maybe it will be slower , There are more bags 、 It takes patience to wait for everything to be ready .

Project creation method 2 : Use IDEA Create the project directly

1、 Create a new project

2、 choice spring initalizr , You can see that the default is to go to the official website's quick build tool to implement

3、 Fill in project information

4、 Select the component to initialize ( Check for beginners Web that will do )

5、 Fill in the project path

6、 Wait for the project to build

Project structure analysis :

Through the above steps to complete the foundation project creation . The following files will be generated automatically .

1、 The main startup class of the program

2、 One application.properties The configuration file

3、 One Test class

4、 One pom.xml

2.3、pom.xml analysis

open pom.xml, have a look Spring Boot Project dependency :


<!--  Parent dependency  -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- web Scene launcher  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- springboot unit testing  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <!--  Eliminate dependency  -->
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!--  Packaging plug-in  -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2.4、 Write a http Interface

1、 In the same level directory of the main program , Create a new one controller package , It must be in the same level directory , Otherwise, we can't recognize

2、 Create a new one in the package HelloController class

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello World";
    }
    
}

3、 After writing , Start the project from the main program , Browser requests , Look at the page back ; Console output Tomcat The port number of the access !

image-20210130235815765

A few simple steps , And I finished one web Interface development ,SpringBoot It's that simple . So we often use it to build our microservice projects !

2.5、 Type the project into jar package , Click on maven Of package

image-20210130235838976

If you encounter the above error , When you can configure packaging Skip the project and run the test case

<!--
     At work , In many cases, we don't want to execute test cases 
     Maybe the test cases are not finished , Or test cases can affect database data 
     Skip test cases 
    -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!-- Skip the project and run the test case -->
        <skipTests>true</skipTests>
    </configuration>
</plugin>

If the packaging is successful , Will be in target Create one in the directory jar package

image-20210130235910960

It's like jar After package , You can run it anywhere !OK

Colored eggs

How to change the alphabet of characters displayed at startup ,SpringBoot Well ? That is to say banner pattern ;

One step : To... Under the project resources Create a new one in the directory banner.txt that will do .

Patterns can be found in :https://www.bootschool.net/ascii This website generates , Then copy it to a file !

image-20210130235933216

3、 On the principle of operation

We wrote before HelloSpringBoot, How does it work ,Maven project , We usually go from pom.xml The document explores ;

3.1、pom.xml

Parent dependency

It mainly depends on a parent project , It mainly manages the resource filtering and plug-ins of the project !

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

Click in , It turns out there's another parent dependency

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

This is the real management SpringBoot All the places in the app that depend on the version ,SpringBoot Version control center for ;

In the future, we import dependency by default and do not need to write version ; But if the imported package is not managed in the dependency, you need to manually configure the version ;

3.2、 starter spring-boot-starter

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

springboot-boot-starter-xxx: Namely spring-boot The scene launcher

spring-boot-starter-web: Helped us import web The components that the module depends on for normal operation ;

SpringBoot Extract all functional scenarios , Make one by one starter ( starter ), Just introduce these into the project starter that will do , All related dependencies are imported in , We can import what kind of scene launcher we want to use ; We can customize it ourselves in the future starter;

3.3、 Main startup class

Analysis of finished pom.xml Let's take a look at this startup class

The default main boot class

//@SpringBootApplication  To mark a main program class 
// It shows that this is a Spring Boot application 
@SpringBootApplication
public class SpringbootApplication {
   public static void main(String[] args) {
     // Thought it was starting a method , I didn't expect to start a service 
      SpringApplication.run(SpringbootApplication.class, args);
   }
}

however A simple startup class is not simple ! Let's analyze what these annotations do

@SpringBootApplication

effect : Mark on a class to indicate that the class is SpringBoot The main configuration class of , SpringBoot You should run this class main Method to start SpringBoot application ;

Go to this annotation : You can see that there are many other annotations on it !

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

@ComponentScan

The note is in Spring It's very important , It corresponds to XML Elements in configuration .

effect : Automatically scan and load eligible components or bean , Put this bean Define loading to IOC In the container

@SpringBootConfiguration

effect :SpringBoot Configuration class , Mark on a class , That means this is one SpringBoot Configuration class ;

Let's go into this annotation and see

//  Click in to get the following  @Component
@Configuration
public @interface SpringBootConfiguration {}

@Component
public @interface Configuration {}

there @Configuration, This is a configuration class , Configuration class is corresponding to Spring Of xml The configuration file ;

Inside @Component This means that , The startup class itself is also Spring It's just a component in , Responsible for launching the app !

We go back to SpringBootApplication Continue to read in the notes .

@EnableAutoConfiguration

@EnableAutoConfiguration : Turn on the auto configuration function

We used to need our own configuration , And now SpringBoot Can automatically help us configure ;@EnableAutoConfiguration tell SpringBoot Turn on the auto configuration function , In this way, the automatic configuration can take effect ;

Click in the comments to see :

@AutoConfigurationPackage : Automatic configuration package

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

@import :Spring Bottom notes @import , Import a component into the container

Registrar.class effect : Scan all components in the package where the main boot class is located and all sub packages under the package to Spring Containers ;

This analysis is over , Step back , Continue to look at

@Import({AutoConfigurationImportSelector.class}) : Import components to the container ;

AutoConfigurationImportSelector : Auto configure import selector , So which component selectors will it import ? Let's click this class to see the source code :

1、 There is one such method in this class

//  Get the candidate configuration 
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // there getSpringFactoriesLoaderFactoryClass() Method 
    // What we returned is the annotation class that started the automatic import of configuration files ;EnableAutoConfiguration
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

2、 This method calls SpringFactoriesLoader Class static methods ! We enter SpringFactoriesLoader class loadFactoryNames() Method

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    // Here it calls  loadSpringFactories  Method 
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

3、 Let's go ahead and click to see loadSpringFactories Method

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    // get classLoader ,  We can go back and see that what we get here is EnableAutoConfiguration Annotated class itself 
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            // To get a resource  "META-INF/spring.factories"
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            // Traverse the read resources , Package into a Properties
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

4、 Found a file that appears many times :spring.factories, Search it globally

spring.factories

We open it from the source spring.factories , See a lot of auto configuration files ; That's the root of autoconfiguration !

image-20210131000942676

WebMvcAutoConfiguration

Let's open any of the auto configuration classes above , such as :WebMvcAutoConfiguration

image-20210131000952703

You can see that all of them are JavaConfig Configuration class , And it's all infused with Bean, You can find some classes you know , Look, get familiar with !

therefore , The real implementation of auto configuration is from classpath Search for all META-INF/spring.factories The configuration file , And the corresponding org.springframework.boot.autoconfigure. Configuration items under the package , Instantiate the corresponding annotation through reflection @Configuration Of JavaConfig Formal IOC Container configuration class , All of these are then aggregated into an instance and loaded into the IOC In the container .

Conclusion :

  1. SpringBoot From the classpath at startup META-INF/spring.factories In order to get EnableAutoConfiguration The specified value
  2. Import these values into the container as autoconfig classes , The autoconfig class takes effect , Help us with automatic configuration ;
  3. Whole J2EE The overall solution and auto configuration of springboot-autoconfigure Of jar In bag ;
  4. It will import a lot of autoconfig classes into the container (xxxAutoConfiguration), It is to import all the components needed for this scenario into the container , And configure these components ;
  5. With the autoconfig class , It is unnecessary for us to write configuration injection function components manually ;

Now we should have a general understanding of ,SpringBoot Operating principle , We will deepen it later !

3.4、SpringApplication

It's not a simple way

At first I thought it was running a main Method , I didn't expect to open a service ;

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

SpringApplication.run analysis

The analysis of this method is mainly divided into two parts , Part of it is SpringApplication Instantiation , Two is run Method execution ;

SpringApplication

This class mainly does the following four things :

1、 Infer whether the type of application is a normal project or Web project

2、 Find and load all available initializers , Set to initializers Properties of the

3、 Find out all the application listeners , Set to listeners Properties of the

4、 Infer and set main Method definition class , Find the running main class

Look at the constructor :

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

run Method flow analysis

image-20210131001531107

4、YAML Configuration injection

4.1、yaml Grammar learning

The configuration file

SpringBoot Use a global profile , Profile name is fixed

  • application.properties

    • Grammatical structure :key=value
  • application.yml

    • Grammatical structure :key: Space value

Role of profile : modify SpringBoot Default for auto configuration , because SpringBoot At the bottom, it's automatically configured for us ;

For example, we can modify it in the configuration file Tomcat Default boot port number ! Test it !

server.port=8081

4.2、yaml summary

YAML yes "YAML Ain't a Markup Language" (YAML Not a markup language ) The recursive abbreviation of . In developing this language ,YAML In fact, it means :"Yet Another Markup Language"( It's still a sign language )

This language uses data do Centered , Instead of focusing on Markup Language !

Previous configuration files , Most of them use xml To configure the ; For example, a simple port configuration , Let's compare yaml and xml

Tradition xml To configure :

<server>
    <port>8081<port>
</server>

yaml To configure :

server:
  prot: 8080

4.3、yaml Basic grammar

explain : The grammar is strict !

1、 Spaces cannot be omitted

2、 Control hierarchy by indenting , As long as the left aligned column of data is at the same level .

3、 The case of attributes and values is very sensitive .

Literal : Normal value [ Numbers , Boolean value , character string ]

The amount of words can be written directly at the back , By default, strings do not need to be enclosed with double quotation marks or single quotation marks ;

k: v

Be careful :

  • “ ” Double quotes , Does not escape special characters in a string , Special characters will be used as the meaning they want to express ;

    such as :name: "kuang \n shen" Output :kuang Line break shen

  • '' Single quotation marks , Can escape special characters , Special characters will eventually be output just like normal characters

    such as :name: ‘kuang \n shen’ Output :kuang \n shen

object 、Map( Key value pair )

# object 、Map Format 
k: 
    v1:
    v2:

On the next line, write the attribute and value relationship of the object , Note that the indentation ; such as :

student:
    name: qinjiang
    age: 3

Inline writing

student: {name: qinjiang,age: 3}

Array ( List、set )

use - Value represents an element in an array , such as :

pets:
 - cat
 - dog
 - pig

Inline writing

pets: [cat,dog,pig]

modify SpringBoot The default port number of

Add... To the configuration file , Parameters of port number , You can switch ports ;

server:
  port: 8082

4.4、 Injection profile

yaml What's more powerful about documents is , He can inject matching values directly into our entity class !

yaml Injection profile

1、 stay springboot In the project resources Create a new file in the directory application.yml

2、 Write an entity class Dog;

package com.kuang.springboot.pojo;

@Component  // register bean Into the container 
public class Dog {
    private String name;
    private Integer age;
    
    // Parametric nonparametric construction 、get、set Method 、toString() Method   
}

3、 reflection , How we used to give bean Injects the value of a property !@Value, Give dogs a test :

@Component // register bean
public class Dog {
    @Value(" Rhubarb ")
    private String name;
    @Value("18")
    private Integer age;
}

4、 stay SpringBoot Under the test class injection dog output ;

@SpringBootTest
class DemoApplicationTests {

    @Autowired // Put the dog in automatically 
    Dog dog;

    @Test
    public void contextLoads() {
        System.out.println(dog); // Print the dog object 
    }

}

Results the output was successful ,@Value Inject success , That's how we used to do it, right .

image-20210131002159669

5、 We're writing a more complex entity class :Person class

@Component // register bean Into the container 
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
    
    // Parametric nonparametric construction 、get、set Method 、toString() Method   
}

6、 Let's use yaml Configure the way to inject , Pay attention to the differences and advantages when you write , Let's write a yaml To configure !

person:
  name: qinjiang
  age: 3
  happy: false
  birth: 2000/01/01
  maps: {k1: v1,k2: v2}
  lists:
   - code
   - girl
   - music
  dog:
    name:  Wangcai 
    age: 1

7、 We have just put person All the values of this object are written , Let's now inject into our class !

/*
@ConfigurationProperties effect :
 Set the value of each property configured in the configuration file , Map to this component ;
 tell SpringBoot Bind all the properties in this class with the relevant configuration in the configuration file 
 Parameters  prefix = “person” :  In the configuration file person All of the following properties correspond to each other 
*/
@Component // register bean
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

8、IDEA Tips ,springboot Configuration annotation processor not found , Let's look at the documentation , We can look at the documentation , Find a dependency !

image-20210131002348052

<!--  Import profile processor , You will be prompted when binding the configuration file , Need to restart  -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

9、 Confirm that the above configuration is OK after , Let's test it in the test class :

@SpringBootTest
class DemoApplicationTests {

    @Autowired
    Person person; // take person Automatically inject in 

    @Test
    public void contextLoads() {
        System.out.println(person); // Print person Information 
    }

}

result : All values are injected successfully !

image-20210131002435632

yaml Configuration injected into the entity class completely OK!

Classroom tests :

1、 Will configure the key value and Property is set to a different value , The result output is null, Injection failed

2、 In the configuration of a person2, And then @ConfigurationProperties(prefix = "person2") Point to our person2;

Load the specified configuration file

@PropertySource : Load the specified configuration file ;

@configurationProperties: Get the value from the global configuration file by default ;

1、 Let's go to resources Create a new one in the directory person.properties file

name=kuangshen

2、 Then specify load in our code person.properties file

@PropertySource(value = "classpath:person.properties")
@Component // register bean
public class Person {

    @Value("${name}")
    private String name;

    ......  
}

3、 Test the output again : The specified configuration file was bound successfully !

image-20210131002540346

Placeholder for profile

Configuration files can also write placeholders to generate random numbers

person:
    name: qinjiang${random.uuid} #  Random uuid
    age: ${random.int}  #  Random int
    happy: false
    birth: 2000/01/01
    maps: {k1: v1,k2: v2}
    lists:
      - code
      - girl
      - music
    dog:
      name: ${person.hello:other}_ Wangcai 
      age: 1

review properties To configure

What we use above yaml Methods are the simplest way , Most commonly used in development ; It's also springboot Recommended ! Let's talk about other ways to achieve it , The truth is the same ; It's still like that ; Configuration files in addition to yml And what we used to use properties , We didn't say , Let's talk about it !

【 Be careful 】properties When the configuration file is written in Chinese , There will be a mess , We need to go to IDEA Set the encoding format to UTF-8;

settings-->FileEncodings Middle configuration ;

image-20210131002627587

testing procedure :

1、 Create a new entity class User

@Component // register bean
public class User {
    private String name;
    private int age;
    private String sex;
}

2、 Edit profile user.properties

user1.name=kuangshen
user1.age=18
user1.sex= male 

3、 We are User Class using the @Value To inject !

@Component // register bean
@PropertySource(value = "classpath:user.properties")
public class User {
    // Use it directly @value
    @Value("${user.name}") // Take value from configuration file 
    private String name;
    @Value("#{9*2}")  // #{SPEL} Spring expression 
    private int age;
    @Value(" male ")  //  Literal 
    private String sex;
}

4、Springboot test

@SpringBootTest
class DemoApplicationTests {

    @Autowired
    User user;

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

}

Result normal output :

image-20210131002750504

Comparison summary

@Value It's not friendly to use ! We need to assign values to individual annotations for each property , More trouble ; Let's look at a functional comparison diagram

image-20210131002810033

1、@ConfigurationProperties Just write it once , @Value You need to add... To each field

2、 Loose binding : What does this mean ? Such as my yml Written in last-name, This and lastName It's the same , - The following letters are capitalized by default . This is loose binding . You can test

3、JSR303 data verification , This is that we can add a layer of filter verification in the field , Can guarantee the legitimacy of the data

4、 Complex type encapsulation ,yml Objects can be encapsulated in , Use value I don't support

Conclusion :

To configure yml And configuration properties You can get the values , Strongly recommend yml;

If we're in a business , Just get a value in the configuration file , You can use it @value;

if , We wrote a special JavaBean To map to the configuration file one by one , Just directly @configurationProperties, Don't hesitate. !

5、JSR303 Data verification and multi environment switching

5.1、JSR303 data verification

Let's see how to use

Springboot Can be used in the @validated To verify the data , If the data is abnormal, the exception will be thrown , It is convenient for the exception center to handle it in a unified way . Let's write a note here to make our name Can only support Email Format ;

@Component // register bean
@ConfigurationProperties(prefix = "person")
@Validated  // data verification 
public class Person {

   @Email(message=" Mailbox format error ") //name Must be in email format 
   private String name;
}

Running results :default message [ Not a legal email address ];

image-20210131002941999

Use data validation , Can guarantee the correctness of the data ;

Common parameters

@NotNull(message=" Name cannot be empty ")
private String userName;
@Max(value=120,message=" The oldest person can't check 120")
private int age;
@Email(message=" Mailbox format error ")
private String email;

 Empty check 
@Null        Verify that the object is null
@NotNull     Verify that the object is not null,  Can't check length is 0 String 
@NotBlank    Check if the constraint string is Null And the Trim Is the length greater than 0, For strings only , And the space before and after will be removed .
@NotEmpty    Check if the constraint element is NULL Or is it EMPTY.
    
Booelan Check 
@AssertTrue      verification  Boolean  Whether the object is  true  
@AssertFalse     verification  Boolean  Whether the object is  false  
    
 Length check 
@Size(min=, max=)  Verify the object (Array,Collection,Map,String) Is the length within the given range   
@Length(min=, max=) string is between min and max included.

 Date check 
@Past        verification  Date  and  Calendar  Whether the object is before the current time   
@Future      verification  Date  and  Calendar  Whether the object is after the current time   
@Pattern     verification  String  Whether the object conforms to the rules of regular expression 

....... wait 
 in addition to , We can also customize some data validation rules 

5.2、 Multi environment switching

profile yes Spring Support different configuration functions for different environments , By activating different versions of the environment , Fast environment switching ;

Multiple profiles

When we write the main configuration file , The filename can be application-{profile}.properties/yml , Used to specify multiple environment versions ;

for example :

application-test.properties Represents the test environment configuration

application-dev.properties Represents the development environment configuration

however Springboot It doesn't launch these profiles directly , it By default application.properties Master profile ;

We need a configuration to choose the environment to activate :

# For example, specify the use of... In the configuration file dev Environmental Science , We can test it by setting different port numbers ;
# We started SpringBoot, You can see that you have switched to dev The configuration of the next ;
spring.profiles.active=dev

yaml Multiple document blocks for

and properties The same as in the configuration file , But use yml To achieve this, you don't need to create multiple configuration files , More convenient !

server:
  port: 8081
# Select the environment block you want to activate 
spring:
  profiles:
    active: prod

---
server:
  port: 8083
spring:
  profiles: dev # Name of configuration environment 


---

server:
  port: 8084
spring:
  profiles: prod  # Name of configuration environment 

Be careful : If yml and properties At the same time, the ports are configured , And it doesn't activate other environments , By default properties Profile's !

Configuration file loading location

There are many ways to load configuration files externally , We can choose the most commonly used one , Configure in the developed resource file !

Official external configuration file description reference document

image-20210131003238428

springboot Startup will scan the following locations application.properties perhaps application.yml File as Spring boot Default configuration file :

image-20210131003248159

Priority from high to low , High priority configuration overrides low priority configuration ;

SpringBoot The main configuration file will be loaded from all four locations ; Complementary configuration ;

We set a project access path configuration in the lowest level configuration file to test the complementarity problem ;

image-20210131003258463

expand , Operation and maintenance tips

Specify the location to load the configuration file

We can also go through spring.config.location To change the default profile location fd

After the project is packaged , We can use the form of command line arguments , When starting a project, specify the new location of the configuration file ; This situation , Generally, we do more in the later stage of operation and maintenance , Same configuration , The configuration file specified externally has the highest priority

java -jar spring-boot-config.jar --spring.config.location=F:/application.properties

6、 Auto configuration principle

What the configuration file can write ? How to write ?

SpringBoot There's a lot of configuration in the official documentation , We can't all remember

image-20210131005647308

6.1、 Analyze the principle of automatic configuration

We use HttpEncodingAutoConfiguration(Http Code auto configuration ) For example, explain the principle of automatic configuration ;

// Indicates that this is a configuration class , Just like the previous configuration file , You can also add components to the container ;
@Configuration 

// Start... For the specified class ConfigurationProperties function ;
  // Enter this HttpProperties see , Set the corresponding value in the configuration file with HttpProperties Bind up ;
  // And put HttpProperties Add to ioc In the container 
@EnableConfigurationProperties({HttpProperties.class}) 

//Spring Bottom @Conditional annotation 
  // Judge according to different conditions , If the specified conditions are met , The configuration in the entire configuration class will take effect ;
  // This means to judge whether the current application is web application , If it is , The current configuration class takes effect 
@ConditionalOnWebApplication(
    type = Type.SERVLET
)

// Determine whether the current project has this class CharacterEncodingFilter;SpringMVC Filter to solve the garbled code in ;
@ConditionalOnClass({CharacterEncodingFilter.class})

// Determine whether a configuration exists in the configuration file :spring.http.encoding.enabled;
  // If it doesn't exist , Judgment is also true 
  // Even if we don't configure spring.http.encoding.enabled=true, It also takes effect by default ;
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
    // He has been with SpringBoot The configuration file of is mapped 
    private final Encoding properties;
    // There is only one case where there is a parameter constructor , The value of the parameter is taken from the container 
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }
    
    // Add a component to the container , Some values for this component need to be from properties In order to get 
    @Bean
    @ConditionalOnMissingBean // Determine that the container does not have this component ?
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
    //.......
}

One sentence summary : Judge according to different conditions , Determine whether the configuration class is effective !

  • Once this configuration class takes effect ; This configuration class will add various components to the container ;
  • The properties of these components are from the corresponding properties Class , Each property in these classes is bound to the configuration file ;
  • All the properties that can be configured in the configuration file are in xxxxProperties Class encapsulates ;
  • The configuration file can refer to the property class corresponding to a function
// Get the specified values and... From the configuration file bean Binding the properties of 
@ConfigurationProperties(prefix = "spring.http") 
public class HttpProperties {
    // .....
}

Let's try the prefix in the configuration file , See Tips !

image-20210131010113779

This is the principle of automatic assembly !

quintessence

1、SpringBoot Startup loads a large number of autoconfig classes

2、 Let's see if the features we need are in SpringBoot In the default auto configuration class ;

3、 Let's see what components are configured in this auto configuration class ;( As long as the components we want to use exist in it , We don't need to configure it manually anymore )

4、 When adding components to the container's auto configuration class , From properties Class to get some properties . We just need to specify the values of these properties in the configuration file ;

xxxxAutoConfigurartion: Automatic configuration class ; Add components to the container

xxxxProperties: Encapsulate the properties in the configuration file ;

understand :@Conditional

After understanding the principle of automatic assembly , Let's focus on a detail , The autoconfig class can only take effect under certain conditions ;

@Conditional Derived annotations (Spring The annotated version is original @Conditional effect )

effect : Must be @Conditional The specified conditions hold , Add components to the container , All the contents in the configuration configuration will take effect ;

image-20210131010311448

So many autoconfig classes , Only under certain conditions can it take effect ; in other words , We loaded so many configuration classes , But not everything works .

How do we know which autoconfig classes work ?

We can use debug=true attribute ; To have the console print the autoconfiguration report , In this way, we can easily know which auto configuration classes are effective ;

# Turn on springboot Debugging class of 
debug=true

Positive matches:( Autoconfig class enabled : Positive match )

Negative matches:( Has not started , No autoconfig classes matched successfully : Negative matching )

Unconditional classes: ( Classes without conditions )

7、 Customize Starter

explain

The starter module is a empty jar file , Only supporting dependency management , These dependencies may be used for automatic assembly or other class libraries ;

Name reduction :

Official name :

  • Prefix :spring-boot-starter-xxx
  • such as :spring-boot-starter-web....

Custom naming :

  • xxx-spring-boot-starter
  • such as :mybatis-spring-boot-starter

7.1、 Write the starter

1、 stay IDEA Create a new empty project in spring-boot-starter-diy

2、 Create a new normal Maven modular :kuang-spring-boot-starter

image-20210131123716239

3、 Create a new one Springboot modular :kuang-spring-boot-starter-autoconfigure

image-20210131123737869

4、 Click on apply that will do , The basic structure

image-20210131123751418

5、 In our starter in Import autoconfigure Dependence !

<!--  starter  -->
<dependencies>
    <!--   Introduce auto configuration module  -->
    <dependency>
        <groupId>com.kuang</groupId>
        <artifactId>kuang-spring-boot-starter-autoconfigure</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

6、 take autoconfigure Delete all the extra files under the project ,Pom There's only one left in this starter, This is the basic configuration of all initiators !

image-20210131123900793

7、 We write our own service

package com.kuang;

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    public String sayHello(String name){
        return helloProperties.getPrefix() + name + helloProperties.getSuffix();
    }

}

8、 To write HelloProperties Configuration class

package com.kuang;

import org.springframework.boot.context.properties.ConfigurationProperties;

//  Prefix  kuang.hello
@ConfigurationProperties(prefix = "kuang.hello")
public class HelloProperties {

    private String prefix;
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

9、 Write our autoconfiguration class and inject bean, test !

package com.kuang;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnWebApplication //web Application takes effect 
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloProperties helloProperties;

    @Bean
    public HelloService helloService(){
        HelloService service = new HelloService();
        service.setHelloProperties(helloProperties);
        return service;
    }

}

10、 stay resources Write your own META-INF\spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.kuang.HelloServiceAutoConfiguration

11、 After writing , Can be installed to maven Warehouse !

image-20210131124109130

7.2、 New project to test our own starter

1、 Create a new one SpringBoot project

2、 Import our own starter

<dependency>
    <groupId>com.kuang</groupId>
    <artifactId>kuang-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

3、 Write a HelloController To test our own written interface !

package com.kuang.controller;

@RestController
public class HelloController {

    @Autowired
    HelloService helloService;

    @RequestMapping("/hello")
    public String hello(){
        return helloService.sayHello("zxc");
    }

}

4、 Writing configuration files application.properties

kuang.hello.prefix="ppp"
kuang.hello.suffix="sss"

5、 Start the project for testing , Results successful !

image-20210131124258717

8、 Integrate JDBC

8.1、SpringData brief introduction

For the data access layer , Whether it's SQL( Relational database ) still NOSQL( Non relational database ),Spring Boot The bottom is all made of Spring Data In a unified way .

Spring Boot The bottom is all made of Spring Data To process all kinds of databases in a unified way ,Spring Data It's also Spring China and Spring Boot、Spring Cloud Wait for a well-known project .

Sping Data Official website :https://spring.io/projects/spring-data

Database related initiators : Please refer to the official documents :

https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter

8.2、 Integrate JDBC

Create test project test data source

1、 I'm going to build a new project to test :springboot-data-jdbc ; Introduce the corresponding module ! Basic module

image-20210131124448336

2、 After the project is completed , Discovery auto helps us import the following initiators :

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

3、 To write yaml The configuration file connects to the database ;

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC Solve the time zone error 
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

4、 After configuring these things , We can use it directly , because SpringBoot It has been automatically configured for us by default ; Go to the test class to test

@SpringBootTest
class SpringbootDataJdbcApplicationTests {

    //DI Inject data source 
    @Autowired
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        // Take a look at the default data source 
        System.out.println(dataSource.getClass());
        // Obtain a connection 
        Connection connection =   dataSource.getConnection();
        System.out.println(connection);
        // Close the connection 
        connection.close();
    }
}

result : We can see that the data source he configured for us by default is : class com.zaxxer.hikari.HikariDataSource , We didn't manually configure

Let's do a global search , All auto configurations for finding data sources are in :DataSourceAutoConfiguration file :

@Import(
    {Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class}
)
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

All the imported classes are in DataSourceConfiguration Configuration class , It can be seen that Spring Boot 2.2.5 By default HikariDataSource data source , And the previous version , Such as Spring Boot 1.5 By default org.apache.tomcat.jdbc.pool.DataSource As a data source ;

HikariDataSource It's called Java WEB The fastest data source at the moment , Compared to traditional C3P0 、DBCP、Tomcat jdbc Wait for the connection pool to be better ;

have access to spring.datasource.type Specify the custom data source type , The value is The fully qualified name of the connection pool implementation to use .

We don't talk about data sources , With a database connection , Obviously CRUD Operating the database . But we need to understand an object first JdbcTemplate

JDBCTemplate

1、 You have the data source (com.zaxxer.hikari.HikariDataSource), Then you can get the database connection (java.sql.Connection), With the connection , You can use native JDBC Statement to operate on the database ;

2、 Even if you don't use the third-party third-party database operation framework , Such as MyBatis etc. ,Spring And also to the original JDBC Made a lightweight package , namely JdbcTemplate.

3、 All of the database operations CRUD Methods in JdbcTemplate in .

4、Spring Boot Not only does it provide a default data source , At the same time, it is configured by default JdbcTemplate Put it in a container , Programmers just need to inject themselves to use

5、JdbcTemplate Automatic configuration of is dependent on org.springframework.boot.autoconfigure.jdbc Under bag JdbcTemplateConfiguration class

JdbcTemplate The following types of methods are provided :

  • execute Method : Can be used to perform any SQL sentence , Generally used for execution DDL sentence ;
  • update The method and batchUpdate Method :update Method to perform the addition 、 modify 、 Delete wait statement ;batchUpdate Method is used to execute batch related statements ;
  • query The method and queryForXXX Method : Used to execute query related statements ;
  • call Method : Used to execute stored procedures 、 Function correlation statement .

test

Write a Controller, Inject jdbcTemplate, Write test methods for access testing ;


package com.kuang.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/jdbc")
public class JdbcController {

    /**
     * Spring Boot  Data source is provided by default , By default  org.springframework.jdbc.core.JdbcTemplate
     * JdbcTemplate  Will inject its own data source , For simplification  JDBC operation 
     *  And avoid some common mistakes , No need to close the database connection by yourself 
     */
    @Autowired
    JdbcTemplate jdbcTemplate;

    // Inquire about employee All data in the table 
    //List  Medium 1 individual  Map  Corresponding database  1 Row data 
    //Map  Medium  key  The field name of the corresponding database ,value  Field values corresponding to the database 
    @GetMapping("/list")
    public List<Map<String, Object>> userList(){
        String sql = "select * from employee";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
    
    // Add a new user 
    @GetMapping("/add")
    public String addUser(){
        // Insert statement , Pay attention to time 
        String sql = "insert into employee(last_name, email,gender,department,birth)" +
                " values (' Madness theory ','24736743@qq.com',1,101,'"+ new Date().toLocaleString() +"')";
        jdbcTemplate.update(sql);
        // Inquire about 
        return "addOk";
    }

    // Modify user information 
    @GetMapping("/update/{id}")
    public String updateUser(@PathVariable("id") int id){
        // Insert statement 
        String sql = "update employee set last_name=?,email=? where id="+id;
        // data 
        Object[] objects = new Object[2];
        objects[0] = " Qinjiang ";
        objects[1] = "24736743@sina.com";
        jdbcTemplate.update(sql,objects);
        // Inquire about 
        return "updateOk";
    }

    // Delete user 
    @GetMapping("/delete/{id}")
    public String delUser(@PathVariable("id") int id){
        // Insert statement 
        String sql = "delete from employee where id=?";
        jdbcTemplate.update(sql,id);
        // Inquire about 
        return "deleteOk";
    }
    
}

The test request , Normal results ;

Here we are ,CURD Basic operation , Use JDBC Just like the .

9、 Integrate Druid

9.1、Druid brief introduction

Java A large part of the program operates on the database , In order to improve performance, when operating the database , You have to use the database connection pool .

Druid It is a database connection pool on Alibaba open source platform , Combined with the C3P0、DBCP etc. DB The advantages of the pool , At the same time, log monitoring is added .

Druid It's a good monitor DB Pool connection and SQL Implementation of , Born for surveillance DB Connection pool .

Druid More than has been deployed in alibaba 600 Applications , After more than a year of large-scale deployment in production environments .

Spring Boot 2.0 The above defaults to Hikari data source , so to speak Hikari And Driud It's all current Java Web The best data source in the world , Let's focus on Spring Boot How to integrate Druid data source , How to realize database monitoring .

Github Address :https://github.com/alibaba/druid/

com.alibaba.druid.pool.DruidDataSource The basic configuration parameters are as follows :

image-20210131124949216

image-20210131125001259

image-20210131125014962

9.2、 Configure data sources

1、 add Druid Data sources depend on .

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

2、 Switch data source ; I've said before Spring Boot 2.0 The above defaults to com.zaxxer.hikari.HikariDataSource data source , But it can adopt spring.datasource.type specify data source .

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource #  Custom data sources 

3、 After switching data sources , Inject... Into the test class DataSource, And get it , Output to see if the switch is successful ;

image-20210131125108560

4、 Switch successful ! Since the switch was successful , You can set the initialization size of the data source connection 、 maximum connection 、 Waiting time 、 Minimum connections And so on ; You can check the source code

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC Solve the time zone error 
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot  By default, these property values are not injected , You need to bind yourself 
    #druid  Data source specific configuration 
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    # Configure monitoring statistics interception filters,stat: Monitoring statistics 、log4j: logging 、wall: defense sql Inject 
    # If the times are allowed to be wrong   java.lang.ClassNotFoundException: org.apache.log4j.Priority
    # Then import  log4j  Rely on it ,Maven  Address :https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

5、 Import Log4j Dependence

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

6、 Now it's up to programmers to do it for themselves DruidDataSource Bind parameters in global configuration file , Add it to the container , No longer used Spring Boot Automatically generated ; We need to Add... Yourself DruidDataSource Component into container , And bind properties ;

package com.kuang.config;

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

import javax.sql.DataSource;

@Configuration
public class DruidConfig {

    /*
        Will customize  Druid Data source added to container , Don't let  Spring Boot  Automatically create 
        Bind... In the global configuration file  druid  Data source properties to  com.alibaba.druid.pool.DruidDataSource So that they work 
       @ConfigurationProperties(prefix = "spring.datasource"): What it does is it takes   In the global configuration file 
        The prefix for  spring.datasource The property value of is injected into  com.alibaba.druid.pool.DruidDataSource  In the parameter of the same name 
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

}

7、 Test it in the test class ; See if it works !

@SpringBootTest
class SpringbootDataJdbcApplicationTests {

    //DI Inject data source 
    @Autowired
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        // Take a look at the default data source 
        System.out.println(dataSource.getClass());
        // Obtain a connection 
        Connection connection =   dataSource.getConnection();
        System.out.println(connection);

        DruidDataSource druidDataSource = (DruidDataSource) dataSource;
        System.out.println("druidDataSource  Data source maximum connections :" + druidDataSource.getMaxActive());
        System.out.println("druidDataSource  Number of data source initialization connections :" + druidDataSource.getInitialSize());

        // Close the connection 
        connection.close();
    }
}

Output results : Visible configuration parameters are in effect !

image-20210131125305546

9.3、 To configure Druid Data source monitoring

Druid The data source has the function of monitoring , And provides a web The interface is convenient for users to view , Similar installation Router when , They also provided a default web page .

So the first step is to set up Druid Background management page , such as Login account 、 password etc. ; Configure background management ;

// To configure  Druid  Monitor and manage the backstage Servlet;
// built-in  Servlet  There is no web.xml file , So use  Spring Boot  Registration of  Servlet  The way 
@Bean
public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

    //  These parameters can be found in  com.alibaba.druid.support.http.StatViewServlet 
    //  Parent class of  com.alibaba.druid.support.http.ResourceServlet  Find 
    Map<String, String> initParams = new HashMap<>();
    initParams.put("loginUsername", "admin"); // Login account of background management interface 
    initParams.put("loginPassword", "123456"); // Background management interface login password 

    // Who is allowed access in the background 
    //initParams.put("allow", "localhost"): Indicates that only the local machine can access 
    //initParams.put("allow", ""): Is empty or null when , Indicates that all access is allowed 
    initParams.put("allow", "");
    //deny:Druid  Who is denied access in the background 
    //initParams.put("kuangshen", "192.168.1.20"); To prohibit ip visit 

    // Set initialization parameters 
    bean.setInitParameters(initParams);
    return bean;
}

After configuration , We can choose to visit :http://localhost:8080/druid/login.html

image-20210131125352413

After entering

image-20210131125400157

To configure Druid web monitor filter filter

// To configure  Druid  monitor   And   web  Monitored  filter
//WebStatFilter: Used for configuration Web and Druid Management association between data sources monitoring statistics 
@Bean
public FilterRegistrationBean webStatFilter() {
   FilterRegistrationBean bean = new FilterRegistrationBean();
   bean.setFilter(new WebStatFilter());

   //exclusions: Set which requests to filter out , So no statistics 
   Map<String, String> initParams = new HashMap<>();
   initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
   bean.setInitParameters(initParams);

   //"/*"  Means filter all requests 
   bean.setUrlPatterns(Arrays.asList("/*"));
   return bean;
}

10、 Integrate MyBatis

Official documents :http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/

Maven Warehouse address :https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter/2.1.1

image-20210131125556576

10.1、 Integration testing

1、 Import MyBatis The dependence needed

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

2、 Configure the database connection information ( unchanged )

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC Solve the time zone error 
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot  By default, these property values are not injected , You need to bind yourself 
    #druid  Data source specific configuration 
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    # Configure monitoring statistics interception filters,stat: Monitoring statistics 、log4j: logging 、wall: defense sql Inject 
    # If the times are allowed to be wrong   java.lang.ClassNotFoundException: org.apache.log4j.Priority
    # Then import  log4j  Rely on it ,Maven  Address :https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3、 Test whether the database is connected successfully !

4、 Create entity class , Import Lombok!

Department.java

package com.kuang.pojo;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {

    private Integer id;
    private String departmentName;

}

5、 establish mapper Directory and corresponding Mapper Interface

DepartmentMapper.java

//@Mapper :  Indicates that this class is a  MyBatis  Of  Mapper
@Mapper
@Repository
public interface DepartmentMapper {

    //  Get all department information 
    List<Department> getDepartments();

    //  adopt id The acquisition Department 
    Department getDepartment(Integer id);

}

6、 Corresponding Mapper The mapping file

DepartmentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.mapper.DepartmentMapper">

    <select id="getDepartments" resultType="Department">
       select * from department;
    </select>

    <select id="getDepartment" resultType="Department" parameterType="int">
       select * from department where id = #{id};
    </select>

</mapper>

7、maven Configuration resource filtering problem

<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

8、 Write the Department's DepartmentController To test !

@RestController
public class DepartmentController {
    
    @Autowired
    DepartmentMapper departmentMapper;
    
    //  Check all departments 
    @GetMapping("/getDepartments")
    public List<Department> getDepartments(){
        return departmentMapper.getDepartments();
    }

    //  Check all departments 
    @GetMapping("/getDepartment/{id}")
    public Department getDepartment(@PathVariable("id") Integer id){
        return departmentMapper.getDepartment(id);
    }
    
}

Start project access to test !

Let's add an employee class and test it again , Prepare for the future

1、 Create a new one pojo class Employee ;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {

   private Integer id;
   private String lastName;
   private String email;
   //1 male, 0 female
   private Integer gender;
   private Integer department;
   private Date birth;
   private Department eDepartment; //  Redundant design 
}

2、 Create a new one EmployeeMapper Interface

//@Mapper :  Indicates that this class is a  MyBatis  Of  Mapper
@Mapper
@Repository
public interface EmployeeMapper {

    //  Get all employee information 
    List<Employee> getEmployees();

    //  A new employee 
    int save(Employee employee);

    //  adopt id Get employee information 
    Employee get(Integer id);

    //  adopt id Delete employee 
    int delete(Integer id);

}

3、 To write EmployeeMapper.xml The configuration file

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.mapper.EmployeeMapper">

    <resultMap id="EmployeeMap" type="Employee">
        <id property="id" column="eid"/>
        <result property="lastName" column="last_name"/>
        <result property="email" column="email"/>
        <result property="gender" column="gender"/>
        <result property="birth" column="birth"/>
        <association property="eDepartment"  javaType="Department">
            <id property="id" column="did"/>
            <result property="departmentName" column="dname"/>
        </association>
    </resultMap>

    <select id="getEmployees" resultMap="EmployeeMap">
        select e.id as eid,last_name,email,gender,birth,d.id as did,d.department_name as dname
        from department d,employee e
        where d.id = e.department
    </select>

    <insert id="save" parameterType="Employee">
        insert into employee (last_name,email,gender,department,birth)
        values (#{lastName},#{email},#{gender},#{department},#{birth});
    </insert>

    <select id="get" resultType="Employee">
        select * from employee where id = #{id}
    </select>

    <delete id="delete" parameterType="int">
        delete from employee where id = #{id}
    </delete>

</mapper>

4、 To write EmployeeController Class to test

@RestController
public class EmployeeController {

    @Autowired
    EmployeeMapper employeeMapper;

    //  Get all employee information 
    @GetMapping("/getEmployees")
    public List<Employee> getEmployees(){
        return employeeMapper.getEmployees();
    }

    @GetMapping("/save")
    public int save(){
        Employee employee = new Employee();
        employee.setLastName("kuangshen");
        employee.setEmail("qinjiang@qq.com");
        employee.setGender(1);
        employee.setDepartment(101);
        employee.setBirth(new Date());
        return employeeMapper.save(employee);
    }

    //  adopt id Get employee information 
    @GetMapping("/get/{id}")
    public Employee get(@PathVariable("id") Integer id){
        return employeeMapper.get(id);
    }

    //  adopt id Delete employee 
    @GetMapping("/delete/{id}")
    public int delete(@PathVariable("id") Integer id){
        return employeeMapper.delete(id);
    }

}

11、Web Developing static resource processing

11.1、Web Development and exploration

brief introduction

Use SpringBoot Steps for :

1、 Create a SpringBoot application , Choose the modules we need ,SpringBoot We will automatically configure our required modules by default

2、 Manually configure part of the configuration items in the configuration file to run

3、 Focus on writing business code , There's no need to think about a lot of configurations like before .

Be familiar with development , The principle of automatic configuration learned before must be understood !

such as SpringBoot What did you configure for us ? Can we modify ? Which configurations can we modify ? Can we expand ?

  • Automatically configure components into containers :*** Autoconfiguration
  • Automatic configuration class , Encapsulate the contents of the configuration file :***Properties

11.2、 Static resource processing

First , Let's build a normal SpringBoot project , Take a look back. HelloWorld Program !

Writing requests is very simple , Then we need to introduce our front-end resources , There are many static resources in our project , such as css,js Wait for the documents , This SpringBoot How to deal with it ?

If we were a web application , our main There will be one next webapp, We used to guide all the pages here , Right ! But what we have now pom Well , The packaging is for jar The way , So this way SpringBoot Can you write us a page ? Of course you can , however SpringBoot For static resource placement , There are rules !

Let's talk about this static resource mapping rule first :

SpringBoot in ,SpringMVC Of web Configuration is all in WebMvcAutoConfiguration In this configuration class ;

We can go and see WebMvcAutoConfigurationAdapter There are many ways to configure ;

There's a way :addResourceHandlers Add resource handling

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        //  Default resource handling Disabled 
        logger.debug("Default resource handling disabled");
        return;
    }
    //  Cache control 
    Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
    CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
    // webjars  To configure 
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                                             .addResourceLocations("classpath:/META-INF/resources/webjars/")
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
    //  Static resource allocation 
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                                             .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
}

Read the source code : Like all /webjars/** , All need to go to classpath:/META-INF/resources/webjars/ Find the corresponding resource ;

What is? webjars Well ?

Webjars The essence is to jar Package way to introduce our static resources , We used to import a static resource file , Import directly .

Use SpringBoot Need to use Webjars, We can search for :

Website :https://www.webjars.org

To use jQuery, We just need to introduce jQuery Corresponding version pom Rely on it !

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

Import finished , see webjars Directory structure , And access Jquery.js file !

image-20210131130636120

visit : As long as it's a static resource ,SpringBoot Will go to the corresponding path to find resources , We are here to visit :http://localhost:8080/webjars/jquery/3.4.1/jquery.js

image-20210131130649013

The second static resource mapping rule

How can we import our own static resources in our project ? Let's look at the next line of code ;

Let's find out staticPathPattern Find the second mapping rule :/** , Access any resources in the current project , It will look for resourceProperties This class , We can click in and look at the analysis :


//  How to get in 
public String[] getStaticLocations() {
    return this.staticLocations;
}
//  Find the corresponding value 
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
//  Find the way 
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { 
    "classpath:/META-INF/resources/",
  "classpath:/resources/", 
    "classpath:/static/", 
    "classpath:/public/" 
};

ResourceProperties You can set parameters related to our static resources ; It points to the folder where it will search for resources , That is, the contents of the above array .

So come to the conclusion , The static resources stored in the following four directories can be identified by us :

"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"

We can do it in resources Create a new folder under the root directory , Can store our static files ;

For example, we visit http://localhost:8080/1.js , He will go to these folders to find the corresponding static resource files ;

Custom static resource path

We can also specify it by ourselves through the configuration file , Which folders need us to put static resource files , stay application.properties Middle configuration ;

spring.resources.static-locations=classpath:/coding/,classpath:/kuang/

Once you define the path to the static folder , The original auto configuration will be invalid !

11.3、 Home page processing

When the static resource folder is finished , Let's continue to look down at the source code ! You can see a map of the welcome page , It's our home page !

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
                                                           FormattingConversionService mvcConversionService,
                                                           ResourceUrlProvider mvcResourceUrlProvider) {
    
    WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
        new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), // getWelcomePage  Get the welcome page 
        this.mvcProperties.getStaticPathPattern());
    welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
    return welcomePageHandlerMapping;
}

Click in and continue to see

private Optional<Resource> getWelcomePage() {
    String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
    // :: yes java8  New operators introduced in 
    // Class::function When function It belongs to Class Of , It should be static .
    // this::function Of funtion It belongs to this object .
    //  In short , It's just a grammatical sugar , It's a shorthand 
    return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
//  The welcome page is a location The next  index.html  nothing more 
private Resource getIndexHtml(String location) {
    return this.resourceLoader.getResource(location + "index.html");
}

The welcome page , All... Under static resource folder index.html page ; By /** mapping .

For example, I visit http://localhost:8080/ , Will find the static resources folder index.html

Create a new one index.html , On top of us 3 Any one of these directories ; Then access the test http://localhost:8080/ Look at the results !<

About website icon description

image-20210131131109339

Like other static resources ,Spring Boot Find... In the configured static content location favicon.ico. If there is such a file , It will automatically be used as the favicon.

1、 close SpringBoot Default icon

# Turn off the default icon 
spring.mvc.favicon.enabled=false

2、 Put an icon in the static resource directory , I put it in public Under the table of contents

3、 Clear browser cache ! Refresh web page , I found that the icon has become my own !

image-20210131131131406

12、Thymeleaf template engine

The page that the front end gave us , yes html page . If we developed it before , We need to turn them into jsp page ,jsp The advantage is that when we find out some data to forward to JSP After the page , We can use jsp Easy to achieve data display , And interaction .

jsp Support very powerful features , Including being able to write Java Code , But what? , The situation we are in now ,SpringBoot This project starts with jar The way , No war, Like the second , We still use embedded Tomcat, So , He now acquiesces that he doesn't support jsp Of .

That doesn't support jsp, If we use the pure static page directly , That will bring us a lot of trouble in development , Then what shall I do? ?

SpringBoot You can use the template engine :

template engine , We've heard a lot about it , Actually jsp It's a template engine , There are also more useful freemarker, Include SpringBoot I recommend Thymeleaf, There are a lot of template engines , But no more template engines , They all have the same ideas , What kind of idea? Let's take a look at this picture :

image-20210131131210667

The function of template engine is to write a page template , For example, some values , Is dynamic , Let's write some expressions . And these are worth , Where do you come from , We encapsulate some data in the background . Then give the template and the data to our template engine , The template engine will help you parse the expression according to our data 、 Fill to our designated location , Then the data is finally generated into a content we want to write for us , This is our template engine , Whether it's jsp Or other template engines , It's all about this idea . It's just , That is to say, between different template engines , They may have different grammar . I won't introduce the others , Let me mainly introduce SpringBoot I recommend Thymeleaf template engine , What about the template engine , Is a high-level language template engine , His grammar is simpler . And , More powerful .

We? , Let's take a look at the template engine , Since it depends on the template engine . First , Let's see SpringBoot How to use it inside .

introduce Thymeleaf

How to introduce , about springboot Come on , Everything is not one start It's a very important thing , Let's introduce... Into the project . Here are three websites :

Thymeleaf Official website :https://www.thymeleaf.org/

Thymeleaf stay Github The home page of :https://github.com/thymeleaf/thymeleaf

Spring Official documents : Find our version

https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter

Find the corresponding pom rely on : You can click into the source code to see the original package !

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

Maven Will automatically download jar package , We can take a look at the downloads ;

image-20210131131243997

12.1、Thymeleaf analysis

What about the front , We have introduced Thymeleaf, How to use this ?

We first have to follow SpringBoot Let's take a look at this Thymeleaf Automatic configuration rules for , According to that rule , We use it .

Let's find out Thymeleaf The automatic configuration class :ThymeleafProperties

@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
    private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML";
    private Charset encoding;
}

We can see the default prefix and suffix in it !

We just need to put our html The page is placed under the classpath templates Next ,thymeleaf It can help us automatically render .

Use thymeleaf Nothing needs to be configured , Just put it in the designated folder !

test

1、 Write a TestController

@Controller
public class TestController {
    
    @RequestMapping("/t1")
    public String test1(){
        //classpath:/templates/test.html
        return "test";
    }
    
}

2、 Write a test page test.html Put it in templates Under the table of contents

<!DOCTYPE html>
<html lang="en">
<head>
    
    <title>Title</title>
</head>
<body>
<h1> The test page </h1>

</body>
</html>

3、 Start the project request test

12.2、Thymeleaf Grammar learning

To learn grammar , It is the most accurate to refer to the official website , Let's find the corresponding version and have a look ;

Thymeleaf Official website :https://www.thymeleaf.org/ , Take a brief look at the official website ! Let's go download Thymeleaf Official documents of !

Let's do the simplest exercise : We need to find some data , Show it on the page

1、 Modify the test request , Increase data transmission ;

@RequestMapping("/t1")
public String test1(Model model){
    // In the data 
    model.addAttribute("msg","Hello,Thymeleaf");
    //classpath:/templates/test.html
    return "test";
}

2、 We're going to use thymeleaf, Need to be in html Import constraints for the namespace in the file , Convenient tips .

We can go to the official document #3 Let's take a look in the middle. Bring it here :

xmlns:th="http://www.thymeleaf.org"

3、 Let's write the front page

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    
    <title> Madness theory </title>
</head>
<body>
<h1> The test page </h1>

<!--th:text Will be div Set the content in to the value it specifies , And what I learned before Vue equally -->
<div th:text="${msg}"></div>
</body>
</html>

4、 Start the test !

image-20210131131603064

OK, Get started , Let's have a serious study Thymeleaf The usage grammar of !

1、 We can use whatever th:attr To replace Html The value of the original attribute !

image-20210131131617402

2、 What expressions can we write ?


Simple expressions:( Expression syntax )
Variable Expressions: ${...}: Get the value of the variable ;OGNL;
    1)、 Get the properties of an object 、 Calling method 
    2)、 Use built-in base objects :#18
         #ctx : the context object.
         #vars: the context variables.
         #locale : the context locale.
         #request : (only in Web Contexts) the HttpServletRequest object.
         #response : (only in Web Contexts) the HttpServletResponse object.
         #session : (only in Web Contexts) the HttpSession object.
         #servletContext : (only in Web Contexts) the ServletContext object.

    3)、 Some built-in tool objects :
      #execInfo : information about the template being processed.
      #uris : methods for escaping parts of URLs/URIs
      #conversions : methods for executing the configured conversion service (if any).
      #dates : methods for java.util.Date objects: formatting, component extraction, etc.
      #calendars : analogous to #dates , but for java.util.Calendar objects.
      #numbers : methods for formatting numeric objects.
      #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
      #objects : methods for objects in general.
      #bools : methods for boolean evaluation.
      #arrays : methods for arrays.
      #lists : methods for lists.
      #sets : methods for sets.
      #maps : methods for maps.
      #aggregates : methods for creating aggregates on arrays or collections.
==================================================================================

  Selection Variable Expressions: *{...}: Select expression : and ${} It's the same in function ;
  Message Expressions: #{...}: Get international content 
  Link URL Expressions: @{...}: Definition URL;
  Fragment Expressions: ~{...}: Fragment reference expression 

Literals( Literal )
      Text literals: 'one text' , 'Another one!' ,…
      Number literals: 0 , 34 , 3.0 , 12.3 ,…
      Boolean literals: true , false
      Null literal: null
      Literal tokens: one , sometext , main ,…
      
Text operations:( Text operation )
    String concatenation: +
    Literal substitutions: |The name is ${name}|
    
Arithmetic operations:( Mathematical operations )
    Binary operators: + , - , * , / , %
    Minus sign (unary operator): -
    
Boolean operations:( Boolean operation )
    Binary operators: and , or
    Boolean negation (unary operator): ! , not
    
Comparisons and equality:( Comparison operations )
    Comparators: > , < , >= , <= ( gt , lt , ge , le )
    Equality operators: == , != ( eq , ne )
    
Conditional operators: Conditional operation ( Ternary operator )
    If-then: (if) ? (then)
    If-then-else: (if) ? (then) : (else)
    Default: (value) ?: (defaultvalue)
    
Special tokens:
    No-Operation: _

Practice testing :

1、 Let's write a Controller, Put in some data

@RequestMapping("/t2")
public String test2(Map<String,Object> map){
    // In the data 
    map.put("msg","<h1>Hello</h1>");
    map.put("users", Arrays.asList("qinjiang","kuangshen"));
    //classpath:/templates/test.html
    return "test";
}

2、 The test page fetches the data


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    
    <title> Madness theory </title>
</head>
<body>
<h1> The test page </h1>

<div th:text="${msg}"></div>
<!-- No escape -->
<div th:utext="${msg}"></div>

<!-- Traversal data -->
<!--th:each Each iteration will generate the current tag : Official website #9-->
<h4 th:each="user :${users}" th:text="${user}"></h4>

<h4>
    <!-- Inline writing : Official website #12-->
    <span th:each="user:${users}">[[${user}]]</span>
</h4>

</body>
</html>

3、 Start project testing !

13、MVC Auto configuration principle

Read on the official website

Before writing a project , There's one more thing we need to know , Namely SpringBoot To our SpringMVC What other configurations have been made , Including how to expand , How to customize .

Only when we get all this clear , We can use it more easily later . Way one : Source code analysis , Way two : Official documents !

Address :https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration

Spring MVC Auto-configuration
// Spring Boot by Spring MVC Provides automatic configuration , It works well with most applications .
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
//  Automatic configuration in Spring Based on the default settings, the following functions are added :
The auto-configuration adds the following features on top of Spring’s defaults:
//  Contains the view parser 
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
//  Supports static resource folder paths , as well as webjars
Support for serving static resources, including support for WebJars 
//  Automatically registered Converter:
//  converter , This is the thing that our web page submits data to the background and automatically encapsulates it as an object , For example "1" The string is automatically converted to int type 
// Formatter:【 formatter , For example, the page gives us a 2019-8-10, It will automatically format to Date object 】
Automatic registration of Converter, GenericConverter, and Formatter beans.
// HttpMessageConverters
// SpringMVC To convert Http Request and response , For example, we're going to put a User Object to JSON character string , You can go to the official website to explain ;
Support for HttpMessageConverters (covered later in this document).
//  Defining error code generation rules 
Automatic registration of MessageCodesResolver (covered later in this document).
//  Home page customization 
Static index.html support.
//  Icon customization 
Custom Favicon support (covered later in this document).
//  Initialize the data binder : Help us bind the request data to JavaBean in !
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

/*
 If you want to keep Spring Boot MVC function , And want to add other MVC To configure ( Interceptor 、 formatter 、 View controllers and other features ), You can add yourself 
 Of @configuration class , The type is webmvcconfiguer, But don't add @EnableWebMvc. If you want to provide 
RequestMappingHandlerMapping、RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver The custom of 
 example , Then you can declare WebMVCregistrationAdapter Instance to provide such components .
*/
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration 
(interceptors, formatters, view controllers, and other features), you can add your own 
@Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide 
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

//  If you want total control Spring MVC, You can add your own @Configuration, And use @EnableWebMvc Annotate .
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

Let's make a careful comparison , See how it works , It tells us SpringBoot It's automatically configured for us SpringMVC, And then automatically configure what ?

13.1、ContentNegotiatingViewResolver Content negotiation view parser

Automatically configured ViewResolver, That's what we learned before SpringMVC View parser ;

That is, get the view object according to the return value of the method (View), Then it's up to the view object to decide how to render ( forward , Redirect ).

Let's take a look at the source code here : We find WebMvcAutoConfiguration , And then the search ContentNegotiatingViewResolver. Find the following !

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
    // ContentNegotiatingViewResolver Use all the other view parsers to locate the view , So it should have a higher priority 
    resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return resolver;
}

We can click in this category ! Find the corresponding parsing view code ;

@Nullable //  Note that :@Nullable  That is, the parameter can be null
public View resolveViewName(String viewName, Locale locale) throws Exception {
   RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
   Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
   List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
   if (requestedMediaTypes != null) {
       //  Get candidate view objects 
       List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
       //  Select the most appropriate view object , And then return this object to 
       View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
       if (bestView != null) {
           return bestView;
       }
   }
   // .....
}

Let's go ahead and see , How did he get the candidate view ?

getCandidateViews We can see that he brought all the view parsers , Conduct while loop , Parsing one by one !

Iterator var5 = this.viewResolvers.iterator();

So come to the conclusion :ContentNegotiatingViewResolver This view parser is used to combine all view parsers

Let's go back to his combinatorial logic , See there's an attribute viewResolvers, See where it's assigned !

protected void initServletContext(ServletContext servletContext) {
    //  Here it is from beanFactory Tool to get all the view parsers in the container 
    // ViewRescolver.class  Combine all the view parsers 
    Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
    ViewResolver viewResolver;
    if (this.viewResolvers == null) {
        this.viewResolvers = new ArrayList(matchingBeans.size());
    }
    // ...............
}

Since it's looking for the view parser in the container , Can we guess , We can implement a view parser ?

We can add a view parser to the container ourselves ; This class will help us automatically combine it ; Let's make it happen

1、 Let's try to write a view parser in our main program ;

@Bean // Put it in bean in 
public ViewResolver myViewResolver(){
    return new MyViewResolver();
}

// Let's write a static inner class , The view parser needs to implement ViewResolver Interface 
private static class MyViewResolver implements ViewResolver{
    @Override
    public View resolveViewName(String s, Locale locale) throws Exception {
        return null;
    }
}

2、 How can we see if our own view parser works ?

We give DispatcherServlet Medium doDispatch Method Add a breakpoint for debugging , Because all requests go into this method

image-20210131132343363

3、 We started our project , And then go to any page , to glance at Debug Information ;

find this

image-20210131132352905

Find the view parser , We see our own definition right here ;

image-20210131132403962

So , If we want to use our own customized things , We just need to add this component to the container ! The rest SpringBoot Will do it for us !

Converters and formatters

Find the format converter :

@Bean
@Override
public FormattingConversionService mvcConversionService() {
    //  Get the formatting rules in the configuration file 
    WebConversionService conversionService = 
        new WebConversionService(this.mvcProperties.getDateFormat());
    addFormatters(conversionService);
    return conversionService;
}

Click to :

public String getDateFormat() {
    return this.dateFormat;
}

/**
* Date format to use. For instance, `dd/MM/yyyy`.  default 
 */
private String dateFormat;

You can see in our Properties In file , We can configure it automatically !

If you configure your own formatting , Will register to Bean Enter into force , We can configure date formatting rules in the configuration file :

image-20210131132452923

The rest will not be exemplified one by one , You can go down and study more !

13.2、 modify SpringBoot Default configuration

So much autoconfiguration , The principle is the same , Through this WebMVC Automatic configuration principle analysis of , We need to learn a way of learning , By exploring the source code , Come to the conclusion ; This conclusion must belong to itself , And it's all over the place .

SpringBoot The bottom of the , A lot of these design details , therefore , No need to read more source code ! Come to the conclusion ;

SpringBoot When configuring many components automatically , Let's see if there is any user configured in the container ( If the user configures @bean), If there is one, use the user configuration , If not, use the auto configuration ;

If some components can have multiple , For example, our view parser , Just combine the user's configuration with your own default !

Extended use SpringMVC The official documents are as follows :

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

All we have to do is write a @Configuration Annotation class , And the type is WebMvcConfigurer, Not yet @EnableWebMvc annotation ; Let's go and write our own ; Let's create a new package called config, Write a class MyMvcConfig;

// Expected type. Required WebMvcConfigurer, So we implement the interface 
// You can use custom class extensions MVC The function of 
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //  Browser send /test ,  It will jump to test page ;
        registry.addViewController("/test").setViewName("test");
    }
}

Let's go to the browser and visit :

image-20210131135934640

It's true that it's also coming ! So , We're going to expand SpringMVC, The official recommends that we use it like this , Existing insurance SpringBoot Leave all the autoconfiguration , We can also use our extended configuration !

We can analyze the principle :

1、WebMvcAutoConfiguration yes SpringMVC The automatic configuration class , There is a class in it WebMvcAutoConfigurationAdapter

2、 There is an annotation on this class , It will be imported when making other automatic configuration :@Import(EnableWebMvcConfiguration.class)

3、 Let's go in EnableWebMvcConfiguration Take a look at this class , It inherits a parent class :DelegatingWebMvcConfiguration

There is such a piece of code in this parent class :

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    
  //  Get all the... From the container webmvcConfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
}

4、 We can find one in this class that we just set up viewController As a reference , Found that it called a

protected void addViewControllers(ViewControllerRegistry registry) {
    this.configurers.addViewControllers(registry);
}

5、 Let's go in and have a look

public void addViewControllers(ViewControllerRegistry registry) {
    Iterator var2 = this.delegates.iterator();

    while(var2.hasNext()) {
        //  Will all WebMvcConfigurer Related configuration to call together ! Including our own configuration and Spring It's configured for us 
        WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
        delegate.addViewControllers(registry);
    }

}

So come to the conclusion : be-all WebMvcConfiguration Will be affected , More than Spring Your own configuration class , Of course, our own configuration class will also be called ;

13.3、 Full takeover SpringMVC

Official documents :

If you want to take complete control of Spring MVC
you can add your own @Configuration annotated with @EnableWebMvc.

A full takeover is :SpringBoot Yes SpringMVC The automatic configuration of does not need , All of them are configured by ourselves !

Just add one to our configuration class @EnableWebMvc.

Let's see if we take over completely SpringMVC 了 , Before us SpringBoot The static resource mapping configured for us is bound to be invalid , We can test it ;

Before no comment , Visit the home page :

image-20210131140101331

Annotate the configuration class :@EnableWebMvc

image-20210131140113883

We found out all about SpringMVC Automatic configuration is out of order ! Back to the original ;

Of course , We are developing , Full take over is not recommended SpringMVC

Think about the problem ? Why add a note , Automatic configuration fails ! Let's look at the source code :

1、 Here we find that it imports a class , We can go in and see

@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

2、 It inherits a parent class WebMvcConfigurationSupport

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
  // ......
}

3、 Let's review Webmvc Automatic configuration class

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//  This note means : When this component is not in the container , This autoconfig class takes effect 
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
    ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    
}

To sum up :@EnableWebMvc take WebMvcConfigurationSupport Components are imported in ;

And imported WebMvcConfigurationSupport It's just SpringMVC The most basic function !

stay SpringBoot There will be a lot of extended configurations in , Just see this , We should pay more attention to ~

14、 Page internationalization

14.1、 preparation

First in IDEA Unified settings in properties The coding problem !

image-20210131141633730

Write internationalization profile , Extract the internationalized page message that the page needs to display . We can go to the login page to see , What content do we need to write international configuration !

14.2、 Configuration file writing

1、 We are resources Create a new one under the resource file i18n Catalog , Store international profiles

2、 Build a login.properties file , One more login_zh_CN.properties; Find out IDEA Automatically identified that we need to do international operations ; The folder has changed !

image-20210131141659420

3、 We can create a new file on it ;

image-20210131141711213

The following page will pop up : Let's add another one in English ;

image-20210131141721100

It's much faster !

image-20210131141734254

4、 Next , Let's write the configuration , We can see idea Here's another view ;

image-20210131141747643

In this view, we click + You can add attributes directly by using the ; We build a new one login.tip, You can see that there are three file boxes on the side that you can enter

image-20210131141757595

Let's add the content of the home page !

image-20210131141807180

Then add other page contents in turn !

image-20210131141818315

Then go to our profile ;

login.properties : Default

login.btn= Sign in 
login.password= password 
login.remember= Remember me 
login.tip= Please log in 
login.username= user name 

english :

login.btn=Sign in
login.password=Password
login.remember=Remember me
login.tip=Please sign in
login.username=Username

chinese :

login.btn= Sign in 
login.password= password 
login.remember= Remember me 
login.tip= Please log in 
login.username= user name 

OK, The configuration file steps are done !

14.3、 Profile validation

Let's go and have a look at SpringBoot Automatic configuration of internationalization ! Here's another class :MessageSourceAutoConfiguration

There is a way , Here we find SpringBoot The components that manage our international resource files have been automatically configured ResourceBundleMessageSource;

//  obtain  properties  Pass the value to judge 
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    if (StringUtils.hasText(properties.getBasename())) {
        //  Set the base name of the internationalization file ( Remove the language country code )
        messageSource.setBasenames(
            StringUtils.commaDelimitedListToStringArray(
                                       StringUtils.trimAllWhitespace(properties.getBasename())));
    }
    if (properties.getEncoding() != null) {
        messageSource.setDefaultEncoding(properties.getEncoding().name());
    }
    messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
    Duration cacheDuration = properties.getCacheDuration();
    if (cacheDuration != null) {
        messageSource.setCacheMillis(cacheDuration.toMillis());
    }
    messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
    messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
    return messageSource;
}

We are true In the case of i18n Under the table of contents , So we're going to configure this messages The path of ;

spring.messages.basename=i18n.login

14.4、 Configure page internationalization values

Go to the page to get the internationalization value , see Thymeleaf Documents , find message The value operation is :#{...}. Let's go to the page and test it :

IDEA There are also hints , Very intelligent !

image-20210131142146947

We can start the project , Visit , Found that it has been automatically recognized as Chinese !

But we want to be better ! You can switch between Chinese and English automatically according to the button !

14.5、 Configuration internationalization analysis

stay Spring There is an international Locale ( Area information object ); There is one called LocaleResolver ( Get area information object ) The parser !

Let's go. Let's webmvc Auto profile , Look for ! notice SpringBoot The default configuration :

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
    //  If you don't have it in the container, just match it yourself , If there is one, it will be configured by the user 
    if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
    }
    //  Receiver head internationalization decomposition 
    AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
    return localeResolver;
}

AcceptHeaderLocaleResolver There is a method in this class

public Locale resolveLocale(HttpServletRequest request) {
    Locale defaultLocale = this.getDefaultLocale();
    //  The default is to obtain the area information brought by the request header Locale Internationalization 
    if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
        return defaultLocale;
    } else {
        Locale requestLocale = request.getLocale();
        List<Locale> supportedLocales = this.getSupportedLocales();
        if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
            Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
            if (supportedLocale != null) {
                return supportedLocale;
            } else {
                return defaultLocale != null ? defaultLocale : requestLocale;
            }
        } else {
            return requestLocale;
        }
    }
}

So if we want to click the link now to make our international resources effective , We need to let our own Locale take effect !

Let's write our own LocaleResolver, You can carry area information on the link !

Modify the jump link on the front page :

<!--  There is no need to use  ? Use  (key=value)-->
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}"> chinese </a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>

Let's write a processing component class !

package com.kuang.component;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

// You can carry area information on the link 
public class MyLocaleResolver implements LocaleResolver {

    // Resolve request 
    @Override
    public Locale resolveLocale(HttpServletRequest request) {

        String language = request.getParameter("l");
        Locale locale = Locale.getDefault(); //  If not, use the system default 
        // If the request link is not empty 
        if (!StringUtils.isEmpty(language)){
            // Split request parameters 
            String[] split = language.split("_");
            // Country , region 
            locale = new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }
}

To make our regionalized information work , We need to configure this component again ! In our own MvcConofig Add below bean;

@Bean
public LocaleResolver localeResolver(){
    return new MyLocaleResolver();
}

15、 Integrate Swagger

image-20210131143319826

Learning goals :

  • understand Swagger The concept and function of
  • Master the integration in the project Swagger Automatic generation API file

15.1、Swagger brief introduction

Fore and aft end separation

  • front end -> Front end control layer 、 View layer
  • Back end -> Back end control layer 、 Service layer 、 Data access layer
  • Through the front and rear end API Interact
  • The front and rear ends are relatively independent and loosely coupled

The problems that arise

  • Front and rear integration , Front end or back end can't do it “ Timely consultation , Settle as soon as possible ”, Finally, the problem is concentrated

Solution

  • First define schema [ The outline of the plan ], And keep track of the latest API, Reduce integration risk

Swagger

  • Known as the most popular in the world API frame
  • Restful Api Document online automatic generator => API file And API Definition synchronization update
  • Direct operation , Online testing API
  • Support for multiple languages ( Such as :Java,PHP etc. )
  • Official website :https://swagger.io/

15.2、SpringBoot Integrate Swagger

SpringBoot Integrate Swagger => springfox, Two jar package

  • Springfox-swagger2
  • swagger-springmvc

Use Swagger

requirement :jdk 1.8 + otherwise swagger2 Unable to run

step :

1、 Create a new one SpringBoot-web project

2、 add to Maven rely on

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

3、 To write HelloController, The test ensures that it runs successfully !

4、 To use Swagger, We need to write a configuration class -SwaggerConfig To configure the Swagger

@Configuration // Configuration class 
@EnableSwagger2//  Turn on Swagger2 Automatic configuration of 
public class SwaggerConfig {  
}

5、 Access test :http://localhost:8080/swagger-ui.html , You can see swagger The interface of ;

image-20210131143449960m

15.3、 To configure Swagger

1、Swagger example Bean yes Docket, So by configuring Docket Instance to configure Swaggger.

@Bean // To configure docket To configure Swagger Specific parameters 
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2);
}

2、 Can pass apiInfo() Property configuration document information

// Configuration documentation information 
private ApiInfo apiInfo() {
   Contact contact = new Contact(" Contact name ", "http://xxx.xxx.com/ Contact access link ", " Contact email ");
   return new ApiInfo(
           "Swagger Study ", //  title 
           " Learn to demonstrate how to configure Swagger", //  describe 
           "v1.0", //  edition 
           "http://terms.service.url/ Organizing Links ", //  Organizing Links 
           contact, //  Contact information 
           "Apach 2.0  The license ", //  The license 
           " License link ", //  Permission to connect 
           new ArrayList<>()//  Expand 
  );
}

3、Docket Instance Association apiInfo()

@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}

4、 Restart project , Access test http://localhost:8080/swagger-ui.html Look at the effect ;

15.4、 Configure scan interface

1、 structure Docket Through select() Method to configure how to scan the interface .

@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .select()//  adopt .select() Method , To configure the scan interface ,RequestHandlerSelectors Configure how to scan the interface 
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
      .build();
}

2、 Restart project testing , Because we configure the scan interface based on the path of the package , So we can only see one class

3、 In addition to configuring the scan interface through the packet path , You can also configure other ways to scan the interface , Here's a comment on all the configuration methods :

any() //  Scan all , All interfaces in the project will be scanned 
none() //  Don't scan the interface 
//  Scan through annotations on methods , Such as withMethodAnnotation(GetMapping.class) Just scan get request 
withMethodAnnotation(final Class<? extends Annotation> annotation)
//  Scan through annotations on classes , Such as .withClassAnnotation(Controller.class) Only scanning has controller Interfaces in annotated classes 
withClassAnnotation(final Class<? extends Annotation> annotation)
basePackage(final String basePackage) //  Scan the interface according to the packet path 

4、 besides , We can also configure interface scan filtering :

@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .select()//  adopt .select() Method , To configure the scan interface ,RequestHandlerSelectors Configure how to scan the interface 
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
       //  How to configure through path Filter , That is to say, only the request is scanned to /kuang The interface at the beginning 
      .paths(PathSelectors.ant("/kuang/**"))
      .build();
}

5、 The optional values here are

any() //  Any request is scanned 
none() //  No requests are scanned 
regex(final String pathRegex) //  Control... Through regular expressions 
ant(final String antPattern) //  adopt ant() control 

image-20210131152125474

15.5、 To configure Swagger switch

1、 adopt enable() Whether method configuration is enabled swagger, If it is false,swagger Will no longer be accessible in the browser

@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .enable(false) // Configure whether to enable Swagger, If it is false, The browser will not be able to access 
      .select()//  adopt .select() Method , To configure the scan interface ,RequestHandlerSelectors Configure how to scan the interface 
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
       //  How to configure through path Filter , That is to say, only the request is scanned to /kuang The interface at the beginning 
      .paths(PathSelectors.ant("/kuang/**"))
      .build();
}

2、 How to configure dynamically when the project is in test、dev The environment shows swagger, be in prod Time does not show ?

@Bean
public Docket docket(Environment environment) {
   //  Set to display swagger Environment 
   Profiles of = Profiles.of("dev", "test");
   //  Judge whether you are currently in the environment 
   //  adopt  enable()  Receive this parameter to determine whether to display 
   boolean b = environment.acceptsProfiles(of);
   
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .enable(b) // Configure whether to enable Swagger, If it is false, The browser will not be able to access 
      .select()//  adopt .select() Method , To configure the scan interface ,RequestHandlerSelectors Configure how to scan the interface 
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
       //  How to configure through path Filter , That is to say, only the request is scanned to /kuang The interface at the beginning 
      .paths(PathSelectors.ant("/kuang/**"))
      .build();
}

3、 You can add one to the project dev View the effect of the configuration file !

image-20210131152238830

15.6、 To configure API grouping

image-20210131152327743

1、 If grouping is not configured , The default is default. adopt groupName() Method to configure the grouping :

@Bean
public Docket docket(Environment environment) {
   return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
      .groupName("hello") //  Configure grouping 
       //  Omit configuration ....
}

2、 Restart the project view group

3、 How to configure multiple groups ? To configure multiple groups, you only need to configure multiple docket that will do :

@Bean
public Docket docket1(){
   return new Docket(DocumentationType.SWAGGER_2).groupName("group1");
}
@Bean
public Docket docket2(){
   return new Docket(DocumentationType.SWAGGER_2).groupName("group2");
}
@Bean
public Docket docket3(){
   return new Docket(DocumentationType.SWAGGER_2).groupName("group3");
}

4、 Restart the project to view

15.7、 Entity configuration

1、 Create a new entity class

@ApiModel(" User entity ")
public class User {
   @ApiModelProperty(" user name ")
   public String username;
   @ApiModelProperty(" password ")
   public String password;
}

2、 As long as the entity is Request interface On the return value of ( Even generics ), Can be mapped to entity items :

@RequestMapping("/getUser")
public User getUser(){
   return new User();
}

3、 Restart the view test

image-20210131152454418

notes : It's not because @ApiModel This annotation makes the entity appear here , Instead, any entity that appears on the return value of an interface method will be displayed here , and @ApiModel and @ApiModelProperty These two annotations are just annotations for entities .

@ApiModel Add comments to the class

@ApiModelProperty Add comments to class properties

15.8、 Commonly used annotations

Swagger All annotations defined in io.swagger.annotations It's a bag

Here are some of the most frequently used , For those not listed, please refer to the instructions separately :

Swagger annotation Briefly explain
@Api(tags = "xxx The module specification ") Works on module classes
@ApiOperation("xxx Interface specification ") It works on interface methods
@ApiModel("xxxPOJO explain ") Acting on model classes : Such as VO、BO
@ApiModelProperty(value = "xxx Attribute specification ",hidden = true) Works on class methods and properties ,hidden Set to true You can hide this property
@ApiParam("xxx Parameter description ") It acts on the parameter 、 Methods and fields , similar @ApiModelProperty

We can also configure some comments for the requested interface

@ApiOperation(" The interface of madness ")
@PostMapping("/kuang")
@ResponseBody
public String kuang(@ApiParam(" The name will be returned ")String username){
   return username;
}

In this case , You can give some properties or interfaces that are difficult to understand , Add some configuration information , Make it easier for people to read !

Compared with traditional Postman or Curl Mode test interface , Use swagger It's a fool's operation , No additional documentation is required ( Well written is a document in itself ) And it's less prone to mistakes , Just enter the data and click Execute, If combined with automation framework , It can be said that there is basically no need for human operation .

Swagger It's a great tool , Now many small and medium-sized Internet companies are using it in China , Compared to the traditional "first come first served" strategy Word How to retest interface documents , Obviously, this is more in line with the current rapid iterative development market . Yes, of course , I'd like to remind you to close it in a formal environment Swagger, First, for security reasons, and second, to save runtime memory .

16、 asynchronous 、 timing 、 Email tasks

16.1、 Asynchronous task

1、 Create a service package

2、 Create a class AsyncService

Asynchronous processing is still very common , For example, we send email on the website , Backstage will send mail , At this time, the front desk will not respond , Until the mail is sent , The response will be successful , So we usually use multithreading to deal with these tasks .

Compiling method , Pretending to be processing data , Use threads to set some latency , Simulate synchronization waiting ;

@Service
public class AsyncService {

   public void hello(){
       try {
           Thread.sleep(3000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(" Business in progress ....");
  }
}

3、 To write controller package

4、 To write AsyncController class

Let's write a Controller Test it

@RestController
public class AsyncController {

   @Autowired
   AsyncService asyncService;

   @GetMapping("/hello")
   public String hello(){
       asyncService.hello();
       return "success";
  }

}

5、 visit http://localhost:8080/hello To test ,3 Seconds later success, This is a synchronous wait situation .

problem : If we want users to get messages directly , Just use multithreading in the background , But every time you need to write a multithreaded implementation manually , It's too troublesome , We just need to do it in a simple way , Add a simple annotation to our method , as follows :

6、 to hello Method add @Async annotation ;

// tell Spring This is an asynchronous method 
@Async
public void hello(){
   try {
       Thread.sleep(3000);
  } catch (InterruptedException e) {
       e.printStackTrace();
  }
   System.out.println(" Business in progress ....");
}

SpringBoot It will open a thread pool by itself , To call ! But to make this annotation work , We also need to add an annotation to the main program @EnableAsync , Turn on the asynchronous annotation function ;

@EnableAsync // Turn on the asynchronous annotation function 
@SpringBootApplication
public class SpringbootTaskApplication {

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

}

7、 Restart test , Web pages respond instantly , The background code is still executing !

16.2、 Timing task

In project development, it is often necessary to perform some scheduled tasks , For example, in the early hours of every day , Analyze the previous day's log information ,Spring It provides us with a way to execute task scheduling asynchronously , Two interfaces are provided .

  • TaskExecutor Interface
  • TaskScheduler Interface

Two notes :

  • @EnableScheduling
  • @Scheduled

cron expression :

   One 、 structure

  corn From left to right ( Space off ): second branch Hours Date in month month Date of the week year

   Two 、 Meaning of each field

Field permitted Special characters allowed
second (Seconds) 0~59 The integer of , - * / Four characters
branch (Minutes) 0~59 The integer of , - * / Four characters
Hours (Hours) 0~23 The integer of , - * / Four characters
date (DayofMonth) 1~31 The integer of ( But you need to think about the number of days in your month ) ,- * ? / L W C Eight characters
month (Month) 1~12 Or JAN-DEC , - * / Four characters
week (DayofWeek) 1~7 Or SUN-SAT (1=SUN) , - * ? / L C # Eight characters
year ( Optional , leave a blank )(Year) 1970~2099 , - * / Four characters

Every field uses numbers , But the following special characters can also appear , They mean :

  (1): Represents any value that matches the field . If be in, Minutes Domain use , It means that the event will be triggered every minute .

  (2)?: Can only be used in DayofMonth and DayofWeek Two domain . It also matches any value of the field , But it doesn't . because DayofMonth and DayofWeek Will affect each other . For example, I want to be in the 20 Daily trigger dispatch , No matter 20 What day is the day of the week , You can only write it as follows : 13 13 15 20 * ?, The last one can only use ?, They can't be used , If you use No matter what day of the week it will trigger , It's not like that .

  (3)-: Scope of representation . For example, in Minutes Domain use 5-20, From 5 be assigned to 20 Trigger every minute

  (4)/: Indicates that the start time starts to trigger , Then trigger every fixed time . For example, in Minutes Domain use 5/20, Means that the 5 Minutes to trigger , and 25,45 Wait for each trigger .

  (5),: Indicates that enumeration values are listed . for example : stay Minutes Domain use 5,20, It means in 5 and 20 Minutes per minute .

  (6)L: Said the last , Can only appear in DayofWeek and DayofMonth Domain . If in DayofWeek Domain use 5L, Means trigger on the last Thursday .

  (7)W: Means effective working day ( Monday to Friday ), Can only appear in DayofMonth Domain , The system will trigger the event... On the most effective working day from the specified date . for example : stay DayofMonth Use 5W, If 5 It's Saturday , On the most recent working day : Friday , namely 4 Day trigger . If 5 Sunday is Sunday , It's in 6 Japan ( Monday ) Trigger ; If 5 A day from Monday to Friday , It's in 5 Day trigger . Another point ,W Our latest search will not cross the month .

  (8)LW: These two characters can be used together , On the last working day of a month , The last Friday .

  (9)#: Used to determine the week ordinal of each month , Can only appear in DayofMonth Domain . For example, in 4#2, The second Wednesday of a month .

   3、 ... and 、 Examples of common expressions

  (1)0 0 2 1 * ? * In the month of 1 Early in the morning 2 Adjust the task

  (2)0 15 10 ? * MON-FRI From Monday to Friday every morning 10:15 Perform operation

  (3)0 15 10 ? 6L 2002-2006 Express 2002-2006 Last Friday morning of each month of the year 10:15 Executive work

  (4)0 0 10,14,16 * * ? Every morning 10 spot , Afternoon 2 spot ,4 spot

  (5)0 0/30 9-17 * * ? Every half hour during 9 to 5 working hours

  (6)0 0 12 ? * WED Every Wednesday at noon 12 spot

  (7)0 0 12 * * ? Every day at noon 12 Some trigger

  (8)0 15 10 ? * * Every morning 10:15 Trigger

  (9)0 15 10 * * ? Every morning 10:15 Trigger

  (10)0 15 10 * * ? * Every morning 10:15 Trigger

  (11)0 15 10 * * ? 2005 2005 Every morning in 1986 10:15 Trigger

  (12)0 * 14 * * ? Every afternoon 2 Point to the afternoon 2:59 During each of the 1 Minutes to trigger

  (13)0 0/5 14 * * ? Every afternoon 2 Point to the afternoon 2:55 During each of the 5 Minutes to trigger

  (14)0 0/5 14,18 * * ? Every afternoon 2 Point to 2:55 During and in the afternoon 6 Point to 6:55 During each of the 5 Minutes to trigger

  (15)0 0-5 14 * * ? Every afternoon 2 Point to the afternoon 2:05 During each of the 1 Minutes to trigger

  (16)0 10,44 14 ? 3 WED Wednesday afternoon in March every year 2:10 and 2:44 Trigger

  (17)0 15 10 ? * MON-FRI Monday to Friday morning 10:15 Trigger

  (18)0 15 10 15 * ? monthly 15 The morning of 10:15 Trigger

  (19)0 15 10 L * ? The morning of the last day of each month 10:15 Trigger

  (20)0 15 10 ? * 6L Last Friday morning of the month 10:15 Trigger

  (21)0 15 10 ? * 6L 2002-2005 2002 - 2005 Last Friday morning of every month in 2006 10:15 Trigger

  (22)0 15 10 ? * 6#3 The third Friday morning of each month 10:15 Trigger

testing procedure :

1、 Create a ScheduledService

There is one in us hello Method , He needs to do it regularly , How to deal with it ?

@Service
public class ScheduledService {
   
   // second     branch     when       Japan     month     What day of the week 
   //0 * * * * MON-FRI
   // Be careful cron Expression usage ;
   @Scheduled(cron = "0 * * * * 0-7")
   public void hello(){
       System.out.println("hello.....");
  }
}

2、 Here, after the timing task is written , We need to add... To the main program @EnableScheduling Turn on timed task function

@EnableAsync // Turn on the asynchronous annotation function 
@EnableScheduling // Enable annotation based scheduled tasks 
@SpringBootApplication
public class SpringbootTaskApplication {

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

}

16.3、 Email tasks

Mail delivery , In our daily development , Also very many ,Springboot Also helped us to do the support

  • Mail delivery needs to introduce spring-boot-start-mail
  • SpringBoot Automatic configuration MailSenderAutoConfiguration
  • Definition MailProperties Content , Configure in application.yml in
  • Automatic assembly JavaMailSender
  • Test mail delivery

test :

1、 introduce pom rely on

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

Look at the dependency it introduces , You can see jakarta.mail

<dependency>
   <groupId>com.sun.mail</groupId>
   <artifactId>jakarta.mail</artifactId>
   <version>1.6.4</version>
   <scope>compile</scope>
</dependency>

2、 See the autoconfiguration class :MailSenderAutoConfiguration

image-20210131153339411

In this class bean,JavaMailSenderImpl

image-20210131153349711

Then let's look at the configuration file

@ConfigurationProperties(
   prefix = "spring.mail"
)
public class MailProperties {
   private static final Charset DEFAULT_CHARSET;
   private String host;
   private Integer port;
   private String username;
   private String password;
   private String protocol = "smtp";
   private Charset defaultEncoding;
   private Map<String, String> properties;
   private String jndiName;
}

3、 The configuration file :

spring.mail.username=24736743@qq.com
spring.mail.password= Yours qq Authorization code 
spring.mail.host=smtp.qq.com
# qq Need configuration ssl
spring.mail.properties.mail.smtp.ssl.enable=true

Get authorization code : stay QQ Settings in the mailbox -> Account -> Turn on pop3 and smtp service

image-20210131153422882

4、Spring unit testing

@Autowired
JavaMailSenderImpl mailSender;

@Test
public void contextLoads() {
   // Email Settings 1: A simple email 
   SimpleMailMessage message = new SimpleMailMessage();
   message.setSubject(" notice - Come to crazy tomorrow ");
   message.setText(" Tonight? 7:30 The meeting ");

   message.setTo("24736743@qq.com");
   message.setFrom("24736743@qq.com");
   mailSender.send(message);
}

@Test
public void contextLoads2() throws MessagingException {
   // Email Settings 2: A complicated email 
   MimeMessage mimeMessage = mailSender.createMimeMessage();
   MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

   helper.setSubject(" notice - Come to crazy tomorrow ");
   helper.setText("<b style='color:red'> today  7:30 Come to the meeting </b>",true);

   // Sending attachments 
   helper.addAttachment("1.jpg",new File(""));
   helper.addAttachment("2.jpg",new File(""));

   helper.setTo("24736743@qq.com");
   helper.setFrom("24736743@qq.com");

   mailSender.send(mimeMessage);
}

Check E-mail , Email received successfully !

We just need to use Thymeleaf You can develop your own website mail sending and receiving function by combining the front and back end !

17、 Rich text editor

reflection : We are usually in the blog Garden , perhaps CSDN When writing on the platform , Have any students thought about how their editor is implemented ?

In the option setting of blog Garden Background , You can see the options for a text editor :

image-20210131153617043

In fact, this is the rich text editor , There are many very mature rich text editors on the market , such as :

  • Editor.md—— A very functional editor , The left editor , Right side preview , Very convenient , Completely free

  • wangEditor—— be based on javascript and css Developed Web Rich text editor , Light weight 、 concise 、 Beautiful interface 、 Easy to use 、 Free open source .

  • TinyMCE——TinyMCE Is a lightweight browser based WYSIWYG editor , from JavaScript It's written in . It's right IE6+ and Firefox1.5+ They all have very good support . The function is all ready , Beautiful interface , The document is in English , There are certain requirements for the English level of developers .

  • Baidu ueditor——UEditor By Baidu web Front end R & D department develops WYSIWYG rich text web Editor , With light weight , The function is all ready , Customizable , Pay attention to user experience and other characteristics , Open source is based on MIT agreement , Allow free use and modification of code , The disadvantage is that there are no updates

  • kindeditor—— The interface is classic .

  • Textbox——Textbox Is a simple but powerful online text editor , Supports desktop and mobile devices . The main functions include built-in image processing and storage 、 File drag and drop 、 Spell check and AutoCorrect . Besides , The tool also implements screen reader and other auxiliary technologies , And accord with WAI-ARIA Accessibility standards .

  • CKEditor—— foreign , Beautiful interface .

  • quill—— Powerful , You can also edit formulas and so on

  • simditor—— Beautiful interface , Functional comparison .

  • summernote——UI good-looking , Exquisite

  • jodit—— The function is all ready

  • froala Editor—— The interface is very nice , Very powerful , Very easy to use ( Non free )

Editor.md

What I use here is Editor.md, As a senior farmer ,Mardown It must be our favorite format , Look below , I fell in love with !

image-20210131153641455

We can download it from the official website :https://pandao.github.io/editor.md/ , Get its compressed package !

After decompression , stay examples Below directory , You can see many of his cases using ! Study , In fact, it depends on how people write , And then just imitate !

We can dump the entire unzipped file into our project , Just delete some useless tests and cases !

17.1、 Basic engineering construction

Database design

article: Article table

id int The only thing in the article ID
author varchar author
title varchar title
content longtext The content of the article

Build table SQL:

CREATE TABLE `article` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'int The only thing in the article ID',
`author` varchar(50) NOT NULL COMMENT ' author ',
`title` varchar(100) NOT NULL COMMENT ' title ',
`content` longtext NOT NULL COMMENT ' The content of the article ',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Basic project construction

1、 To build a SpringBoot Project configuration

spring:
datasource:
  username: root
  password: 123456
  #?serverTimezone=UTC Solve the time zone error 
  url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
  driver-class-name: com.mysql.cj.jdbc.Driver
<resources>
   <resource>
       <directory>src/main/java</directory>
       <includes>
           <include>**/*.xml</include>
       </includes>
       <filtering>true</filtering>
   </resource>
</resources>

2、 Entity class :

// Articles 
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Article implements Serializable {

   private int id; // The only thing in the article ID
   private String author; // The author's name 
   private String title; // title 
   private String content; // The content of the article 

}

3、mapper Interface :

@Mapper
@Repository
public interface ArticleMapper {
   // Check all the articles 
   List<Article> queryArticles();

   // Add an article 
   int addArticle(Article article);

   // According to the article id Search article 
   Article getArticleById(int id);

   // According to the article id Delete articles 
   int deleteArticleById(int id);

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.mapper.ArticleMapper">

   <select id="queryArticles" resultType="Article">
      select * from article
   </select>
   
   <select id="getArticleById" resultType="Article">
      select * from article where id = #{id}
   </select>
   
   <insert id="addArticle" parameterType="Article">
      insert into article (author,title,content) values (#{author},#{title},#{content});
   </insert>
   
   <delete id="deleteArticleById" parameterType="int">
      delete from article where id = #{id}
   </delete>
   
</mapper>

Now that it has been provided myBatis Mapping profile for , Naturally tell spring boot The location of these documents

mybatis:
	mapper-locations: classpath:com/kuang/mapper/*.xml
	type-aliases-package: com.kuang.pojo

Write a Controller Under test , whether ok;

17.2、 Article editing Integration ( a key )

1、 Import editor.md resources , Delete redundant files

2、 Edit the article page editor.html、 Need to introduce jQuery;

<!DOCTYPE html>
<html class="x-admin-sm" lang="zh" xmlns:th="http://www.thymeleaf.org">

<head>
   
   <title> Qinjiang 'Blog</title>
   
   
   
   <!--Editor.md-->
   <link rel="stylesheet" th:href="@{/editormd/css/editormd.css}"/>
   <link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />
</head>

<body>

<div class="layui-fluid">
   <div class="layui-row layui-col-space15">
       <div class="layui-col-md12">
           <!-- Blog forms -->
           <form name="mdEditorForm">
               <div>
                   title :<input type="text" name="title">
               </div>
               <div>
                   author :<input type="text" name="author">
               </div>
               <div id="article-content">
                   <textarea name="content" id="content" style="display:none;"> </textarea>
               </div>
           </form>

       </div>
   </div>
</div>
</body>

<!--editormd-->
<script th:src="@{/editormd/lib/jquery.min.js}"></script>
<script th:src="@{/editormd/editormd.js}"></script>

<script type="text/javascript">
   var testEditor;

   //window.onload = function(){ }
   $(function() {
       testEditor = editormd("article-content", {
           width : "95%",
           height : 400,
           syncScrolling : "single",
           path : "../editormd/lib/",
           saveHTMLToTextarea : true,    //  preservation  HTML  To  Textarea
           emoji: true,
           theme: "dark",// Toolbar theme 
           previewTheme: "dark",// Preview the theme 
           editorTheme: "pastel-on-dark",// Edit the theme 
           tex : true,                   //  Unlock the scientific formula TeX Language support , Off by default 
           flowChart : true,             //  Enable flow chart support , Off by default 
           sequenceDiagram : true,       //  Open the timing / Sequence diagram support , Off by default ,
           // Image upload 
           imageUpload : true,
           imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
           imageUploadURL : "/article/file/upload",
           onload : function() {
               console.log('onload', this);
          },
           /* Specify the function buttons to display */
           toolbarIcons : function() {
               return ["undo","redo","|",
                   "bold","del","italic","quote","ucwords","uppercase","lowercase","|",
                   "h1","h2","h3","h4","h5","h6","|",
                   "list-ul","list-ol","hr","|",
                   "link","reference-link","image","code","preformatted-text",
                   "code-block","table","datetime","emoji","html-entities","pagebreak","|",
                   "goto-line","watch","preview","fullscreen","clear","search","|",
                   "help","info","releaseIcon", "index"]
          },

           /* Custom function button , Now I customize 2 individual , One is release , One is to return to the home page */
           toolbarIconTexts : {
               releaseIcon : "<span bgcolor=\"gray\"> Release </span>",
               index : "<span bgcolor=\"red\"> Back to the home page </span>",
          },

           /* Assign a callback function to a custom button */
           toolbarHandlers:{
               releaseIcon : function(cm, icon, cursor, selection) {
                   // Form submission 
                   mdEditorForm.method = "post";
                   mdEditorForm.action = "/article/addArticle";// Path submitted to server 
                   mdEditorForm.submit();
              },
               index : function(){
                   window.location.href = '/';
              },
          }
      });
  });
</script>

</html>

3、 To write Controller, To jump , And save articles

@Controller
@RequestMapping("/article")
public class ArticleController {

   @GetMapping("/toEditor")
   public String toEditor(){
       return "editor";
  }
   
   @PostMapping("/addArticle")
   public String addArticle(Article article){
       articleMapper.addArticle(article);
       return "editor";
  }
   
}

Picture upload problem

1、 front end js Add configuration in

// Image upload 
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "/article/file/upload", // // This is the access address when uploading the picture 

2、 Back end request , Receive and save this picture , Import required FastJson Dependence !

// Blog image upload problem 
@RequestMapping("/file/upload")
@ResponseBody
public JSONObject fileUpload(@RequestParam(value = "editormd-image-file", required = true) MultipartFile file, HttpServletRequest request) throws IOException {
   // Upload path save settings 

   // get SpringBoot The path to the current project :System.getProperty("user.dir")
   String path = System.getProperty("user.dir")+"/upload/";

   // Sort by month :
   Calendar instance = Calendar.getInstance();
   String month = (instance.get(Calendar.MONTH) + 1)+" month ";
   path = path+month;

   File realPath = new File(path);
   if (!realPath.exists()){
       realPath.mkdir();
  }

   // Upload file address 
   System.out.println(" Upload file save address :"+realPath);

   // Solve the file name problem : We use uuid;
   String filename = "ks-"+UUID.randomUUID().toString().replaceAll("-", "");
   // adopt CommonsMultipartFile Write the file directly ( Pay attention to this time )
   file.transferTo(new File(realPath +"/"+ filename));

   // to editormd Make a callback 
   JSONObject res = new JSONObject();
   res.put("url","/upload/"+month+"/"+ filename);
   res.put("success", 1);
   res.put("message", "upload success!");

   return res;
}

3、 Solve the problem of file echo display , Set up virtual directory mapping ! In our own expansion MvcConfig You can configure it in !

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

   //  The files are kept in a real Directory /upload/ Next ,
   //  Use virtual paths when accessing /upload, For example, the file name is 1.png, Just directly /upload/1.png Just ok 了 .
   @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
       registry.addResourceHandler("/upload/**")
          .addResourceLocations("file:"+System.getProperty("user.dir")+"/upload/");
  }

}

The expression pack problem

Download by yourself ,emoji emoticon , Put it under the picture path :

modify editormd.js file

// Emoji graphics files url path
editormd.emoji     = {
   path : "../editormd/plugins/emoji-dialog/emoji/",
   ext   : ".png"
};

17.3、 Article display

1、Controller Add method in

@GetMapping("/{id}")
public String show(@PathVariable("id") int id,Model model){
   Article article = articleMapper.getArticleById(id);
   model.addAttribute("article",article);
   return "article";
}

2、 Write page article.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
   
   
   <title th:text="${article.title}"></title>
</head>
<body>

<div>
   <!-- The header information of the article : title , author , Last update date , Navigation -->
   <h2 style="margin: auto 0" th:text="${article.title}"></h2>
   author :<span style="float: left" th:text="${article.author}"></span>
   <!-- The main content of the article -->
   <div id="doc-content">
       <textarea style="display:none;" placeholder="markdown" th:text="${article.content}"></textarea>
   </div>

</div>

<link rel="stylesheet" th:href="@{/editormd/css/editormd.preview.css}" />
<script th:src="@{/editormd/lib/jquery.min.js}"></script>
<script th:src="@{/editormd/lib/marked.min.js}"></script>
<script th:src="@{/editormd/lib/prettify.min.js}"></script>
<script th:src="@{/editormd/lib/raphael.min.js}"></script>
<script th:src="@{/editormd/lib/underscore.min.js}"></script>
<script th:src="@{/editormd/lib/sequence-diagram.min.js}"></script>
<script th:src="@{/editormd/lib/flowchart.min.js}"></script>
<script th:src="@{/editormd/lib/jquery.flowchart.min.js}"></script>
<script th:src="@{/editormd/editormd.js}"></script>

<script type="text/javascript">
   var testEditor;
   $(function () {
       testEditor = editormd.markdownToHTML("doc-content", {// Be careful : Here is the top DIV Of id
           htmlDecode: "style,script,iframe",
           emoji: true,
           taskList: true,
           tocm: true,
           tex: true, //  Default not resolved 
           flowChart: true, //  Default not resolved 
           sequenceDiagram: true, //  Default not resolved 
           codeFold: true
      });});
</script>
</body>
</html>

18、Dubbo and Zookeeper Integrate

18.1、 Distributed theory

What is a distributed system ?

stay 《 Principle and model of distributed system 》 There are the following definitions in this book :“ A distributed system is a collection of independent computers , These computers are like a single related system to users ”;

Distributed system is a group of communication through the network 、 A system of computer nodes that coordinate work to accomplish common tasks . Distributed systems have emerged to use cheap 、 Ordinary machines do calculations that a single computer can't do 、 Storage tasks . Its purpose is to Use more machines , Processing more data .

Distributed systems (distributed system) It's a software system based on the network .

The first thing to be clear is , Only when the processing power of a single node can not meet the growing Computing 、 When storing tasks , And the improvement of hardware ( Add memory 、 Add disk 、 Use a better CPU) When it's too high to pay , When the application can't be further optimized , We need to think about distributed systems . because , The problem of distributed system is the same as that of stand-alone system , And because distributed systems have multiple nodes 、 The topology of communication over a network , There will be a lot of problems that single machine systems don't have , In order to solve these problems, more mechanisms will be introduced 、 agreement , Bring more problems ...

Dubbo file

With the development of the Internet , The scale of web applications continues to grow , Conventional vertical application architectures can no longer cope , Distributed service architecture and flow computing architecture are imperative , urgent need A governance system Make sure the architecture evolves in an orderly way .

stay Dubbo There is such a picture in the official website of

image-20210131154452190

Single application architecture

When website traffic is low , Just one app , Deploy all functions together , To reduce deployment nodes and costs . here , Data access framework to simplify the work of adding, deleting, modifying and querying (ORM) Is the key .

image-20210131154534777

For small websites , Small management system , Deploy all functions into one function , Simple and easy to use .

shortcoming :

1、 Performance scaling is difficult

2、 Collaborative development issues

3、 Not conducive to upgrade maintenance

Vertical application architecture

When the number of visits increases gradually , Single application increases the acceleration brought by the machine is getting smaller and smaller , Split the application into several unrelated applications , To improve efficiency . here , For accelerating front-end page development Web frame (MVC) Is the key .

image-20210131154625157

The independent deployment of each module can be realized through business segmentation , Reduced the difficulty of maintenance and deployment , It's easier to manage a team , Performance scaling is also more convenient , More targeted .

shortcoming : Common modules cannot be reused , Development waste

Distributed service architecture

As more and more vertical applications , Interaction between applications is inevitable , Extract core business , As an independent service , Gradually form a stable service center , Enable front-end applications to respond more quickly to changing market demands . here , To improve business reuse and integration Distributed service framework (RPC) Is the key .

image-20210131154702291

Flow computing architecture

When the service is more and more , Capacity assessment , Problems such as waste of small service resources are gradually emerging , At this time, it is necessary to add a dispatching center to manage the cluster capacity in real time based on the access pressure , Improve cluster utilization . here , be used for Resource scheduling and Governance Center to improve machine utilization (SOA)[ Service Oriented Architecture] Is the key .

image-20210131154730305

18.2、 What is? RPC

RPC【Remote Procedure Call】 Remote procedure call , It is a way of inter process communication , He is a technical thought , Not the norm . It allows the program to call another address space ( Usually on another machine that shares the network ) A process or function of , Instead of programmers explicitly coding the details of this remote call . That is, whether the programmer calls local or remote functions , Essentially, the call code written is basically the same .

That is to say, two servers A,B, An application is deployed in A Server , Want to call B Functions provided by the application on the server / Method , Because it's not in a memory space , Can't call directly , We need to express the semantics and data of the call through the network . Why use RPC Well ? Just can't be in a process , Even a computer through local calls to complete the requirements , For example, communication between different systems , Even communication between different organizations , Because computing power needs to scale out , You need to deploy applications on a cluster of multiple machines .RPC It is to call remote functions just like calling local functions ;

Recommended reading articles :https://www.jianshu.com/p/2accc2840a1b

RPC The basic principle

image-20210131154800930

Steps to resolve :

image-20210131154814945

RPC Two core modules : Communications , serialize .

18.3、 Test environment construction

Dubbo

Apache Dubbo |ˈdʌbəʊ| It's a high performance 、 Lightweight open source Java RPC frame , It provides three core competencies : Interface oriented remote method call , Intelligent fault tolerance and load balancing , And automatic service registration and discovery .

dubbo Official website http://dubbo.apache.org/zh-cn/index.html

1. understand Dubbo Characteristics of

2. Check out the official documents

dubbo Basic concepts

image-20210131154854320

Service providers (Provider): Service providers that expose services , The service provider at startup , Register your services with the registry .

Serving consumers (Consumer): The service consumer that invokes the remote service , Service consumers at startup , Subscribe to the registry for the services you need , Serving consumers , From the provider address list , Based on soft load balancing algorithm , Select a provider to invoke , If the call fails , Select another call .

Registry Center (Registry): The registry returns a list of service provider addresses to the consumer , If there are changes , The registry pushes the change data to the consumer based on the long connection

The monitoring center (Monitor): Service consumers and providers , Accumulates the number of calls and the call time in memory , Send statistics to the monitoring center every minute on a regular basis

Call relation description

l The service container is responsible for starting , load , Run the service provider .

l The service provider at startup , Register your services with the registry .

l Service consumers at startup , Subscribe to the registry for the services you need .

l The registry returns a list of service provider addresses to the consumer , If there are changes , The registry pushes the change data to the consumer based on the long connection .

l Serving consumers , From the provider address list , Based on soft load balancing algorithm , Select a provider to invoke , If the call fails , Select another call .

l Service consumers and providers , Accumulates the number of calls and the call time in memory , Send statistics to the monitoring center every minute on a regular basis .

Dubbo Environment building

Click in dubbo Official documents , It is recommended that we use Zookeeper Registry Center

What is? zookeeper Well ? You can view the official documentation

Window Lower installation zookeeper

1、 download zookeeper : Address , We download 3.4.14 , The latest version ! decompression zookeeper

2、 function /bin/zkServer.cmd , The initial operation will report an error , No, zoo.cfg The configuration file ;

There may be problems : Flash back !

Solution : edit zkServer.cmd Add at the end of the file pause . In this way, it will not exit if it runs wrong , Error messages will be prompted , It's easy to find out why .

image-20210131154948865

image-20210131154958848

3、 modify zoo.cfg The configuration file

take conf Under the folder zoo_sample.cfg Make a copy and change its name to zoo.cfg that will do .

Pay attention to a few important places :

dataDir=./ Directory of temporary data storage ( Write relative path )

clientPort=2181 zookeeper Port number

Start again after modification zookeeper

image-20210131155010286

4、 Use zkCli.cmd test

ls /: List zookeeper All nodes saved under the root

[zk: 127.0.0.1:2181(CONNECTED) 4] ls /
[zookeeper]

create –e /kuangshen 123: Create a kuangshen node , The value is 123

image-20210131155038849

get /kuangshen: obtain /kuangshen The value of the node

image-20210131155048416

Let's take a look at the nodes

image-20210131155059471

window Lower installation dubbo-admin

dubbo It's not a service software in itself . It's actually a jar package , Can help you java The program connects to zookeeper, And make use of zookeeper consumption 、 Provide services .

But in order to let users better manage and monitor numerous dubbo service , The official provides a visual monitoring program dubbo-admin, But even if this monitoring is not installed, it will not affect the use of .

Let's install it here :

1、 download dubbo-admin

Address :https://github.com/apache/dubbo-admin/tree/master

2、 Unzip into directory

modify dubbo-admin\src\main\resources \application.properties Appoint zookeeper Address

server.port=7001
spring.velocity.cache=false
spring.velocity.charset=UTF-8
spring.velocity.layout-url=/templates/default.vm
spring.messages.fallback-to-system-locale=false
spring.messages.basename=i18n/message
spring.root.password=root
spring.guest.password=guest

dubbo.registry.address=zookeeper://127.0.0.1:2181

3、 Under the project directory pack dubbo-admin

mvn clean package -Dmaven.test.skip=true

The first packaging was a bit slow , It takes patience ! Until success !

image-20210131155259145

4、 perform dubbo-admin\target Under the dubbo-admin-0.0.1-SNAPSHOT.jar

java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

【 Be careful :zookeeper Your service must be turned on !】

completion of enforcement , Let's visit http://localhost:7001/ , At this time, we need to enter the login account and password , We are all by default root-root;

After successful login , View interface

image-20210131155319250

installation is complete !

18.4、SpringBoot + Dubbo + zookeeper

Frame building

1. start-up zookeeper !

2. IDEA Create an empty project ;

3. Create a module , Implementing service providers :provider-server , choice web Rely on it

4. Project creation completed , Let's write a service , For example, ticketing services ;

Writing interface

package com.kuang.provider.service;

public interface TicketService {
   public String getTicket();
}

Write the implementation class

package com.kuang.provider.service;

public class TicketServiceImpl implements TicketService {
   @Override
   public String getTicket() {
       return "《 Madness theory Java》";
  }
}

5. Create a module , Achieve service consumers :consumer-server , choice web Rely on it

6. Project creation completed , Let's write a service , Like user services ;

To write service

package com.kuang.consumer.service;

public class UserService {
   // We need to go to the registry service 
}

demand : Now our users want to use the ticketing service , How to do this ?

Service providers

1、 Register the service provider with the registry , We need to integrate Dubbo and zookeeper, So we need to guide the package

We from dubbo Official website access github, Look at the help document below , find dubbo-springboot, Find the dependency package

<!-- Dubbo Spring Boot Starter -->
<dependency>
   <groupId>org.apache.dubbo</groupId>
   <artifactId>dubbo-spring-boot-starter</artifactId>
   <version>2.7.3</version>
</dependency>    

zookeeper We're going to maven Warehouse downloading ,zkclient;

<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
   <groupId>com.github.sgroschupf</groupId>
   <artifactId>zkclient</artifactId>
   <version>0.1</version>
</dependency>

【 The new version of the pit 】zookeeper And its dependent packages , Resolve log conflicts , You also need to eliminate log dependencies ;

<!--  introduce zookeeper -->
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-framework</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-recipes</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.14</version>
   <!-- Exclude this slf4j-log4j12-->
   <exclusions>
       <exclusion>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
       </exclusion>
   </exclusions>
</dependency>

2、 stay springboot Configuration in profile dubbo Related properties !

# Current app name 
dubbo.application.name=provider-server
# Address of Registration Center 
dubbo.registry.address=zookeeper://127.0.0.1:2181
# Scan the services under the specified package 
dubbo.scan.base-packages=com.kuang.provider.service

3、 stay service Configure service annotations in the implementation class of , Publishing services ! Pay attention to the problem of leading package

import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

@Service // Publish the service 
@Component // Put in container 
public class TicketServiceImpl implements TicketService {
   @Override
   public String getTicket() {
       return "《 Madness theory Java》";
  }
}

Logical understanding : The app starts ,dubbo Will scan the specified package with @component Annotated Services , Publish it in the designated registry !

Serving consumers

1、 Import dependence , Just like before ;

<!--dubbo-->
<!-- Dubbo Spring Boot Starter -->
<dependency>
   <groupId>org.apache.dubbo</groupId>
   <artifactId>dubbo-spring-boot-starter</artifactId>
   <version>2.7.3</version>
</dependency>
<!--zookeeper-->
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
   <groupId>com.github.sgroschupf</groupId>
   <artifactId>zkclient</artifactId>
   <version>0.1</version>
</dependency>
<!--  introduce zookeeper -->
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-framework</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-recipes</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.14</version>
   <!-- Exclude this slf4j-log4j12-->
   <exclusions>
       <exclusion>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
       </exclusion>
   </exclusions>
</dependency>

2、 Configuration parameters

# Current app name 
dubbo.application.name=consumer-server
# Address of Registration Center 
dubbo.registry.address=zookeeper://127.0.0.1:2181

3. The normal step is to package the interface of the service provider , And then use pom File import , We use a simple way here , Bring the interface of the service directly , The path must be right , It's the same as a service provider ;

image-20210131155635814

4. Improve consumer services

package com.kuang.consumer.service;

import com.kuang.provider.service.TicketService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;

@Service // Pour into the container 
public class UserService {

   @Reference // Remote reference to the specified service , He will match by the full class name , Let's see who registered the full class name for the registry 
   TicketService ticketService;

   public void bugTicket(){
       String ticket = ticketService.getTicket();
       System.out.println(" At the registry "+ticket);
  }

}

5. Test class writing

@RunWith(SpringRunner.class)
@SpringBootTest
public class ConsumerServerApplicationTests {

   @Autowired
   UserService userService;

   @Test
   public void contextLoads() {

       userService.bugTicket();

  }

}

Start the test

1. Turn on zookeeper

2. open dubbo-admin Implementation monitoring 【 You don't have to do it 】

3. Turn on the server

4. Consumer testing , result :

image-20210131155739425

The monitoring center :

image-20210131155750129

ok , This is it. SpingBoot + dubbo + zookeeper Realize the application of distributed development , In fact, it is the idea of service splitting ;

19、 Integrate SpringSecurity

Introduction to safety

stay Web In development , Security has always been a very important aspect . Security is a non functional requirement of an application , But it should be considered in the early stage of application development . If we only consider the issue of security at the later stage of application development , You could be in a dilemma : One side , There are serious security vulnerabilities in the application , Unable to meet the user's requirements , And may cause the user's privacy data to be stolen by the attacker ; On the other hand , The basic architecture of the application has been determined , To fix security holes , It may be necessary to make major adjustments to the architecture of the system , So more development time is needed , Affect the release process of the application . therefore , From the first day of application development, security related factors should be taken into consideration , And in the whole application development process .

There are some famous ones on the market :Shiro,Spring Security !

What needs to be explained here is , Every framework comes into being to solve a problem , that Spring Security What is the purpose of the framework ?

First of all, let's take a look at its official website :Spring Security Official website address

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

Spring Security Is a powerful and highly customizable authentication and access control framework . It's actually protection based on spring The standard of the application .

Spring Security It's a framework , Focus on Java Applications provide authentication and Authorization . With all the Spring Same project ,Spring The real power of security is that it can be easily extended to meet custom requirements

From the introduction of the official website, we can see that this is a permission framework . Before we did the project, we didn't use the framework to control the permissions ? For permissions It is generally subdivided into function permissions , Access right , And menu permissions . The code will be very cumbersome , redundancy .

How to solve the cumbersome problem of writing permission code before , The problem of redundancy , Some mainstream frameworks came into being and Spring Scecurity It's one of them .

Spring It's a very popular and successful Java Application development framework .Spring Security be based on Spring frame , A set of Web A complete solution for application security . Generally speaking ,Web Application security includes user authentication (Authentication) And user authorization (Authorization) Two parts . User authentication is to verify whether a user is a legal subject in the system , That is, whether users can access the system . User authentication generally requires the user to provide a user name and password . The system verifies the user name and password to complete the authentication process . User authorization refers to verifying whether a user has permission to perform an operation . In a system , Different users have different permissions . For example, for a file , Some users can only read , And some users can modify . Generally speaking , The system will assign different roles to different users , Each role corresponds to a series of permissions .

For the two scenarios mentioned above ,Spring Security Frameworks are well supported . In terms of user authentication ,Spring Security The framework supports mainstream authentication methods , Include HTTP Basic authentication 、HTTP Form validation 、HTTP Abstract authentication 、OpenID and LDAP etc. . In terms of user authorization ,Spring Security Provides role based access control and access control list (Access Control List,ACL), Fine grained control of domain objects in applications .

19.1、 Actual test

Construction of experimental environment

1、 Create an initial springboot project web modular ,thymeleaf modular

2、 Import static resources

welcome.html
|views
    |level1
        1.html
        2.html
        3.html
    |level2
        1.html
        2.html
        3.html
    |level3
        1.html
        2.html
        3.html
Login.html

3、controller Jump !

package com.kuang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RouterController {

   @RequestMapping({"/","/index"})
   public String index(){
       return "index";
  }

   @RequestMapping("/toLogin")
   public String toLogin(){
       return "views/login";
  }

   @RequestMapping("/level1/{id}")
   public String level1(@PathVariable("id") int id){
       return "views/level1/"+id;
  }

   @RequestMapping("/level2/{id}")
   public String level2(@PathVariable("id") int id){
       return "views/level2/"+id;
  }

   @RequestMapping("/level3/{id}")
   public String level3(@PathVariable("id") int id){
       return "views/level3/"+id;
  }

}

4、 Test whether the experimental environment is OK!

19.2、 know SpringSecurity

Spring Security Is aimed at Spring Project security framework , It's also Spring Boot The default technology selection of the underlying security module , He can achieve powerful Web safety control , For security control , We just need to introduce spring-boot-starter-security modular , Make a small amount of configuration , To achieve strong security management !

Remember a few classes :

  • WebSecurityConfigurerAdapter: Customize Security Strategy
  • AuthenticationManagerBuilder: Custom authentication policy
  • @EnableWebSecurity: Turn on WebSecurity Pattern

Spring Security The two main goals of are “ authentication ” and “ to grant authorization ”( Access control ).

“ authentication ”(Authentication)

Authentication is about verifying your credentials , Such as user name / user ID And password , To verify your identity .

Authentication is usually done with a user name and password , Sometimes used in combination with authentication factors .

“ to grant authorization ” (Authorization)

Authorization occurs after the system has successfully verified your identity , Finally, you will be granted access to resources ( Such information , file , database , Money , Location , Almost anything ) Full authority of .

The concept is universal , Not just in Spring Security in .

19.3、 Certification and authorization

at present , Our test environment , It's accessible to anyone , We use Spring Security Add the function of authentication and Authorization

1、 introduce Spring Security modular

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

2、 To write Spring Security Configuration class

Refer to the official website :https://spring.io/projects/spring-security

Look at the versions in our own projects , Find the corresponding help document :

https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5 #servlet-applications 8.16.4

image-20210131160114321

3、 Write basic configuration class

package com.kuang.config;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity //  Turn on WebSecurity Pattern 
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       
  }
}

4、 Custom request authorization rules

@Override
protected void configure(HttpSecurity http) throws Exception {
   //  Custom request authorization rules 
   //  The home page is accessible to everyone 
   http.authorizeRequests().antMatchers("/").permitAll()
  .antMatchers("/level1/**").hasRole("vip1")
  .antMatchers("/level2/**").hasRole("vip2")
  .antMatchers("/level3/**").hasRole("vip3");
}

5、 Test it : I found I couldn't get in except the front page ! Because we don't currently have a login role , Because the request requires the login role to have the corresponding permissions !

6、 stay configure() Method , Turn on the auto configured login function !

//  Turn on the auto configured login function 
// /login  The request comes to the landing page 
// /login?error  Redirection here means login failed 
http.formLogin();

7、 Test it : Find out , When you don't have authority , Will jump to the login page !

image-20210131160200069

8、 Check the comments on the login page just now ;

We can define authentication rules , rewrite configure(AuthenticationManagerBuilder auth) Method

// Define authentication rules 
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
   // Define in memory , It can also be in jdbc Go to get ....
   auth.inMemoryAuthentication()
          .withUser("kuangshen").password("123456").roles("vip2","vip3")
          .and()
          .withUser("root").password("123456").roles("vip1","vip2","vip3")
          .and()
          .withUser("guest").password("123456").roles("vip1","vip2");
}

9、 test , We can use these accounts to log in and test ! If you find out, you will report an error !

There is no PasswordEncoder mapped for the id “null”

image-20210131160228064

10、 reason , We need to encrypt the password from the front end in some way , Otherwise, you can't log in , Modify the code

// Define authentication rules 
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   // Define in memory , It can also be in jdbc Go to get ....
   //Spring security 5.0 A variety of encryption methods have been added , Also changed the format of the password .
   // If you want our project to be able to log in normally , It needs to be revised configure The code in . We need to encrypt the password from the front end in some way 
   //spring security  The official recommendation is to use bcrypt encryption .
   
   auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
          .withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
          .and()
          .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
          .and()
          .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
}

11、 test , Find out , Login successful , And each role can only access the rules under its own authentication ! Get it done

19.4、 Access control and logout

1、 Enable auto configured logout

// Custom request authorization rules 
@Override
protected void configure(HttpSecurity http) throws Exception {
   //....
   // Enable auto configured logout 
      // /logout  Cancellation request 
   http.logout();
}

2、 We're at the front end , Add a logout button ,index.html In the navigation bar

<a class="item" th:href="@{/logout}">
   <i class="address card icon"></i>  Cancellation 
</a>

3、 We can test it , After successful login, click logout , After logging out, you will jump to the login page !

4、 however , We want him to log off and , You can still jump to the home page , How to deal with it ?

// .logoutSuccessUrl("/");  Log off successfully and come to the home page 
http.logout().logoutSuccessUrl("/");

5、 test , After logging out , Find jump to the home page OK

6、 Now we have another demand : When the user is not logged in , Only the login button is displayed on the navigation bar , After the user logs in , The navigation bar can display the login user information and the logout button ! And that is , such as kuangshen This user , It's just vip2,vip3 function , Then login only displays these two functions , and vip1 The function menu of is not displayed ! This is the real situation of the website ! How to do it ?

We need to combine thymeleaf Some functions in

sec:authorize="isAuthenticated()": Whether to authenticate login ! To display different pages

Maven rely on :

<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
   <groupId>org.thymeleaf.extras</groupId>
   <artifactId>thymeleaf-extras-springsecurity5</artifactId>
   <version>3.0.4.RELEASE</version>
</dependency>

7、 Modify our Front page

  1. Import namespace

  2. xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
    

Modify navigation bar , Add authentication judgment

<!-- Log in and log out -->
<div class="right menu">

   <!-- If you are not logged in -->
   <div sec:authorize="!isAuthenticated()">
       <a class="item" th:href="@{/login}">
           <i class="address card icon"></i>  Sign in 
       </a>
   </div>

   <!-- If you are logged in -->
   <div sec:authorize="isAuthenticated()">
       <a class="item">
           <i class="address card icon"></i>
           user name :<span sec:authentication="principal.username"></span>
           role :<span sec:authentication="principal.authorities"></span>
       </a>
   </div>

   <div sec:authorize="isAuthenticated()">
       <a class="item" th:href="@{/logout}">
           <i class="address card icon"></i>  Cancellation 
       </a>
   </div>
</div>

8、 Restart test , We can log in and try , After successful login, it is true that , It shows the page we want ;

9、 If you log out 404 了 , It's because it prevents... By default csrf Cross-site request forgery , Because there will be security problems , We can change the request to post Form submission , Or in spring security Closed in csrf function ; Let's try : stay Add... To the configuration http.csrf().disable();

http.csrf().disable();// close csrf function : Cross-site request forgery , Default can only be done by post Mode submission logout request 
http.logout().logoutSuccessUrl("/");

10、 Let's continue to complete the following role function block Authentication !

<!-- sec:authorize="hasRole('vip1')" -->
<div class="column" sec:authorize="hasRole('vip1')">
   <div class="ui raised segment">
       <div class="ui">
           <div class="content">
               <h5 class="content">Level 1</h5>
               <hr>
               <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
               <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
               <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
           </div>
       </div>
   </div>
</div>

<div class="column" sec:authorize="hasRole('vip2')">
   <div class="ui raised segment">
       <div class="ui">
           <div class="content">
               <h5 class="content">Level 2</h5>
               <hr>
               <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
               <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
               <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
           </div>
       </div>
   </div>
</div>

<div class="column" sec:authorize="hasRole('vip3')">
   <div class="ui raised segment">
       <div class="ui">
           <div class="content">
               <h5 class="content">Level 3</h5>
               <hr>
               <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
               <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
               <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
           </div>
       </div>
   </div>
</div>

11、 Test it !

12、 Access control and logout !

19.5、 Remember me

The present situation , We just log in and , Close the browser , Login again , We'll log back in , But a lot of websites , It has a function of remembering passwords , How to achieve this ? It's simple

1、 Turn on remember me

// Custom request authorization rules 
@Override
protected void configure(HttpSecurity http) throws Exception {
//...........
   // Remember me 
   http.rememberMe();
}

2、 Let's start the project again and test it , I found that the login page has one more function to remember me , We log in and close browser , Then open the browser again , Found that users still exist !

reflection : How to achieve it ? It's very simple

We can see the browser's cookie

image-20210131160552278

3、 When we click logout , You can find ,spring security Help us automatically delete this cookie

image-20210131160603120

4、 Conclusion : After successful login , take cookie Send to browser to save , Log in later and bring this cookie, As long as you pass the check, you can log in free . If you click logout , Will delete this cookie, The concrete principle is that we are JavaWeb I've talked about all the stages , I won't talk about it here !

19.6、 Customize the landing page

Now this login page is spring security default , How can we use our own Login Interface ?

1、 After the configuration of the login page, specify loginpage

http.formLogin().loginPage("/toLogin");

2、 Then the front end also needs to point to our own definition of login request

<a class="item" th:href="@{/toLogin}">
   <i class="address card icon"></i>  Sign in 
</a>

3、 We log in , Where does this information need to be sent , We also need to configure ,login.html Configure the submission request and method , The way must be post:

stay loginPage() The comments in the source code indicate that :

image-20210131160707285

<form th:action="@{/login}" method="post">
   <div class="field">
       <label>Username</label>
       <div class="ui left icon input">
           <input type="text" placeholder="Username" name="username">
           <i class="user icon"></i>
       </div>
   </div>
   <div class="field">
       <label>Password</label>
       <div class="ui left icon input">
           <input type="password" name="password">
           <i class="lock icon"></i>
       </div>
   </div>
   <input type="submit" class="ui blue submit button"/>
</form>

4、 This request is submitted , We also need validation processing , How to do it? ? We can see formLogin() Method source code ! We configure the parameters to receive the login user name and password !

http.formLogin()
  .usernameParameter("username")
  .passwordParameter("password")
  .loginPage("/toLogin")
  .loginProcessingUrl("/login"); //  Login form submit request 

5、 Add the remember me box to the login page

<input type="checkbox" name="remember">  Remember me 

6、 Back end validation processing !

// Custom remember my parameters !
http.rememberMe().rememberMeParameter("remember");

7、 test ,OK

19.7、 Complete configuration code

package com.kuang.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   // Custom request authorization rules 
   @Override
   protected void configure(HttpSecurity http) throws Exception {

       http.authorizeRequests().antMatchers("/").permitAll()
      .antMatchers("/level1/**").hasRole("vip1")
      .antMatchers("/level2/**").hasRole("vip2")
      .antMatchers("/level3/**").hasRole("vip3");


       // Turn on the auto configured login function : If you don't have permission , Will jump to the login page !
           // /login  The request comes to the landing page 
           // /login?error  Redirection here means login failed 
       http.formLogin()
          .usernameParameter("username")
          .passwordParameter("password")
          .loginPage("/toLogin")
          .loginProcessingUrl("/login"); //  Login form submit request 

       // Enable auto configured logout 
           // /logout  Cancellation request 
           // .logoutSuccessUrl("/");  Log off successfully and come to the home page 

       http.csrf().disable();// close csrf function : Cross-site request forgery , Default can only be done by post Mode submission logout request 
       http.logout().logoutSuccessUrl("/");

       // Remember me 
       http.rememberMe().rememberMeParameter("remember");
  }

   // Define authentication rules 
   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       // Define in memory , It can also be in jdbc Go to get ....
       //Spring security 5.0 A variety of encryption methods have been added , Also changed the format of the password .
       // If you want our project to be able to log in normally , It needs to be revised configure The code in . We need to encrypt the password from the front end in some way 
       //spring security  The official recommendation is to use bcrypt encryption .

       auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
              .withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
              .and()
              .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
              .and()
              .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
  }
}

版权声明
本文为[ANTIA11]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/01/20210131162055338g.html

Scroll to Top