Springboot
1 Introduction
Spring Boot is a new framework provided by the Pivotal team, designed to simplify the initial setup and development of new Spring applications. The framework uses a specific way to configure, so that developers no longer need to define boilerplate configuration.
1.1. Dependency
<dependencies> <!-- Launcher --> <dependency> <groupId>org.springframework.boot</groupId> <!-- Springboot dependencies are based on starter --> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- You don't have to write the version number, it is managed by the parent dependency --> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
1.2. Modify the Springboot startup icon
Create a banner.txt file under resources and copy the content to be replaced into it
_ \`*-. ) _`-. . : `. . : _ ' \ ; *` _. `*-._ `-.-' `-. ; ` `. :. . \ . \ . : .-' . ' `+.; ; ' : : ' | ; ;-. ; ' : :`-: _.`* ; .*' / .*' ; .*`- +' `*' `*-* `*-* `*-*'
1.3, Springboot automatic assembly principle analysis
- SpringBoot startup loads a large number of auto-configuration classes
- Let's see if the functions we need are in the automatic configuration classes written by SpringBoot by default.
- Let's look at which components are configured in this automatic configuration class; (as long as the components we want to use exist in it, we don't need to configure it manually)
- When adding components to the auto-configuration class in the container, certain properties are obtained from the properties class. We only need to specify the values of these properties in the configuration file;
xxxxAutoConfigurartion: Automatic configuration class; add components to the container
xxxxProperties: related properties in the package configuration file;
[External link image transfer failed, the source site may have an anti-leech mechanism, it is recommended to save the image and upload it directly (img-FmkYqmZy-1659458588010)(F:\Paint\SpringBoot automatic assembly principle.png)]
Configuration in Springboot's application.properties
debug=true:(output the components we have configured) Positive matches:(autoconfiguration class enabled: positive match) Negative matches:(No startup, no auto-config class that matched successfully: negative match) Unconditional classes: (class without conditions)
2. JSR303 verification
2.1, yaml syntax
- Spaces cannot be omitted, there must be a space between key-value pairs
- The hierarchical relationship is controlled by indentation, as long as a column of data aligned to the left is at the same level.
- Properties and values are case sensitive.
2.2, yaml configuration file format
server: prot: 8080
2.3, yaml configuration bean properties
person: name: luffy${random.uuid} age: ${random.int} happy: false birth: 2020/12/2 # hello: haha maps: {k1: v1,k2: v2} lists: - code - music - girl dog: firstName: ${person.hello:hehe}little black age: 3
2.4, yaml binding configuration file
@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; }
2.5, test yaml
@Autowired private Person person; @Test void contextLoads() { System.out.println(person); }
2.6, loose binding
first-name in yml = firstName in bean
2.7, data verification
@Validated public class person{ // JSR303 common check @NotNull(message="name cannot be empty") private String userName; @Max(value=120,message="The maximum age can not be checked 120") private int age; @Email(message="Email format error") private String email; } // Empty check @Null Verify that the object is null @NotNull Verify that the object is not null, Unable to check string with length 0 @NotBlank Check if the constraint string is not Null also been Trim Is the length greater than 0,only for strings,and will remove leading and trailing spaces. @NotEmpty Check if the constraint element is NULL or EMPTY. // Booelan inspection @AssertTrue verify Boolean whether the object is true @AssertFalse verify Boolean whether the object is false // length check @Size(min=, max=) Authentication object ( Array,Collection,Map,String)Whether the length is within the given range @Length(min=, max=) string is between min and max included. // date check @Past verify Date and Calendar whether the object is before the current time @Future verify Date and Calendar whether the object is after the current time @Pattern verify String Whether the object conforms to the rules of the regular expression
2.8, where application.yml can be installed
priority:
- file:config/appplication.yml
- file:application.yml
- resources:/config/application.yml
- resources:application.yml
2.9, yml multi-configuration
server: port: 8080 # Choose which configuration to use spring: profiles: active: dev # activate dev configuration --- server: port: 8081 spring: profiles: test --- server: port: 8082 spring: profiles: dev
3. web development
3.1. Static resources
source code
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); // The first way webjars if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/") .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } // the second way String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } }
webjars method (deprecated)
- Need to import static resources that need to be used, such as Jquery's maven dependencies
- The imported static resources are under META-INF/resources/webjars of the dependency
- The access path follows http://localhost:8080/webjars/ and you can use it
Recommended method
/**: The static resources accessed at http://localhost:8080/ will get the static resources of the following paths
Prioritization:
- classpath: /META-INF/resources/
- classpath: /resources/
- classpath: /static/ (created by default)
- classpath: /public/
Configure the static resource path by yourself in application.properties, then the path specified by the system will not take effect
spring.mvc.static-path-pattern=/hello/,classpath:/luffy/
3.2. Home
Just create index.html under the corresponding static resource path
source code
private Resource getIndexHtml(String location) { return this.getIndexHtml(this.resourceLoader.getResource(location)); } private Resource getIndexHtml(Resource location) { try { Resource resource = location.createRelative("index.html"); if (resource.exists() && resource.getURL() != null) { return resource; } } catch (Exception var3) { } return null; }
public interface ResourceLoader { String CLASSPATH_URL_PREFIX = "classpath:"; Resource getResource(String location); @Nullable ClassLoader getClassLoader(); }
3.3. Web icon customization
The new version code was changed
Here's how to do it in older versions
- Find a picture and change the suffix name to ico
- Put it in the public static folder
- Set application.properties properties to turn off the default icon
- spring.mvc.favicon.enabled=false
Look at the source code specifically, I won't go into details here
3.4, thymeleaf template engine
The interface used by the thymeleaf template engine is placed under templates
source code
@ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; // prefix public static final String DEFAULT_PREFIX = "classpath:/templates/"; // suffix public static final String DEFAULT_SUFFIX = ".html"; ... }
Steps for usage
-
import dependencies
<!--thymeleaf The template engine is based on 3.x Version development--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency>
-
Controller layer
@RequestMapping("/test") public String test(Model model){ model.addAttribute("msg","hello,springboot ! "); return "test"; }
-
html interface
<!-- all html elements can be thymeleaf Replace takeover: th: element name --> <div th:text="${msg}"></div>
3.5, thymeleaf syntax
Escape syntax and traversal operations
-
Controller layer
@Controller public class TestController { @RequestMapping("/test") public String test(Model model){ model.addAttribute("msg1","<h1>hello,springboot !</h1>"); model.addAttribute("msg2","<h1>hello,springboot!</h1>"); model.addAttribute("users", Arrays.asList("Luffy","One Piece")); return "test"; } }
-
html interface
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- all html elements can be thymeleaf Replace takeover: th: element name --> <!--Escape, all to text--> <div th:text="${msg1}"></div> <!--Text is not escaped, tags can be read--> <div th:utext="${msg2}"></div> <!--recommend--> <h1 th:each="users:${users}" th:text="${users}"></h1> <!--Not recommended--> <!--<h1 th:each="users:${users}">[[${users}]]</h1>--> </body> </html>
Common grammar
[External link image transfer failed, the source site may have an anti-leech mechanism, it is recommended to save the image and upload it directly (img-sB9Ro99p-1659458588012)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220622101953141.png)]
3.6, SpringMVC extension
Sprint-boot must learn to read the source code. If you don't read the source code, you must learn it badly.
Create a Config class under the Config folder, implement implements WebMvcConfigurer, rewrite some methods, and achieve the extension of MVC
You can no longer use @EnableWebMvc, because the same class is finally implemented in this annotation, and continued use will make them all invalid
/** * Extend SpringMVC: implement the WebMvcConfigurer interface, rewrite some methods, let us implement some new configurations * Note: @EnableWebMvc can no longer be used, this method will invalidate the extension method */ @Configuration public class MyMVCConfig implements WebMvcConfigurer { /** * View jump Access /ssl, jump to config.html */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/ssl").setViewName("config"); } }
4. Project combat
4.1. Preparations
pojo
@Data //parameterized construction @AllArgsConstructor //No-argument constructor @NoArgsConstructor public class Department { private Integer id; private String departmentName; }
@Data @NoArgsConstructor public class Employee { private Integer id; private String lastName; private String email; private Integer gender;//0 female 1 male 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
@Repository public class EmployeeDao { private static Map<Integer, Employee> employees = null; @Autowired private DepartmentDao departmentDao; static { employees = new HashMap<Integer,Employee>();//Create an employee table employees.put(1001,new Employee(1001,"Ma Jiaqi","11@qq.com",1,new Department(101,"Education Department"))); employees.put(1002,new Employee(1002,"Ding Chengxin","12@qq.com",1,new Department(101,"Education Department"))); employees.put(1003,new Employee(1003,"Song Yaxuan","13@qq.com",1,new Department(102,"Marketing Department"))); employees.put(1004,new Employee(1004,"Liu Yaowen","14@qq.com",1,new Department(102,"Marketing Department"))); employees.put(1005,new Employee(1005,"Zhang Zhenyuan","15@qq.com",1,new Department(103,"Teaching and Research Department"))); employees.put(1006,new Employee(1006,"Yan Haoxiang","16@qq.com",1,new Department(104,"Operations"))); employees.put(1007,new Employee(1007,"He Junlin","17@qq.com",1,new Department(104,"Operations"))); } //Primary key auto increment private static Integer initId = 1008; //add an employee public void save(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 based on employee id public void delete(Integer id){ employees.remove(id); } }
@Repository public class DepartmentDao { private static Map<Integer, Department> departments = null; static { departments = new HashMap<Integer, Department>();//Create a department table departments.put(101,new Department(101,"Education Department")); departments.put(102,new Department(102,"Marketing Department")); departments.put(103,new Department(103,"Teaching and Research Department")); departments.put(104,new Department(104,"Operations")); departments.put(105,new Department(105,"Finance Department")); } //Get all department information public Collection<Department> getDepartments(){ return departments.values(); } //get department by id public Department getDepartmentById(Integer id){ return departments.get(id); } }
4.2. Homepage implementation
MyMvcConfig
//If you want to diy some customized functions, just write this component, and then hand it over to Springboot, and Springboot will automatically assemble it for us! //Extending Springmvc dispatchservlet @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("index.html").setViewName("index"); } }
Turn off thymeLeaf caching
#Turn off thymeLeaf caching spring.thymeleaf.cache=false
front-end interface
404.html
<!DOCTYPE html> <!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ --> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Dashboard Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link th:href="@{/css/dashboard.css}" rel="stylesheet"> <style type="text/css"> /* Chart.js */ @-webkit-keyframes chartjs-render-animation { from { opacity: 0.99 } to { opacity: 1 } } @keyframes chartjs-render-animation { from { opacity: 0.99 } to { opacity: 1 } } .chartjs-render-monitor { -webkit-animation: chartjs-render-animation 0.001s; animation: chartjs-render-animation 0.001s; } </style> </head> <body> <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0"> <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company name</a> <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search"> <ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a> </li> </ul> </nav> <div class="container-fluid"> <div class="row"> <nav class="col-md-2 d-none d-md-block bg-light sidebar"> <div class="sidebar-sticky"> <ul class="nav flex-column"> <li class="nav-item"> <a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"> <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> Dashboard <span class="sr-only">(current)</span> </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"> <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path> <polyline points="13 2 13 9 20 9"></polyline> </svg> Orders </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart"> <circle cx="9" cy="21" r="1"></circle> <circle cx="20" cy="21" r="1"></circle> <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path> </svg> Products </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"> <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path> <circle cx="9" cy="7" r="4"></circle> <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path> <path d="M16 3.13a4 4 0 0 1 0 7.75"></path> </svg> Customers </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2"> <line x1="18" y1="20" x2="18" y2="10"></line> <line x1="12" y1="20" x2="12" y2="4"></line> <line x1="6" y1="20" x2="6" y2="14"></line> </svg> Reports </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers"> <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon> <polyline points="2 17 12 22 22 17"></polyline> <polyline points="2 12 12 17 22 12"></polyline> </svg> Integrations </a> </li> </ul> <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted"> <span>Saved reports</span> <a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg> </a> </h6> <ul class="nav flex-column mb-2"> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Current month </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Last quarter </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Social engagement </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Year-end sale </a> </li> </ul> </div> </nav> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h1>404</h1> </main> </div> </div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script type="text/javascript" th:src="@{/js/jquery-3.2.1.slim.min.js}" ></script> <script type="text/javascript" th:src="@{/js/popper.min.js}" ></script> <script type="text/javascript" th:src="@{/js/bootstrap.min.js}" ></script> <!-- Icons --> <script type="text/javascript" th:src="@{/js/feather.min.js}" ></script> <script> feather.replace() </script> <!-- Graphs --> <script type="text/javascript" th:src="@{/js/Chart.min.js}" ></script> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'line', data: { labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], datasets: [{ data: [15339, 21345, 18483, 24003, 23489, 24092, 12034], lineTension: 0, backgroundColor: 'transparent', borderColor: '#007bff', borderWidth: 4, pointBackgroundColor: '#007bff' }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero: false } }] }, legend: { display: false, } } }); </script> </body> </html>
dashboard.html
<!DOCTYPE html> <!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ --> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Dashboard Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link th:href="@{/css/dashboard.css}" rel="stylesheet"> <style type="text/css"> /* Chart.js */ @-webkit-keyframes chartjs-render-animation { from { opacity: 0.99 } to { opacity: 1 } } @keyframes chartjs-render-animation { from { opacity: 0.99 } to { opacity: 1 } } .chartjs-render-monitor { -webkit-animation: chartjs-render-animation 0.001s; animation: chartjs-render-animation 0.001s; } </style> </head> <body> <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0"> <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company name</a> <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search"> <ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a> </li> </ul> </nav> <div class="container-fluid"> <div class="row"> <nav class="col-md-2 d-none d-md-block bg-light sidebar"> <div class="sidebar-sticky"> <ul class="nav flex-column"> <li class="nav-item"> <a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"> <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> Dashboard <span class="sr-only">(current)</span> </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"> <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path> <polyline points="13 2 13 9 20 9"></polyline> </svg> Orders </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart"> <circle cx="9" cy="21" r="1"></circle> <circle cx="20" cy="21" r="1"></circle> <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path> </svg> Products </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"> <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path> <circle cx="9" cy="7" r="4"></circle> <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path> <path d="M16 3.13a4 4 0 0 1 0 7.75"></path> </svg> Customers </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2"> <line x1="18" y1="20" x2="18" y2="10"></line> <line x1="12" y1="20" x2="12" y2="4"></line> <line x1="6" y1="20" x2="6" y2="14"></line> </svg> Reports </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers"> <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon> <polyline points="2 17 12 22 22 17"></polyline> <polyline points="2 12 12 17 22 12"></polyline> </svg> Integrations </a> </li> </ul> <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted"> <span>Saved reports</span> <a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg> </a> </h6> <ul class="nav flex-column mb-2"> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Current month </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Last quarter </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Social engagement </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Year-end sale </a> </li> </ul> </div> </nav> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <div class="chartjs-size-monitor" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;"> <div class="chartjs-size-monitor-expand" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;"> <div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0"></div> </div> <div class="chartjs-size-monitor-shrink" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;"> <div style="position:absolute;width:200%;height:200%;left:0; top:0"></div> </div> </div> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom"> <h1 class="h2">Dashboard</h1> <div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-group mr-2"> <button class="btn btn-sm btn-outline-secondary">Share</button> <button class="btn btn-sm btn-outline-secondary">Export</button> </div> <button class="btn btn-sm btn-outline-secondary dropdown-toggle"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-calendar"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> This week </button> </div> </div> <canvas class="my-4 chartjs-render-monitor" id="myChart" width="1076" height="454" style="display: block; width: 1076px; height: 454px;"></canvas> </main> </div> </div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script type="text/javascript" th:src="@{/js/jquery-3.2.1.slim.min.js}" ></script> <script type="text/javascript" th:src="@{/js/popper.min.js}" ></script> <script type="text/javascript" th:src="@{/js/bootstrap.min.js}" ></script> <!-- Icons --> <script type="text/javascript" th:src="@{/js/feather.min.js}" ></script> <script> feather.replace() </script> <!-- Graphs --> <script type="text/javascript" th:src="@{/js/Chart.min.js}" ></script> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'line', data: { labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], datasets: [{ data: [15339, 21345, 18483, 24003, 23489, 24092, 12034], lineTension: 0, backgroundColor: 'transparent', borderColor: '#007bff', borderWidth: 4, pointBackgroundColor: '#007bff' }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero: false } }] }, legend: { display: false, } } }); </script> </body> </html>
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Signin Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link th:href="@{/css/signin.css}" rel="stylesheet"> </head> <body class="text-center"> <form class="form-signin" action="dashboard.html"> <img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72"> <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1> <label class="sr-only">Username</label> <input type="text" class="form-control" placeholder="Username" required="" autofocus=""> <label class="sr-only">Password</label> <input type="password" class="form-control" placeholder="Password" required=""> <div class="checkbox mb-3"> <label> <input type="checkbox" value="remember-me"> Remember me </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> <p class="mt-5 mb-3 text-muted">© 2017-2018</p> <a class="btn btn-sm">Chinese</a> <a class="btn btn-sm">English</a> </form> </body> </html>
list.html
<!DOCTYPE html> <!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ --> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Dashboard Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link th:href="@{/css/dashboard.css}" rel="stylesheet"> <style type="text/css"> /* Chart.js */ @-webkit-keyframes chartjs-render-animation { from { opacity: 0.99 } to { opacity: 1 } } @keyframes chartjs-render-animation { from { opacity: 0.99 } to { opacity: 1 } } .chartjs-render-monitor { -webkit-animation: chartjs-render-animation 0.001s; animation: chartjs-render-animation 0.001s; } </style> </head> <body> <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0"> <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company name</a> <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search"> <ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a> </li> </ul> </nav> <div class="container-fluid"> <div class="row"> <nav class="col-md-2 d-none d-md-block bg-light sidebar"> <div class="sidebar-sticky"> <ul class="nav flex-column"> <li class="nav-item"> <a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"> <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> Dashboard <span class="sr-only">(current)</span> </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"> <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path> <polyline points="13 2 13 9 20 9"></polyline> </svg> Orders </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart"> <circle cx="9" cy="21" r="1"></circle> <circle cx="20" cy="21" r="1"></circle> <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path> </svg> Products </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"> <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path> <circle cx="9" cy="7" r="4"></circle> <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path> <path d="M16 3.13a4 4 0 0 1 0 7.75"></path> </svg> Customers </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2"> <line x1="18" y1="20" x2="18" y2="10"></line> <line x1="12" y1="20" x2="12" y2="4"></line> <line x1="6" y1="20" x2="6" y2="14"></line> </svg> Reports </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers"> <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon> <polyline points="2 17 12 22 22 17"></polyline> <polyline points="2 12 12 17 22 12"></polyline> </svg> Integrations </a> </li> </ul> <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted"> <span>Saved reports</span> <a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg> </a> </h6> <ul class="nav flex-column mb-2"> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Current month </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Last quarter </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Social engagement </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Year-end sale </a> </li> </ul> </div> </nav> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2>Section title</h2> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>#</th> <th>Header</th> <th>Header</th> <th>Header</th> <th>Header</th> </tr> </thead> <tbody> <tr> <td>1,001</td> <td>Lorem</td> <td>ipsum</td> <td>dolor</td> <td>sit</td> </tr> <tr> <td>1,002</td> <td>amet</td> <td>consectetur</td> <td>adipiscing</td> <td>elit</td> </tr> <tr> <td>1,003</td> <td>Integer</td> <td>nec</td> <td>odio</td> <td>Praesent</td> </tr> <tr> <td>1,003</td> <td>libero</td> <td>Sed</td> <td>cursus</td> <td>ante</td> </tr> <tr> <td>1,004</td> <td>dapibus</td> <td>diam</td> <td>Sed</td> <td>nisi</td> </tr> <tr> <td>1,005</td> <td>Nulla</td> <td>quis</td> <td>sem</td> <td>at</td> </tr> <tr> <td>1,006</td> <td>nibh</td> <td>elementum</td> <td>imperdiet</td> <td>Duis</td> </tr> <tr> <td>1,007</td> <td>sagittis</td> <td>ipsum</td> <td>Praesent</td> <td>mauris</td> </tr> <tr> <td>1,008</td> <td>Fusce</td> <td>nec</td> <td>tellus</td> <td>sed</td> </tr> <tr> <td>1,009</td> <td>augue</td> <td>semper</td> <td>porta</td> <td>Mauris</td> </tr> <tr> <td>1,010</td> <td>massa</td> <td>Vestibulum</td> <td>lacinia</td> <td>arcu</td> </tr> <tr> <td>1,011</td> <td>eget</td> <td>nulla</td> <td>Class</td> <td>aptent</td> </tr> <tr> <td>1,012</td> <td>taciti</td> <td>sociosqu</td> <td>ad</td> <td>litora</td> </tr> <tr> <td>1,013</td> <td>torquent</td> <td>per</td> <td>conubia</td> <td>nostra</td> </tr> <tr> <td>1,014</td> <td>per</td> <td>inceptos</td> <td>himenaeos</td> <td>Curabitur</td> </tr> <tr> <td>1,015</td> <td>sodales</td> <td>ligula</td> <td>in</td> <td>libero</td> </tr> </tbody> </table> </div> </main> </div> </div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script type="text/javascript" th:src="@{/js/jquery-3.2.1.slim.min.js}"></script> <script type="text/javascript" th:src="@{/js/popper.min.js}"></script> <script type="text/javascript" th:src="@{/js/bootstrap.min.js}"></script> <!-- Icons --> <script type="text/javascript" th:src="@{/js/feather.min.js}"></script> <script> feather.replace() </script> <!-- Graphs --> <script type="text/javascript" th:src="@{/js/Chart.min.js}"></script> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'line', data: { labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], datasets: [{ data: [15339, 21345, 18483, 24003, 23489, 24092, 12034], lineTension: 0, backgroundColor: 'transparent', borderColor: '#007bff', borderWidth: 4, pointBackgroundColor: '#007bff' }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero: false } }] }, legend: { display: false, } } }); </script> </body> </html>
4.3. Page internationalization
Realize the page Chinese and English language switching! !!
- Create the corresponding switching configuration file under Resource
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-e512MW5a-1659458588014)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220702135818964.png)]
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-Vq7dzPgf-1659458588014)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220702140228426.png)]
- Write the path to the configuration file in application.properties
#the location of the configuration file spring.messages.basename=i18n.login
- Use syntax to retrieve values in front-end pages
<form class="form-signin" action="dashboard.html"> <img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72"> <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}"></h1> <label class="sr-only">Username</label> <input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus=""> <label class="sr-only">Password</label> <input type="password" class="form-control" th:placeholder="#{login.password}" required=""> <div class="checkbox mb-3"> <label> <input type="checkbox" value="remember-me" th:text="#{login.remember}"> </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}"></button> <p class="mt-5 mb-3 text-muted">© 2017-2018</p> <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> </form>
-
Write the MyLocalResolver class
public class MyLocalResolver implements LocaleResolver { //Parse the request @Override public Locale resolveLocale(HttpServletRequest request) { String language = request.getParameter("l"); Locale locale = Locale.getDefault();//If not, use the default //If the request link carries internationalized parameters if (!StringUtils.isEmpty(language)){ String[] split = language.split("_"); locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } }
-
register in mymvcconfig
@Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("index.html").setViewName("index"); } //custom internationalization @Bean public LocaleResolver localeResolver(){ return new MyLocalResolver(); } }
Note: You need to set the encoding language to utf-8 in the file encoding set by idea first, otherwise it may be garbled.
4.4. Implementation of login function
-
LoginController
@Controller public class LoginController { @RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){ if(!StringUtils.isEmpty(username) && "123456".equals(password)){ //Redirect page after successful login return "redirect:/main.html"; }else { model.addAttribute("msg","Username or password is incorrect"); return "index"; } } }
-
Configure virtual requests in MyMvcConfig.java
registry.addViewController("main.html").setViewName("dashboard");
-
Receive error message with P tag on front-end page
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
4.5, login interceptor
-
LoginHandlerInterceptor
public class LoginHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //After a successful login, there should be a user session Object loginUser = request.getSession().getAttribute("loginUser"); if(loginUser == null){ request.setAttribute("msg","Permission denied,please log in first"); request.getRequestDispatcher("/index.html").forward(request,response); return false; }else{ return true; } } }
-
MyMvcConfig configures the interceptor bean
//Login blocker @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/css/**","/img/**","/img/**"); }
-
Configure session in login request
@RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){ if(!StringUtils.isEmpty(username) && "123456".equals(password)){ session.setAttribute("loginUser",username); //Redirect page after successful login return "redirect:/main.html"; }else { model.addAttribute("msg","Username or password is incorrect"); return "index"; } }
-
Login successfully on the front end of the home page and get the session display
[[${session.loginUser}]]
4.6. Display employee page
-
Create subfolders in templates (emp put subpages, commons is fragmented management)
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-pJMJqU3I-1659458588015)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220703103230189.png)]
-
commons code (mainly distinguish the top and side code, easy to share)
- th:fragment="××" (define fragment)
- th:insert="~{commons/commons::××}" (using fragments)
<!--top--> <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar"> <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a> <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search"> <ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" th:href="@{/user/signOut}">log out</a> </li> </ul> </nav> <!--side--> <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar"> <div class="sidebar-sticky"> <ul class="nav flex-column"> <li class="nav-item"> <a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/main.html}"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"> <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> front page <span class="sr-only">(current)</span> </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"> <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path> <polyline points="13 2 13 9 20 9"></polyline> </svg> Orders </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart"> <circle cx="9" cy="21" r="1"></circle> <circle cx="20" cy="21" r="1"></circle> <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path> </svg> Products </a> </li> <li class="nav-item"> <a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"> <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path> <circle cx="9" cy="7" r="4"></circle> <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path> <path d="M16 3.13a4 4 0 0 1 0 7.75"></path> </svg> Staff management </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2"> <line x1="18" y1="20" x2="18" y2="10"></line> <line x1="12" y1="20" x2="12" y2="4"></line> <line x1="6" y1="20" x2="6" y2="14"></line> </svg> Reports </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers"> <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon> <polyline points="2 17 12 22 22 17"></polyline> <polyline points="2 12 12 17 22 12"></polyline> </svg> Integrations </a> </li> </ul> <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted"> <span>Saved reports</span> <a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg> </a> </h6> <ul class="nav flex-column mb-2"> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Current month </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Last quarter </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Social engagement </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> <polyline points="14 2 14 8 20 8"></polyline> <line x1="16" y1="13" x2="8" y2="13"></line> <line x1="16" y1="17" x2="8" y2="17"></line> <polyline points="10 9 9 9 8 9"></polyline> </svg> Year-end sale </a> </li> </ul> </div> </nav>
-
Add the method of using fragments under the dashboard and list to improve code reusability
- sidebar and topbar are the names of the fragments
- The parentheses that follow are the parameter name active, and when you click on the dashboard, the parameter main.html is passed to highlight the sidebar
- Highlight the corresponding code: th:class="${active=='main.html'?'nav-link active':'nav-link'}"
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-nSYPFiQd-1659458588016)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220703103626117.png)]
-
EmployeeController (query employee information when clicking on the employee management module and return to list.html for display)
@RequestMapping("/emps") public String list(Model model){ Collection<Employee> employees = employeeDao.getAll(); model.addAttribute("emps",employees); return "emp/list"; }
-
The list page traverses employee information
<table class="table table-striped table-sm"> <thead> <tr> <th>id</th> <th>lastName</th> <th>email</th> <th>gender</th> <th>department</th> <th>birth</th> <th>operate</th> </tr> </thead> <tbody> <tr th:each="emp:${emps}"> <td th:text="${emp.getId()}"></td> <td th:text="${emp.getLastName()}"></td> <td th:text="${emp.getEmail()}"></td> <td th:text="${emp.getGender()==0?'Female':'male'}"></td> <td th:text="${emp.department.getDepartmentName()}"></td> <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"> </td> <td> <button class="btn btn-sm btn-primary">edit</button> <button class="btn btn-sm btn-danger">del</button> </td> </tr> </tbody> </table>
4.7. Increase staff
-
Add a button to add an employee to the user presentation page
<!--Add employees--> <h2> <!--default get Request, the first time to echo the department information in the employee, use the default get ask--> <a class="btn btn-sm btn-success" th:href="@{/emp}">Add employees</a> </h2>
-
Write the add page add.html
LastNameEmailGendermaleFemaleDepartmentBirthAdd to -
EmployeeController adds two requests
//Click the Add button to request to the add page, because adding requires department information, first query the department information and pass it as a parameter @RequestMapping("/emp") public String toAddpage(Model model){ Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments",departments); return "emp/add"; } //Click the Add success button to save the added data, and then redirect to the request to query the user page @PostMapping("/emp") public String toAddEmp(Employee employee){ employeeDao.save(employee); return "redirect:/emps"; }
-
Modified date format
# Modify the date format, the default is yyyy/MM/dd spring.mvc.date-format=yyyy-MM-dd
4.7. Modify employees
-
Edit button parameters
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">edit</a>
-
Two requests from EmployeeController
//Receive the passed id, then query the department information, and jump to the modification interface @GetMapping("/emp/{id}") public String toUpdateEmpPage(@PathVariable("id") Integer id, Model model) { Employee employee = employeeDao.getEmployeeById(id); model.addAttribute("emp", employee); Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments", departments); return "emp/update"; } //A request to redirect the modified information to the query user page @PostMapping("/updateEmp") public String updateEmp(Employee employee) { employeeDao.save(employee); return "redirect:/emps"; }
-
write edit page
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <form th:action="@{/updateEmp}" method="post"> <!--hide detected empId--> <input type="hidden" name="id" th:value="${emp.getId()}"> <div class="form-group"> <label>LastName</label> <input name="lastName" th:value="${emp.getLastName()}" type="text" class="form-control" id="exampleInputEmail1" placeholder="lastName"> </div> <div class="form-group"> <label>Email</label> <input name="email" th:value="${emp.getEmail()}" type="email" class="form-control" id="exampleInputPassword1" placeholder="email"> </div> <div class="form-group"> <label>Gender</label> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==1}" class="form-check-input" type="radio" value="1" name="gender"/> <label class="form-check-label">male</label> </div> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==0}" class="form-check-input" type="radio" value="0" name="gender"/> <label class="form-check-label">Female</label> </div> </div> <div class="form-group"> <label>Department</label> <!--submitted value Yes id--> <select name="department.id" class="form-control"> <!--echo: department id equal to the employee department id--> <option th:each="dept:${departments}" th:selected="${dept.getId()==emp.getDepartment().getId()}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option> </select> </div> <div class="form-group"> <label>Birth</label> <!--Date echo, need to change the default format--> <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" name="birth" type="text" class="form-control" placeholder="yyyy-MM-dd"> </div> <button type="submit" class="btn btn-default">Revise</button> </form> </main>
4.8. Delete employees
-
delete button parameter
<a class="btn btn-sm btn-danger" th:href="@{/deleteEmp/}+${emp.getId()}">delete</a>
-
A request from EmployeeController
@GetMapping("/deleteEmp/{id}") public String deleteEmp(@PathVariable("id") Integer id) { employeeDao.delete(id); return "redirect:/emps"; }
4.9. Error page
- springBoot is encapsulated, create an error package in the template, put 404.html below, and automatically jump to this page when an error occurs
4.10. Cancellation
-
logout button
<a class="nav-link" th:href="@{/user/signOut}">log out</a>
-
A request from LoginController
@RequestMapping("/user/signOut") public String signOut(HttpSession session){ session.removeAttribute("loginUser"); return "redirect:/index.html"; }
5. springboot integration
5.1. Integrate JDBC
-
add dependencies
<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>
-
Use yaml to connect to the database configuration
spring: datasource: username: root password: root #?serverTimezone=UTC resolves time zone errors url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver
-
Test the integrated JDBC environment
@SpringBootTest class Springboot04DataApplicationTests { //DI injection data source @Autowired(required = false) DataSource dataSource; @Test public void contextLoads() throws SQLException { //Take a look at the default data source System.out.println(dataSource.getClass()); //get connected Connection connection = dataSource.getConnection(); System.out.println(connection); //close the connection connection.close(); } }
-
Test CRUD
@RestController public class JDBCController { @Autowired(required = false) JdbcTemplate jdbcTemplate; // Query all information in the database // If there is no entity class, how to get the things from the database? Map @GetMapping("/userList") public List<Map<String,Object>> userList() { String sql = "select * from mybatis.user"; List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql); return list_maps; } @GetMapping("/addUser") public String addUser() { String sql = "insert into mybatis.user(id, name, pwd) values(8,'Xiao Ming','123456')"; jdbcTemplate.update(sql); return "update-ok"; } @GetMapping("/updateUser/{id}") public String updateUser(@PathVariable("id") int id) { String sql = "update mybatis.user set name = ?,pwd = ? where id = " + id; //package Object[] objects = new Object[2]; objects[0] = "Xiao Ming 2"; objects[1] = "aaaaaaa"; jdbcTemplate.update(sql,objects); return "update-ok"; } @GetMapping("/deleteUser/{id}") public String deleteUser(@PathVariable("id") int id) { String sql = "delete from mybatis.user where id = ?"; jdbcTemplate.update(sql,id); return "update-ok"; } }
5.2. Integrate Druid
- Druid is a database connection pool implementation on Alibaba's open source platform. It combines the advantages of DB pools such as C3P0 and DBCP, and adds log monitoring.
-
Add Druid and log4j data source dependencies
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.24</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
yaml configuration file to add Druid requirements
type: com.alibaba.druid.pool.DruidDataSource #Spring Boot does not inject these property values by default, you need to bind them 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 filters for monitoring statistics interception, stat: monitoring statistics, log4j: logging, wall: defense against sql injection #If allowed, it will report an error java.lang.ClassNotFoundException: org.apache.log4j.Priority #Then import the log4j dependency, 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
-
Write Druid configuration class
@Configuration public class DruidConfig { //Configure custom Druid into the IOC container @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource getDataSource() { return new DruidDataSource(); } //Configure Druid to monitor and manage the Servlet in the background; //There is no web.xml file when the Servlet container is built-in, so use Spring Boot's registration Servlet method @Bean public ServletRegistrationBean statViewServlet() { ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); // These parameters can be found at com.alibaba.druid.support.http.StatViewServlet // found in the parent class of com.alibaba.druid.support.http.ResourceServlet Map<String, String> initParams = new HashMap<>(); initParams.put("loginUsername", "root"); //The login account of the background management interface initParams.put("loginPassword", "admin"); //The login password of the background management interface //Who can access the backend //initParams.put("allow", "localhost"): Indicates that only the local machine can access //initParams.put("allow", ""): When empty or null, it means all access is allowed initParams.put("allow", ""); //deny: Druid background denies who to access //initParams.put("kuangshen", "192.168.1.20"); indicates that this ip is forbidden to access //Set initialization parameters bean.setInitParameters(initParams); return bean; } //Configure the filter for web monitoring of Druid monitoring //WebStatFilter: Used to configure management correlation monitoring statistics between Web and Druid data sources @Bean public FilterRegistrationBean webStatFilter() { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); //exclusions: Set which requests are filtered and excluded, so that no statistics are performed 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; } }
After configuring the druid class, visit http://localhost:8080/druid/ to enter the druid monitoring. The account password is specified in the configuration class. After entering, you can view the information of each sql
5.3. Integrate mybatis
-
import dependencies
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency>
-
Configure application.yaml
# Integrate data sources spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chacacterEncoding=utf-8&serverTimezone=UTC # Springboot uses com.mysql.cj.jdbc.Driver for Mysql8 and above, 5 may have bug s driver-class-name: com.mysql.cj.jdbc.Driver # Integrate mybatis mybatis: type-aliases-package: com.example.springboot05mybatis.pojo # Solve the binding exception: mapper.xml should be consistent with the package name path of the interface mapper-locations: classpath:mybatis/mapper/*.xml
-
entity class
@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
-
mapper layer interface
@Mapper// This annotation indicates that this is a mapper annotation class @Repository public interface UserMapper { List<User> getAllUser(); User getUserById(@Param("id") int id); void addUser(User user); void deleteUser(@Param("id")int id); User updateUser(User user); }
-
mapper.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.example.springboot05mybatis.mapper.UserMapper"> <select id="getAllUser" resultType="User"> SELECT * FROM mybatis.user; </select> <select id="getUserById" parameterType="int" resultType="User"> select * from mybatis.user where id=#{id}; </select> <insert id="addUser" parameterType="User"> insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd}) </insert> <update id="updateUser" parameterType="User"> update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id} </update> <delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id} </delete> </mapper>
6,SpringSecurity
6.1, Introduction to Spring Security
SpringSecurity is a security framework for spring projects, and it is also the default security module technology selection at the bottom of springboot. It can implement a powerful Web security mechanism. It only needs a few spring-boot--spring-security dependencies and a small amount of configuration. Enable robust security management
-
WebSecurityConfigurerAdapter: Custom Security Policy
-
AuthenticationManagerBuilder: custom authentication strategy
-
@EnableWebSecurity: Enable WebSecurity mode [@Enablexxxx: Enable a function]
Two main goals of Spring Security
- Authentication method: Authentication
- Permission: Authorization
6.1, Springboot integrates SpringSecurity environment to build
-
import dependencies
<!--security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency> <!--security Integrate thymeleaf,Ease of front-end integration Springboot2.1.X above need springSecurity5 version of but xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" suffix or 4 can still be used --> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency>
-
RouterController
@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; } }
-
import static resources
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-0VXsgx4T-1659458588017)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220706154208100.png)]
-
MySecurityConfig security configuration class writing
@EnableWebSecurity public class MySecurityConfig extends WebSecurityConfigurerAdapter { // url authorization: HttpSecurity @Override protected void configure(HttpSecurity security) throws Exception { // The home page can be accessed by everyone, but the functions can only be accessed by those who have permission security .authorizeRequests() // Authentication request .antMatchers("/").permitAll()//Anyone can enter this request .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3") ; // Comes with login page, http://localhost:8080/login // Custom login page, loginPage("/toLogin") // Specify the form submission url: loginProcessingUrl("/login") security.formLogin().loginPage("/toLogin") .usernameParameter("username").passwordParameter("password") .loginProcessingUrl("/login"); // Prevent website tools from getting post security.csrf().disable();//Turn off the csrf function, the reason why the login fails // Turn on the remember me function: the essence is to remember a cookie, which is saved for 2 weeks by default security.rememberMe().rememberMeParameter("remember"); // Turn on the logout function, the source code is http://localhost:8080/logout, and after the logout is successful, jump to the Controller of / security.logout().logoutSuccessUrl("/"); } // User Authentication: AuthenticationManagerBuilder // After Spring Security 5, Xinzheng password encryption method is required by default @Override public void configure(AuthenticationManagerBuilder builder) throws Exception { // In-memory test data builder.inMemoryAuthentication() // After Spring Security 5, Xinzheng password encryption method is required by default .passwordEncoder(new BCryptPasswordEncoder()) .withUser("luffy").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1") .and() .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2", "vip3") ; } }
-
index.html Modify the code built by thymeleaf and security
<!--login logout--> <div class="right menu"> <!--If not logged in, disappear login button--> <div sec:authorize="!isAuthenticated()"> <a class="item" th:href="@{/toLogin}"> <i class="address card icon"></i> Log in </a> </div> <!--If logged in, display username and log out--> <div sec:authorize="isAuthenticated()"> <a class="item" th:href="@{/logout}"> <!--get from authorization name--> username:<span sec:authentication="name"></span> <!-- Have bug Cannot be used, role:<span sec:authentication="principal.getAuthorities()"></span>--> </a> <a class="item" th:href="@{/logout}"> <i class="sign-out card icon"></i> log out </a> </div> </div>
-
login.html
<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> <div class="field"> <div class="field"> <input type="checkbox" name="remember"> remember me </div> </div> <input type="submit" class="ui blue submit button"/> </form>
6,Shiro
Apache Shiro is a security framework for Java. At present, more and more people use Apache Shiro because it is quite simple. Compared with Spring Security, it may not be as powerful as Spring Security, but it may not require such complex things in actual work, so use the small and simple Shiro is enough As for which of the two is better, there is no need to tangle, it would be better to solve the project problem more easily.
- Three core objects: user Subject, management user SecurityManager, connection data Realms
- Subject: the "current operating user". However, in Shiro, the concept of Subject does not just refer to a person, but can also be a third-party process, a Daemon Account, or other similar things. It simply means "what is currently interacting with the software".
- SecurityManager: It is the core of Shiro framework, typical Facade mode, Shiro manages internal component instances through SecurityManager, and provides various services for security management through it.
- Realm: Realm acts as a "bridge" or "connector" between Shiro and application security data. That is, when authentication (login) and authorization (access control) are performed on a user, Shiro looks up the user and their permission information from the Realm configured by the application. In this sense, Realm is essentially a security-related DAO: it encapsulates the connection details of the data source and provides relevant data to Shiro when needed. When configuring Shiro, you must specify at least one Realm for authentication and/or authorization. It is possible to configure multiple Realm s, but at least one is required.
6.1. Shiro Quick Start
-
import dependencies
<!--Front-end interactive integration--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <!--Integrate Mybatis--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.22</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- shiro-spring --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.3</version> </dependency> <!--thymeleaf--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.5.3</version> </dependency>
-
write shiro.ini
[users] # user 'root' with password 'secret' and the 'admin' role root = secret, admin # user 'guest' with the password 'guest' and the 'guest' role guest = guest, guest # user 'presidentskroob' with password '12345' ("That's the same combination on # my luggage!!!" ;)), and role 'president' presidentskroob = 12345, president # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' darkhelmet = ludicrousspeed, darklord, schwartz # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' lonestarr = vespa, goodguy, schwartz # ----------------------------------------------------------------------------- # Roles with assigned permissions # # Each line conforms to the format defined in the # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc # ----------------------------------------------------------------------------- [roles] # 'admin' role has all permissions, indicated by the wildcard '*' admin = * # The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:* # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
-
Write the Quickstart class
public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { // The easiest way to create a Shiro SecurityManager with configured // realms, users, roles and permissions is to use the simple INI config. // We'll do that by using a factory that can ingest a .ini file and // return a SecurityManager instance: // Use the shiro.ini file at the root of the classpath // (file: and url: prefixes load from files and urls respectively): DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); IniRealm iniRealm=new IniRealm("classpath:shiro.ini"); defaultSecurityManager.setRealm(iniRealm); // for this simple example quickstart, make the SecurityManager // accessible as a JVM singleton. Most applications wouldn't do this // and instead rely on their container configuration or web.xml for // webapps. That is outside the scope of this simple quickstart, so // we'll just do the bare minimum so you can continue to get a feel // for things. SecurityUtils.setSecurityManager(defaultSecurityManager); // Now that a simple Shiro environment is set up, let's see what you can do: // get the currently executing user: Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String) session.getAttribute("someKey"); if (value.equals("aValue")) { log.info("Retrieved the correct value! [" + value + "]"); } // let's login the current user so we can check against roles and permissions: if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); token.setRememberMe(true); try { currentUser.login(token); } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } // ... catch more exceptions here (maybe custom ones specific to your application? catch (AuthenticationException ae) { //unexpected condition? error? } } //say who they are: //print their identifying principal (in this case, a username): log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //test a role: if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //test a typed permission (not instance-level) if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } //a (very powerful) Instance Level permission: if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //all done - log out! currentUser.logout(); System.exit(0); } }
-
Run the Quickstart class to print shiro basic information
[External link image transfer failed, the source site may have an anti-leech mechanism, it is recommended to save the image and upload it directly (img-4ybDkGtR-1659458588017)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220707155611813.png)]
6.2, springboot integrated shiro environment to build
-
import dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.23</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--introduce mybatis,This is MyBatis Officially provided adaptation spring Boot , instead of spring Boot my own--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> <scope>compile</scope> </dependency>
-
MyController
@Controller public class MyController { @RequestMapping({"/","/index"}) public String toIndex(Model model){ model.addAttribute("msg","hello,shiro"); return "index"; } @RequestMapping("/user/add") public String add(){ return "user/add"; } @RequestMapping("/user/update") public String update(){ return "user/update"; } @RequestMapping("/toLogin") public String toLogin(){ return "login"; } @RequestMapping("/login") public String login(String username,String password,Model model) { //get current user Subject subject = SecurityUtils.getSubject(); //Encapsulates the user's login data UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password); // Verify login try { // Perform login operation, call across classes subject.login(usernamePasswordToken); model.addAttribute("msg", "successfully logged in"); return "index"; } catch (UnknownAccountException uae) { model.addAttribute("msg", "username error"); return "login"; } catch (IncorrectCredentialsException ice) { model.addAttribute("msg", "wrong user password"); return "login"; } } @RequestMapping("/noauth") @ResponseBody public String unauthorized(){ return "Unauthorized access"; } }
-
ShiroConfig configuration class
@Configuration public class ShiroConfig { //Step 3: Create ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //Set up security manager bean.setSecurityManager(defaultWebSecurityManager); //Add shiro's built-in filter /* anon:Access without authentication authc:Must be authenticated to access user:Must have "remember me" feature to use perms:have permission to a resource to access role:Access to a certain role */ //intercept Map<String,String> filterMap = new LinkedHashMap<String,String>(); //Authorization, under normal circumstances, if there is no permission, it will jump to the unauthorized page filterMap.put("/user/add","perms[user:add]"); filterMap.put("/user/update","perms[user:update]"); filterMap.put("/user/*","authc"); bean.setFilterChainDefinitionMap(filterMap); //set login request bean.setLoginUrl("/toLogin"); //Set up unauthorized page bean.setUnauthorizedUrl("/noauth"); return bean; } //Step 2: Create DefaultWebSecurityManager @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //Associate UserRealm securityManager.setRealm(userRealm); return securityManager; } //Step 1: Create a realm object Custom @Bean public UserRealm userRealm(){ return new UserRealm(); } //Integrate ShiroDialect for integrating Shiro and Thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } }
-
UserRealm class required by shiro configuration class
//Custom Inherit AuthorizingRealm public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; //Authorize protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //This is hard-coded. As long as the user comes in after successful authentication, the permission given here means that all users have this permission. // info.addStringPermission("user:add"); //Get the currently logged in object Subject subject = SecurityUtils.getSubject(); User currentUser = (User) subject.getPrincipal();//get the user object //Set permissions for the current user info.addStringPermission(currentUser.getPerms()); return info; } //Certification protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // Find out the username and password in the simulated database UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; User user = userService.queryUserByName(userToken.getUsername()); if(user == null){//no such person return null; } Subject currentSubject= SecurityUtils.getSubject(); Session session = currentSubject.getSession(); session.setAttribute("loginUser",user); // Password verification, shiro is completed, no user judgment is required. Return directly return new SimpleAuthenticationInfo(user, user.getPwd(), ""); } }
-
application.yaml
# Integrate data sources spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chacacterEncoding=utf-8&serverTimezone=UTC # Springboot uses com.mysql.cj.jdbc.Driver for Mysql8 and above, 5 may have bug s driver-class-name: com.mysql.cj.jdbc.Driver # Integrate mybatis mybatis: type-aliases-package: com.luffy.shirospringboot.pojo # Solve the binding exception: mapper.xml should be consistent with the package name path of the interface mapper-locations: classpath:mapper/*.xml #SpringBoot does not inject these by default, and needs to be bound by itself #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 filters for monitoring statistics interception, stat: monitoring statistics, log4j: logging, wall: defense against sql injection #If an error is allowed, java.lang.ClassNotFoundException: org.apache.Log4j.Properity #Just import log4j dependencies filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionoProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
-
index.html
<!DOCTYPE html> <html lang="en" xmlns:shiro="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>home</h1> <div th:if="${session.loginUser==null}"> <a th:href="@{/toLogin}">Log in</a> </div> <p th:text="${msg}"></p> <div shiro:hasPermission="user:add"> <a th:href="@{/user/add}">add</a> </div> <div shiro:hasPermission="user:update"> <a th:href="@{/user/update}">update</a> </div> </body> </html>
-
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>login</h1> <p th:text="${msg}" style="color: red"></p> <form th:action="@{/login}"> <p>username:<input type="text" name="username"></p> <p>password:<input type="text" name="password"></p> <p>LOGIN<input type="submit"></p> </form> </body> </html>
7,Swagger
7.1. Introduction
swagger is known as the most popular API framework in the world. API documents and API definitions are updated synchronously, run directly, and support multiple languages.
Using swagger in your project requires Springfox (version 2.x):
- swagger2
- swagger-ui
7.2. Integration
-
import dependencies
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency>
-
controller class
@RestController public class HelloController { @RequestMapping(value = "/hello") public String hello(){ return "hello"; } }
-
Write SwaggerConfig configuration class
@Configuration @EnableSwagger2 //Open Swagger2 public class SwaggerConfig { }
-
Access: http://localhost:port number/swagger-ui/index.html, (note that the address accessed in swagger2.x version is http://localhost:port number/swagger-ui.html), you can access Swagger Home
-
If the Swagger3.0 version does not match Springboot, the following is the matching version number (if you don't want to reduce the Springboot version number, there are other solutions to Baidu)
Spring Boot version Swagger version 2.5.6 2.9.2
7.3, configure Swagger information
-
config configuration class added
@Bean public Docket docket() { return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()); } //Configure document information private ApiInfo apiInfo() { Contact contact = new Contact("Luffy", "http://www.baidu.com/", "2234179482@qq.com"); return new ApiInfo( "Swagger study", // title "Learn how to configure Swagger", // describe "v1.0", // Version "http://terms.service.url/organization link", // organization link contact, // contact information "Apach 2.0 license", // license "License link", // License connection new ArrayList<>()// expand ); }
7.4, configure the scan interface and switch
-
Configure how to scan the interface through the select() method when building a Docket
@Bean public Docket docket(Environment environment) { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(false) //Configure whether to enable Swagger, if it is false, it will not be accessible in the browser .select()// Through the .select() method, configure the scanning interface, RequestHandlerSelectors configures how to scan the interface .apis(RequestHandlerSelectors.basePackage("com.luffy.swaggerdemo.controller")) // .paths(PathSelectors.ant("/luffy/swaggerdemo/**")) .build(); // All scan methods of RequestHandlerSelectors: // any() // Scan all, all interfaces in the project will be scanned // none() // do not scan the interface // // Scan through annotations on the method, such as withMethodAnnotation(GetMapping.class) only scans get requests // withMethodAnnotation(final Class<? extends Annotation> annotation) // // Scan through annotations on classes, such as .withClassAnnotation(Controller.class) only scans interfaces in classes annotated with controller // withClassAnnotation(final Class<? extends Annotation> annotation) // basePackage(final String basePackage) // Scan interface based on package path // paths(PathSelectors.ant("/luffy/swaggerdemo/**")) //What path to filter: filter all paths under /luffy/swaggerdemo }
7.5. Swagger is used in the production environment and not used in the release (problem)
-
Configure several different properties environments, the environment used in the overall properties configuration
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-9RHfSspM-1659458588018)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220712123806470.png)]
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-7Jioetta-1659458588018)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220712123853933.png)]
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-VTkWrKKb-1659458588019)(C:\Users341\AppData\Roaming\Typora\typora-user-images\ image-20220712123907210.png)]
-
The config configuration class determines whether to open swagger according to whether the displayed environment can find the returned boolean value and provide it to the enable method
@Bean public Docket docket(Environment environment) { // Set the environment in which to display swagger Profiles of = Profiles.of("dev", "test"); // Determine whether you are currently in this environment // Receive this parameter through enable() to determine whether to display boolean flag = environment.acceptsProfiles(of); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(flag) //Configure whether to enable Swagger, if it is false, it will not be accessible in the browser .select()// Through the .select() method, configure the scanning interface, RequestHandlerSelectors configures how to scan the interface .apis(RequestHandlerSelectors.basePackage("com.luffy.swaggerdemo.controller")) // .paths(PathSelectors.ant("/luffy/swaggerdemo/**")) .build(); // All scan methods of RequestHandlerSelectors: // any() // Scan all, all interfaces in the project will be scanned // none() // do not scan the interface // // Scan through annotations on the method, such as withMethodAnnotation(GetMapping.class) only scans get requests // withMethodAnnotation(final Class<? extends Annotation> annotation) // // Scan through annotations on classes, such as .withClassAnnotation(Controller.class) only scans interfaces in classes annotated with controller // withClassAnnotation(final Class<? extends Annotation> annotation) // basePackage(final String basePackage) // Scan interface based on package path // paths(PathSelectors.ant("/luffy/swaggerdemo/**")) //What path to filter: filter all paths under /luffy/swaggerdemo }
8. Asynchronous, timing, mail tasks
8.1. Asynchronous tasks
-
create a service
@Service public class AsyncService { @Async public void hello(){ try { Thread.sleep(3000); }catch (Exception e){ } System.out.println("Data is being processed!"); } }
-
write controller
@RestController public class AsyncController { @Autowired private AsyncService asyncService; @RequestMapping("/hello") public String hello(){ asyncService.hello(); return "OK!"; } }
-
SpringbootTaskApplication main startup class
@EnableAsync @SpringBootApplication public class Springboot09TestApplication { public static void main(String[] args) { SpringApplication.run(Springboot09TestApplication.class, args); } }
-
@EnableAsync annotation: start asynchronous
-
@Async annotation: mark a method as asynchronous
-
Generally, after a request is executed, it will wait for the return result to be successful before returning to the corresponding interface. An asynchronous task is to return to the successful interface first and then continue to execute the request task.
8.2. Mail tasks
-
import dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
application.properties file configuration
spring.mail.username=2234179482@qq.com spring.mail.password=dvfdnthbsjjreaei spring.mail.host=smtp.qq.com # qq needs to configure ssl spring.mail.properties.mail.smtp.ssl.enable=true
-
test class
@SpringBootTest class Springboot09TestApplicationTests { public static void main(String[] args) { } @Autowired JavaMailSenderImpl mailSender; @Test public void contextLoads() { //Mail Setup 1: A Simple Mail SimpleMailMessage message = new SimpleMailMessage(); message.setSubject("Notice-Come to Mad God tomorrow to listen to the class"); message.setText("7 tonight:30 meeting"); message.setTo("2234179482@qq.com"); message.setFrom("2234179482@qq.com"); mailSender.send(message); } @Test public void contextLoads2() throws MessagingException { //Mail Setup 2: A Complex Mail MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); helper.setSubject("Notice-Come to Mad God tomorrow to listen to the class"); helper.setText("<b style='color:red'>today 7:30 come to a meeting</b>",true); //send attachments helper.addAttachment("1.jpg",new File("E:\\picture\\1.jpg")); helper.setTo("2234179482@qq.com"); helper.setFrom("2234179482@qq.com"); mailSender.send(mimeMessage); } }
8.3. Scheduled tasks
In project development, it is often necessary to perform some scheduled tasks. For example, it is necessary to analyze the log information of the previous day in the early morning of each day. Spring provides us with a way of asynchronously executing task scheduling and provides two interfaces.
- TaskExecutor interface
- TaskScheduler interface
Two annotations:
- @EnableScheduling
- @Scheduled
How to use:
-
Springboot09TestApplication
@EnableAsync//Enable asynchronous annotation @EnableScheduling //Enable annotation-based scheduled tasks @SpringBootApplication public class Springboot09TestApplication { public static void main(String[] args) { SpringApplication.run(Springboot09TestApplication.class, args); } }
-
Service class
@Service public class ScheduledService { //second minute hour day month day of week //0 * * * * MON-FRI //Note the usage of cron expressions; @Scheduled(cron = "0/2 * * * * ?") public void hello(){ System.out.println("hello....."); } /* (1)0/2 * * * * ? Indicates that the task is executed every 2 seconds (1)0 0/2 * * * ? Indicates that the task is executed every 2 minutes (1)0 0 2 1 * ? Indicates that the task is adjusted at 2:00 AM on the 1st of each month (2)0 15 10 ? * MON-FRI Indicates that the job is executed at 10:15 am every day from Monday to Friday (3)0 15 10 ? 6L 2002-2006 Indicates that the operation is executed at 10:15 am on the last Friday of each month from 2002 to 2006 (4)0 0 10,14,16 * * ? 10am, 2pm, 4pm daily (5)0 0/30 9-17 * * ? Every half hour during 9 to 5 working hours (6)0 0 12 ? * WED Means every Wednesday at 12 noon (7)0 0 12 * * ? Trigger every day at 12 noon (8)0 15 10 ? * * Trigger every day at 10:15am (9)0 15 10 * * ? Trigger every day at 10:15am (10)0 15 10 * * ? Trigger every day at 10:15am (11)0 15 10 * * ? 2005 2005 Triggered at 10:15 am every day of the year (12)0 * 14 * * ? Fires every 1 minute between 2pm and 2:59pm every day (13)0 0/5 14 * * ? Fires every 5 minutes between 2pm and 2:55pm every day (14)0 0/5 14,18 * * ? Fires every 5 minutes between 2pm and 2:55pm and every 5 minutes between 6pm and 6:55pm (15)0 0-5 14 * * ? Fires every 1 minute between 2pm and 2:05pm every day (16)0 10,44 14 ? 3 WED Every March Wednesday at 2:10pm and 2:44pm (17)0 15 10 ? * MON-FRI Triggered at 10:15 a.m. Monday-Friday (18)0 15 10 15 * ? Triggered at 10:15 am on the 15th of every month (19)0 15 10 L * ? Triggered at 10:15 AM on the last day of the month (20)0 15 10 ? * 6L Fires on the last Friday of the month at 10:15 AM (21)0 15 10 ? * 6L 2002-2005 2002 Fired at 10:15 am on the last Friday of every month from year to 2005 (22)0 15 10 ? * 6#3 Fires at 10:15 AM on the third Friday of the month */ }
9. Dubbo and Zookeeper integration
9.1. What is a distributed system?
-
A distributed system is a collection of independent computers that appear to users as a single related system
-
A distributed system is a software system built on a network. It is precisely because of the characteristics of software that distributed systems are highly cohesive and transparent. Therefore, the difference between a network and a distributed system is more in the high-level software (especially the operating system) than in the hardware.
9.2. Install Zookeeper
-
URL: https://zookeeper.apache.org/ (download with bin)
-
Run /bin/zkServer.cmd, the first run will report an error (flashback), there is no zoo.cfg configuration file under conf
-
Copy the zoo_sample.cfg under the conf folder and rename it to zoo.cfg
-
Run /bin/zkServer.cmd (server)
-
Run zkCli.cmd (client)
-
Enter on the client: ls / (list all nodes saved under the zookeeper root)
-
Enter on the client: create -e /luffy 123 (create a luffy node with a value of 123)
-
Enter on the client: get /luffy (get the value of the /luffy node)
9.3. Install Dubbo-admin
dubbo itself is not a service software. It is actually a jar package that can help your java program connect to zookeeper, and use zookeeper to consume and provide services.
However, in order to allow users to better manage and monitor many dubbo services, the official provides a visual monitoring program dubbo-admin, but this monitoring does not affect the use even if it is not installed.
Instructions
-
download dubbo-admin
- https://github.com/stackXu/dubbo-admin
-
Unzip into the directory
- Modify dubbo-admin\src\main\resources \application.properties to specify the zookeeper address
-
Package dubbo-admin in the Dubbo-admin project directory
- mvn clean package -Dmaven.test.skip=true
-
Execute dubbo-admin-0.0.1-SNAPSHOT.jar under dubbo-admin\target
-
The download URL given here has a jar package directly. If the configurations of zookeeper and dubbo are the same, you can run the jar package directly
-
Run cmd in the jar package directory: java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
-
Note: the zookeeper service needs to be turned on
-
-
After all execution, visit http://localhost:7001/ , enter the login account and password, the default is root-root, you can display the dubbo-admin interface
9.4. Actual combat of service registration discovery
1. Start zookeeper!
2. IDEA creates an empty project;
3. Create a module to implement the service provider: provider-server, just select the web dependency
Ticket selling interface
package com.luffy.service; public interface TicketService { public String getTicket(); }
Ticket sales implementation class
package com.luffy.service; public class TicketServiceImpl implements TicketService { @Override public String getTicket() { return "<Mad God says Java>"; } }
provider dependent
<!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.3</version> </dependency> <!-- 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>
provider application.properties
#current application name dubbo.application.name=provider-server #Registration center address dubbo.registry.address=zookeeper://127.0.0.1:2181 #Scan the services under the specified package dubbo.scan.base-packages=com.luffy.service
4. Create a module to implement service consumers: consumer-server, just select web dependencies
5. After the project is created, we write a service, such as the user's service
consumer reliance
<!--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>
consumer application.properties
#current application name dubbo.application.name=consumer-server #Registration center address dubbo.registry.address=zookeeper://127.0.0.1:2181
Consumer service class
package com.luffy.service; import com.guo.provider.service.TicketService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.stereotype.Service; @Service //inject into the container public class UserService { @Reference //When referring to the specified service remotely, he will match it according to the full class name to see who has registered the full class name with the registry. TicketService ticketService; public void buyTicket(){ String ticket = ticketService.getTicket(); System.out.println("buy at the registry"+ticket); } }
6. Write test classes
@RunWith(SpringRunner.class) @SpringBootTest public class ConsumerServerApplicationTests { @Autowired UserService userService; @Test public void contextLoads() { userService.buyTicket(); } }