[Microservice Development 1] Spring Boot Detailed Explanation

1. Detailed explanation of SpringBoot

1. Initial Spring Boot

SpringBoot is a javaweb development framework

With the core idea of ​​convention being greater than configuration, SpringBoot helps us make many settings by default, and most SpringBoot applications only need a small amount of Spring configuration. It integrates a large number of third-party library configurations

2. The first SpringBoot program

  • You can create a project on https://start.spring.io/official website
  • Create projects directly in idea (recommended)
    • New project Spring Initializr.
    • Spring Boot 2.7.7 (Spring Boot 3.0 uses Java 17 as minimum version)
    • SDK version 1.8
    • java version 8
  • Create a controller at the same level as the project main program XXXApplication.java

Change the project port number:

​ Change server.port=8081 in the application.properties configuration file

Change the banner:

​ In the resources directory, create banner.txt, the content of which is the banner to be displayed

3. The principle of automatic assembly

3.1 pom.xml

  • The spring-boot-dependencies core dependency is in the parent project
  • When we introduce springboot dependencies, we don't need to specify the version, because the corresponding version number is configured in the pom of the parent project

3.2 Launcher

  • <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    
  • The starter is the startup scene of springboot

  • For example, spring-boot-starter-web helps us automatically import all dependencies in the web environment

  • springboot will turn all functional scenarios into starters

  • Starter https://docs.spring.io/spring-boot/docs/2.7.7/reference/html/using.html#using

3.3 Main program

//This annotation is required
@SpringBootApplication
public class SpringBoot01Application {
    
    public static void main(String[] args) {
        //Use this statement to start the springboot project
        SpringApplication.run(SpringBoot01Application.class, args);
    }

}

annotation:

@SpringBootApplication
	@SpringBootConfiguration//configuration of springboot
		@Configuration//spring configuration class
			@Component//Description is a component
	@EnableAutoConfiguration//automatic configuration
		@AutoConfigurationPackage//auto-configuration package
			@Import({Registrar.class})//Import, automatically configure the internal class Registrar of this class
		@Import({AutoConfigurationImportSelector.class})//import, auto-configuration imports selector classes

The core configuration file for auto-configuration:

​ META-INF/spring.factories file under the jar package of spring-boot-autoconfigure-2.7.7 (deprecated in version 2.7)

SpringApplication does four things:

  • Infer whether the type of application is a normal project or a web project
  • Find and load all available initializers and set them in the initializers property
  • Find all application listeners and set them to the listeners property
  • Infer and set the definition class of the main method, and find the running main class

4. Yaml explanation

The global configuration file of springboot

  • application.properties
    • key=value
  • application.yaml
    • key: space value
server:
  port: 8080
#Ordinary key-value
name: lisa
#object
student:
  name: lisa
  age: 3
#object inline
student1: {name: lisa,age: 3}
#array
pets:
  - cat
  - dog
  - pig
#Array inline writing
pets1: [cat,dog,pig]

yaml can assign values ​​to entity classes

Dog.java

@Component
public class Dog {
    private String name;
    private int age;
}

Person.java

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

application.yaml

person:
  name: lisa${random.uuid}
  age: 32
  happy: false
  birth: 2013/2/3
  maps: {k1: v1,k2: v2}
  lists:
    - code
    - music
    - read
  dog:
    name: Awang
    age: 3

test class

@SpringBootTest
class Springboot02ConfigApplicationTests {
    @Autowired
    private Person person;
    @Test
    void contextLoads() {
        System.out.println(person);
    }

}

5. Configuration file location

Configuration file reference configuration address: https://docs.spring.io/spring-boot/docs/2.7.7/reference/html/application-properties.html#appendix.application-properties

Where to place configuration files (in order of priority):

  • file:./config/ (under the config folder under the project root directory)
  • file:./ (under the project root directory)
  • classpath:/config/ (under the config folder under the resources directory)
  • classpath:/ (under the resources directory)

Mainly pay attention to XXXAutoConfiguration and XXXProperties, two kinds of files, refer to and configure in the configuration file

6. Spring Boot Web Development

6.1 Static resource import

source code:

public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //If there is a custom configuration, the default path will fail
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
    } else {
        this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
        this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
            registration.addResourceLocations(this.resourceProperties.getStaticLocations());
            if (this.servletContext != null) {
                ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
                registration.addResourceLocations(new Resource[]{resource});
            }

        });
    }
}

Import method one (not recommended):

​ Use webjars

​ classpath:/META-INF/resources/

​ What is webjars: It means to introduce the jar package of the web in the form of maven, and the address /webjars/ is mapped to /META-INF/resources/webjars/ when accessing

Import method 2: (the static file 1.js is placed in the following directory, which can be accessed directly at http://localhost:8080/1.js)

classpath is the resources in the root directory of the project

  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/

6.2 Home page and icon customization

index.html in the static resource directory will be regarded as the home page

Pages under templates can only be accessed through the controller

  • When using files under templates, you need to use thymeleaf dependencies, otherwise an error will be reported

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

Icons are placed under the static folder

Introduce <link rel="icon" href="favicon.ico"> to web pages that require icons

6.3 thymeleaf template engine

Documentation: https://www.thymeleaf.org/documentation.html

Package needs to be imported before use

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

Secondly, you need to import the namespace xmlns:th="http://www.thymeleaf.org" in html

thymeleaf syntax

  • Simple expression:
    • Variable Expressions: ${...}
    • Selection Variable Expressions: *{...}
    • Message Expressions: #{...}
    • Link URL Expressions: @{...}
    • Fragment Expressions: ~{...}
  • Literals
    • 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:
    • String concatenation: +
    • Literal substitutions: |The name is ${name}|
  • Arithmetic operations:
    • Binary operators: +, -, *, /, %
    • Minus sign (unary operator): -
  • Boolean operations:
    • Binary operators: and, or
    • Boolean negation (unary operator): !, not
  • Comparisons and equality:
    • Comparators: >, <, >=, <= (gt, lt, ge, le)
    • Equality operators: ==, != (eq, ne)
  • Conditional operators:
    • If-then: (if) ? (then)
    • If-then-else: (if) ? (then) : (else)
    • Default: (value) ?: (defaultvalue)
<p>[[${msg}]]</p>
<p th:text="${msg}"></p>
<p th:utext="${msg}"></p>
<h1 th:each="item:${list}" th:text="${item}"></h1>

code reuse
<div th:fragment="sidebar">a bunch of code</div>Define the sidebar, the code is located in file 1.html
<div th:insert="~{1.html::sidebar}"></div>insert sidebar,The code is located in file 2.html

6.4 Extended MVC configuration

  • Write a class annotated with @Configuration
  • This class implements the WebMvcConfigurer interface
  • And the @EnableWebMvc annotation cannot be marked
  • The general configuration class is written under the config package
package com.daban.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    //View jump, jump to test page through kk request
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/kk").setViewName("test");
    }
}

7. Employee management system

7.1 Create a page to simulate database data

  • pojo.Department.java

    • package com.daban.pojo;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class Department {
          private Integer id;
          private String departmentName;
      }
      
  • pojo.Employee.java

    • package com.daban.pojo;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.util.Date;
      
      @Data
      @NoArgsConstructor
      public class Employee {
          private Integer id;
          private String lastName;
          private String email;
          private Integer gender;
          private Department department;
          private Date birth;
      
          public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
              this.id = id;
              this.lastName = lastName;
              this.email = email;
              this.gender = gender;
              this.department = department;
              this.birth = new Date();
          }
      }
      
  • dao.DepartmentDao.java

    • package com.daban.dao;
      
      import com.daban.pojo.Department;
      import org.springframework.stereotype.Repository;
      
      import java.util.Collection;
      import java.util.HashMap;
      import java.util.Map;
      
      @Repository
      public class DepartmentDao {
          private static Map<Integer, Department> department = null;
          static {
              department = new HashMap<Integer, Department>();
              department.put(101,new Department(101,"Education Department"));
              department.put(102,new Department(102,"Marketing Department"));
              department.put(103,new Department(103,"Department of Teaching and Research"));
              department.put(104,new Department(104,"Operations"));
              department.put(105,new Department(105,"logistics department"));
          }
          //Get all department information
          public Collection<Department> getDepartment(){
              return department.values();
          }
          //Get department by id
          public Department getDepartmentById(Integer id){
              return department.get(id);
          }
      }
      
  • dao.EmployeeDao.java

    • package com.daban.dao;
      
      import com.daban.pojo.Department;
      import com.daban.pojo.Employee;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Repository;
      
      import java.util.Collection;
      import java.util.HashMap;
      import java.util.Map;
      
      @Repository
      public class EmployeeDao {
          private static Map<Integer, Employee> employees = null;
          @Autowired
          private DepartmentDao departmentDao;
          static {
              employees = new HashMap<Integer, Employee>();
              employees.put(101,new Employee(1001,"AA","12345@qq.com",1,new Department(101,"Education Department")));
              employees.put(102,new Employee(1002,"BB","12345@qq.com",0,new Department(102,"Marketing Department")));
              employees.put(103,new Employee(1003,"CC","12345@qq.com",0,new Department(103,"Department of Teaching and Research")));
              employees.put(104,new Employee(1004,"DD","12345@qq.com",1,new Department(104,"Operations")));
              employees.put(105,new Employee(1005,"EE","12345@qq.com",0,new Department(105,"logistics department")));
          }
      
          //primary key auto increment
          private static Integer initId = 1006;
          //add an employee
          public void addEmployee(Employee employee){
              if(employee.getId()==null){
                  employee.setId(initId++);
              }
          employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
              employees.put(employee.getId(),employee);
          }
      
          //Query all employees
          public Collection<Employee> getAll(){
              return employees.values();
          }
          //Query employees by id
          public Employee getEmployeeById(Integer id){
              return employees.get(id);
          }
          //delete employee
          public void delete(Integer id){
              employees.remove(id);
          }
      }
      
  • paging file

    • https://github.com/twbs/bootstrap/releases/download/v5.3.0-alpha1/bootstrap-5.3.0-alpha1-examples.zip
    • After downloading, unzip the dashboard and sign-in in the file

7.2 Home page

The home page uses the configuration class to do it

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    //view jump
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
    }
}

7.3 Page internationalization (multilingual)

  • Create each language file

    • Create an i18n folder under the resources folder, and create three files in it

    • #Configure internationalization parameters in the global configuration file application.properties
      spring.messages.basename=i18n.login
      
    • #Create the following three files, and you can edit the three files synchronously in the Resource Bundle of the idea
      #login.properties
      login.signin=log in
      login.password=password
      login.remember=remember me
      login.tip=please login
      login.username=username
      #login_zh_CN.properties
      login.signin=sign in
      login.password=password
      login.remember=Remember me
      login.tip=please sign in
      login.username=username
      #login_en_US.properties
      login.signin=log in
      login.password=password
      login.remember=remember me
      login.tip=please login
      login.username=username
      
  • html file binding

    • Use #{} in the html file to bind the corresponding field

    • <h1 class="h3 mb-3 fw-normal" th:text="#{login.tip}">Please sign in</h1>
      <label for="floatingInput" th:text="#{login.username}">Username</label>
      <label for="floatingPassword" th:text="#{login.password}">Password</label>
      <button class="w-100 btn btn-lg btn-primary" type="submit" th:text="#{login.signin}">Sign in</button>
      <span th:text="#{login.remember}">Remember me</span>
      
  • Set Chinese and English switching buttons

    • <a class="btn btn-sm" th:href="@{/index(l='zh_CN')}">Chinese</a>
      <a class="btn btn-sm" th:href="@{/index(l='en_US')}">English</a>
      
  • Create a class MyLocaleResolver

    • package com.daban.config;
      
      import org.springframework.web.servlet.LocaleResolver;
      import org.thymeleaf.util.StringUtils;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.util.Locale;
      
      public class MyLocaleResolver implements LocaleResolver {
          @Override
          public Locale resolveLocale(HttpServletRequest request) {
              String language = request.getParameter("l");
              Locale locale = Locale.getDefault();
              if(!StringUtils.isEmpty(language)){
                  String[] s = language.split("_");
                  locale = new Locale(s[0], s[1]);
              }
              return locale;
          }
      
          @Override
          public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
      
          }
      }
      
  • Register in the MyMvcConfig class

    • For the MyMvcConfig class, see "6.4 Extended MVC Configuration"

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

7.4 Login function

login controller

package com.daban.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.thymeleaf.util.StringUtils;

@Controller
public class LoginController {
    @RequestMapping("/login")
    public String login(String username, String password, Model model){
        System.out.println(username+password);
        if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
            model.addAttribute("msg","Username or password cannot be empty");
            return "index";
        }
        else if (username.equals("admin") && password.equals("123")){
            session.setAttribute("loginUser",username);
            return "redirect:main.html";
        }else {
            model.addAttribute("msg","The username or password entered is incorrect");
            return "index";
        }

    }
}

address mapping

package com.daban.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    //view jump
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/main.html").setViewName("dashboard");
    }
    //Custom internationalization takes effect
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}

7.5 Login Interceptor

Write an interceptor class

package com.daban.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");
        if(loginUser==null){
            request.setAttribute("msg","You are not logged in");
            request.getRequestDispatcher("/index").forward(request,response);
            return false;
        }else {
            return true;
        }
    }
}

Added in MyMvcConfig class

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoginInterceptor())
            .addPathPatterns("/**")
        	//Filter without passing through the interceptor
            .excludePathPatterns("/index","/","/login","/css/**","/js/**","/img/**");
}

7.6 Display employee list

1. Extract public pages

  • code reuse
    <div th:fragment="sidebar">a bunch of code</div>Define the sidebar, the code is located in file 1.html
    <div th:insert="~{1.html::sidebar}"></div>insert sidebar,The code is located in file 2.html
     or<div th:replace="~{1.html::sidebar}"></div>insert sidebar,The code is located in file 2.html
    

2. If you want to pass parameters

​ Use () to <div th:replace="~{1.html::sidebar(active="list.html")}"></div>

3. Use the passed parameters

​ <a th:class="${active=='list.html'?'nav-link active': 'nav-link'}"/>

​ means that if active==list.html, the class attribute is nav-link active, otherwise it is nav-link

4. List cycle display

<tr th:each="item:${emps}">
  <td th:text="${item.getId()}"></td>
  <td th:text="${item.getLastName()}"></td>
  <td th:text="${item.getEmail()}"></td>
  <td th:text="${item.getGender()==0?'Female':'male'}"></td>
  <td th:text="${item.getDepartment().getDepartmentName()}"></td>
  <td th:text="${#dates.format(item.getBirth(),'yyyy-MM-dd')}"></td>
  <td>
    <a class="btn btn-sm btn-primary" th:href="">edit</a>
    <a class="btn btn-sm btn-danger" th:href="">delete</a>
  </td>
</tr>

7.7 Adding staff

Main points:

  • In the global configuration file spring.mvc.format.date=yyyy/MM/dd, set the time format

  • The form field is an object, just pass the id

    • <div class="form-group">
        <label>department</label>
        <select name="department.id" class="form-control">
          <option th:each="item:${departments}"
                  th:text="${item.getDepartmentName()}"
                  th:value="${item.getId()}">
          </option>
        </select>
      </div>
      

7.8 Modify employee information

Main points:

  • link passing parameters

    • <a class="btn btn-sm btn-primary" th:href="@{/toUpdate(id=${item.getId()})}">edit</a>
      
  • The data echo of the radio button

    • <input type="radio" th:checked="${emps.getGender()==0}" class="form-check-input" name="gender" value="0">
      
  • Drop-down box data echo

    • <select name="department.id" class="form-control">
        <option th:selected="${item.getId() ==emps.getDepartment().getId()}"
                th:each="item:${departments}"
                th:text="${item.getDepartmentName()}"
                th:value="${item.getId()}">
        </option>
      </select>
      
  • Form hidden field submission

    • <input type="hidden" th:value="${emps.getId()}" name="id" class="form-control">
      

7.9 Delete employee and 404

delete:

@RequestMapping("/delete")
public String delete(Integer id){
    //Employee employee = employeeDao.getEmployeeById(id);
    employeeDao.delete(id);
    return "redirect:/emps";
}

404:

​ The 404 function is to create an error folder in the templates directory, and place 404.html in it. Other 500s are similar

Logout:

@RequestMapping("/logout")
public String logout(HttpSession session){
    session.invalidate();
    return "redirect:index";
}

Tags: Java Spring Spring Boot Front-end Microservices

Posted by Arbitus on Wed, 04 Jan 2023 01:18:38 +0530