SpringBoot 学习

微服务

什么是微服务

微服务是一种架构风格,要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合,可以通过http的方式进行沟通

单体应用架构

所谓单体应用架构(all in one)是指,我们将一个应用中的所有应用服务都封装在一个应用中

无论是ERP,CRM或是其他什么系统,你都把数据库访问,web访问,等等各个功能放到一个war包内

这样做的好处是易于开发和测试,也十分方便部署,当需要扩展时,只需要将war包复制多份,然后放到多个服务器上,再做个负载均衡就可以了

单体应用架构的缺点是:修改需要停掉整个服务,重新打包,部署这个应用war包

微服务架构

all in one的架构方式,我们把所有的功能单元放在一个应用里面,然后我们把整个应用部署到服务器上,如果负载能力不行,我们将整个应用进行水平复制,进行扩展,然后再负载均衡

所谓微服务架构,就是打破之前all in one的架构方式,把每个功能元素独立出来,把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些时可以整合多个功能元素,所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制

好处:

节省了调用资源

每个功能元素的服务都是一个可替换的,可独立升级的软件代码

第一个springboot程序

官方:提供了一个快速生成的网站

原理初探

自动配置:

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中

  • 我们在写或者引入一些springboot依赖的时候,不需要指定版本,就因为有这些版本仓库

启动器:

<!--启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
  • 启动器:就是springboot的启动场景
  • 比如spring-boot-starter-web,就会帮我们自动导入web环境所有的依赖
  • springboot会将所有的功能场景,都变成一个个的启动器,
  • 我们要使用什么功能,就只需要找到对应的启动器就可以了,

主程序:

//SpringBootApplication:标注这个类是一个SpringBoot的应用
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        
        //将SpringBoot应用启动
        SpringApplication.run(DemoApplication.class, args);
    }

}
  • 注解:

    • @SpringBootConfiguration:springboot的配置
      	@Configuration:spring配置类
      		@Component:说明这也是一个spring的组件
      	
      	
      	
      	
      	
      	
      @EnableAutoConfiguration:自动配置
      	@AutoConfigurationPackage:自动配置包
      		@Import(AutoConfigurationPackages.Registrar.class):自动配置‘包注册’
      	@Import(AutoConfigurationImportSelector.class):自动配置导入选择
      
      //获取所有的配置
      List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
      
      
      

获取候选的配置

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
META-INF/spring.factories:自动配置的核心文件

自动配置原理分析:

@SpringBootApplication--》@SpringBootConfiguration:@Configuration,@EnableAutoConfiguration:自动导入包,@ComponentScan:扫描当前主启动类同级的包

@EnableAutoConfiguration:

  • @AutoConfigurationPackage :@Import(AutoConfigurationPackages.Registrar.class):自动注册表

  • @Import(AutoConfigurationImportSelector.class:自动导入包的核心)

    • getAutoConfigurationEntry()获得自动配置的实体

    • getCandidateConfigurations():获取候选的配置

    • public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
         String factoryTypeName = factoryType.getName();
         return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
      }
      //获取所有的加载配置
      loadSpringFactories
      	classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :项目资源  META-INF/spring.factories:自动配置的核心文件
      	ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));系统资源
      	
      	从这些资源中遍历了所有的nextElement(自动配置),遍历完成之后封装成为Properties供我们使用
      

核心注解:@ConditionOnXXX:如果这里面的条件都满足,才会生效

结论:springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功

1.Springboot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值

2.将自动配置的 类导入容器,自动配置就会生效,帮我们进行自动配置

3.以前我们需要自动配置的东西,现在springboot帮我们做了

4.整个javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.3.4.RELEASE.jar这个包下

5.它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器

6.容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件并自动配置,@Configuration,JavaConfig

7.有了自动配置类,免去了手动配置编写的工作

主启动类怎么运行

//SpringBootApplication:标注这个类是一个SpringBoot的应用
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {

        //将SpringBoot应用启动
        //该方法返回一个configurableApplicationContext对象
        //参数一:应用入口的类,参数类:命令行参数
        SpringApplication.run(DemoApplication.class, args);
    }

}

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行

SpringApplication

  • 这个类主要做了以下四件事情,
  • 推断应用的类型是普通的项目还是Web项目
  • 查找并加载所有可用初始化器,设置到initializers属性
  • 找出所有的应用程序监听器,设置到listeners属性中
  • 推断并设置main方法的定义类,找到运行的主类
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

yaml语法

springboot配置

官方配置太多

了解原理

springboot使用一个全局的配置文件,配置文件名称时固定的

application.properties

  • 语法结构:key=value

application.yml

  • 语法结构:key:空格value

配置文件的作用:修改springboot自动配置的默认值,因为springboot在底层 都给我们自动配置好了

#普通的key-value
name: ming
# 对象
student:
  name: ming
  age: 3

#行内写法
student: {name: ming,age: 3}

#数组
pets:
  - cat
  - dog

pets: [cat,dog,pig]

给属性赋值的几种方式

yaml可以直接给实体类赋值

student:
  name: ming
  age: 3

在注入的类上使用注解@ConfigurationProperties

需要配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

加载指定的properties文件

@PropertySource(value=“classpath:ming.properties”)

  • @value(“${name}”)

JSR303校验

spring-boot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理

导入包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@Validated//数据校验
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;
    @Email()
    private String email;
}

多环境配置及配置文件位置

可以选择激活哪一个配置文件

spring.profile.active=dev.properties
server:
	port: 8082
spring:
  profiles:
    active: dev
----
server:
	port: 8083
spring:
	profiles: dev

自动配置原理

  • springboot在启动时会加载大量的自动配置类
  • 看需要的功能有没有在springboot默认写好的自动配置类当中
  • 看这个自动配置类中到底配置了哪些组件
  • 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,只需要在配置文件中指定这些属性的值即可

xxxAutoConfiguration:自动配置类:给容器中添加组件

xxxProperties:封装配置文件中相关属性

@Conditonal

自动配置类必须在一定的条件下才能生效

web开发探究

jar:webapp

自动装配:

springboot帮我们配置了什么,我们能不能进行修改,能修改哪些东西

  • xxxAutoConfiguration:向容器中自动装配组件
  • xxxProperties:自动装配类,装配配置文件中自定义的一些内容

要解决的问题

  • 导入静态资源
  • 首页
  • jsp,模板引擎Thymeleaf
  • 装配扩展Springmvc
  • 增删改查
  • 拦截器
  • 国际化

静态资源导入探究

什么是webjars

https://www.webjars.org/

第一种引入jquery

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

第二种

resources下的目录:public,static,resources 下的文件都能被直接访问到,localhost:8080/1.js

优先级:resources>static(默认)>public

第三中自定义:

配置文件中:spring.mvc.static-path-pattern=/ming/**

则其他的失效

总结:在springboot中,可以使用以下方式处理静态资源

首页定制

第一种:在资源路径下命名为index.html

第二种:

package com.company.demo.controller;


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


//在templates目录下的所有页面,只能通过controller来跳转
//这个需要模板引擎的支持thymeleaf依赖
@Controller
public class IndexController {

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

thymeleaf模板引擎

导入依赖

<dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>

结论:只要需要使用thymeleaf,只需要导入对应的依赖就可以了,将html放在templates目录下,就可以访问了

package com.company.demo.controller;


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


//在templates目录下的所有页面,只能通过controller来跳转
//这个需要模板引擎的支持thymeleaf依赖
@Controller
public class IndexController {

    @RequestMapping("/test")
    public String index(Model model){

        model.addAttribute("msg","hello,springboot");
        return "test";
    }
}

templates/test.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--所有的html元素都可以被thymeleaf替换接管  th:元素名-->
<h1 th:text="${msg}"></h1>
</body>
</html>

Thymeleaf语法

th:utext(带转义)

th:text(不专一)

th:each(遍历)

<h1 th:each="user:${users}" th:text="${user}"></h1>

th:if

MVC配置原理

配置config包下新建类,实现WebMvcConfigurer接口

package com.company.demo.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Locale;


//如果,你向diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//扩展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    //ViewResolver 实现了视图解析器接口的类,我们就可以把它看作视图解析器

    @Bean
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }



    //自定义了一个自己的视图解析器MyViewResolver
    public static class MyViewResolver implements ViewResolver{

        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            return null;
        }
    }
}

扩展SpringMVC

application.yml

#自定义的配置日期格式化
spring:
  mvc:
    format:
      date: dd/MM/yyyy

SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@Bean)如果有就用用户自己配置的,如果没有就用自动配置的,如果有些组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来

package com.company.demo.config;

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

//如果我们要扩展springmvc,官方建议我们这样做
@Configuration
public class NewMvcConfig implements WebMvcConfigurer {


    //视图跳转  跳转到test.html
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

        registry.addViewController("/ming").setViewName("test");
    }
}

员工管理系统:准备工作

静态资源导入

数据库

pojo层,两个表类

package com.company.demo.pojo;


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

//部门表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {

    private Integer id;
    private String departmentName;

}
package com.company.demo.pojo;


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

import java.util.Date;

//员工表
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;//0:女,1:男

    private Department department;
    private Date birth;
}
package com.company.demo.dao;


import com.company.demo.pojo.Department;
import org.springframework.stereotype.Repository;

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

//部门dao
@Repository
public class DepartmentDao {



    //模拟数据库中的数据
    private static Map<Integer, Department> departments = null;
    static{
        departments  = new HashMap<>();//创建一个部门表
        departments.put(101,new Department(101,"教学部"));
        departments.put(102,new Department(102,"市场部"));
        departments.put(103,new Department(103,"教研部"));
        departments.put(104,new Department(104,"运营部"));
        departments.put(105,new Department(105,"后勤部"));
    }

    //获得所有部门信息
    public Collection<Department> getDepartment(){
        return departments.values();
    }

    //通过id得到部门
    public Department getDepartmentById(Integer id){
        return departments.get(id);
    }
}

dao层,表查询类

package com.company.demo.dao;

import com.company.demo.pojo.Department;
import com.company.demo.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

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


//员工dao
@Repository
public class EmployeeDao {

    //模拟数据库中的数据
    private static Map<Integer, Employee> employees = null;

    //员工有所属的部门
    @Autowired
    private DepartmentDao departmentDao;
    static{
        employees  = new HashMap<>();//创建一个部门表
        employees.put(101,new Employee(101,"AA","A101@qq.com",0,new Department(101,"教学部")));
        employees.put(102,new Employee(102,"BB","B102@qq.com",1,new Department(102,"市场部")));
        employees.put(103,new Employee(103,"CC","C103@qq.com",0,new Department(103,"教研部")));
        employees.put(104,new Employee(104,"DD","D104@qq.com",1,new Department(104,"运营部")));
        employees.put(105,new Employee(105,"EE","E105@qq.com",0,new Department(105,"后勤部")));
    }

    //主键自增
    private static Integer initId = 1006;
    //增加一个员工
    public void save(Employee employee){
        if(employee.getId()==null){
            employee.setId(initId++);
        }
        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        employees.put(employee.getId(),employee);
    }

    //查询全部员工信息
    public Collection<Employee> getAll(){
        return employees.values();
    }

    //通过id查询员工
    public Employee getEmployeeById(Integer id){
        return employees.get(id);
    }
    
    //删除员工通过id
    public void delete(Integer id){
        employees.remove(id);
    }
}

员工管理系统:首页实现

控制层,访问首页

package com.company.demo.config;


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


//如果,你向diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//扩展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}

html导入thymeleaf命名空间

th:href="@{/ff/ff.css}"

<html lang="en" xmlns:th="http://www.thymeleaf.org">

首页配置:所有页面的静态资源都需要是哟个thymeleaf接管;@{}

首页国际化:中英文切换

  • 配置i18n文件
  • 如果需要在项目中进行按钮自动切换,需要自定义一个组件LocaleResolver
  • 记得将自己写的组件配置到spring容器,@Bean
  • ‘#{}’

login_en_US.properties

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

login_zh_CN.properties

login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名

login.properties

login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名

index.html:thymeleaf接管,#{取值}

<!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" th:text="#{login.tip}"></h1>
         <label class="sr-only" th:text="#{login.username}"></label>
         <input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
         <label class="sr-only" th:text="#{login.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')}">中文</a>
         <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
      </form>

   </body>

</html>

地区解析器

package com.company.demo.config;

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

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

public class MyLocaleResolver implements LocaleResolver {

    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        //获取请求中的语言参数
        String language = request.getParameter("l");
        Locale locale = Locale.getDefault();//如果没有就使用默认的
        //如果请求的链接携带了国际化的参数
        if(!StringUtils.isEmpty(language)){
            //zh_CN
            String[] s = language.split("_");
            //国家_地区
            locale = new Locale(s[0], s[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

注册bean

package com.company.demo.config;


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


//如果,你向diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//扩展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }

    //自定义的国际化组件就生效了
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}

员工管理系统:登录实现

登录控制器

package com.company.demo.controller;

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

@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)){
            return "redirect:/main.html";//重定向
        }
        else{
            model.addAttribute("msg","用户名或者密码错误");
            return "index";
        }
    }
}

解析器中增加url,视图解析

package com.company.demo.config;


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


//如果,你向diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//扩展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/main.html").setViewName("dashboard");
    }

    //自定义的国际化组件就生效了
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}

如果错误,给出提示

<p style="color:red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

员工管理系统:登录拦截器

登录后添加session

package com.company.demo.controller;

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

import javax.servlet.http.HttpSession;

@Controller
public class LoginController {

    @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);
            return "redirect:/main.html";
        }
        else{
            //告诉用户,你登陆失败了
            model.addAttribute("msg","用户名或者密码错误");
            return "index";
        }
    }
}

拦截器

package com.company.demo.config;

import org.springframework.web.servlet.HandlerInterceptor;

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

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        
        //登录成功之后,应该有用户的session
        Object loginUer = request.getSession().getAttribute("loginUer");
        if(loginUer==null){
            //没有登录
            request.setAttribute("msg","没有权限,请先登录");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else{
            return true;
        }
        
    }

    
}

配置拦截器

package com.company.demo.config;


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


//如果,你向diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//扩展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/main.html").setViewName("dashboard");
    }

    //自定义的国际化组件就生效了
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/css/**","/js/**","/img/**");
    }
}

员工管理系统:展示员工列表

前端

抽取侧边栏

<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 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" 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>
               员工管理
            </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>
<!--侧边栏-->
<div th:insert="~{dashboard::sidebar}"></div>

员工列表展示:

  • 提取公共页面
<div th:insert="~{dashboard::sidebar}"></div>
<div th:replace="~{dashboard::sidebar}"></div>
<--!传参数!-->
<div th:insert="~{dashboard::sidebar(active='1.html')}"></div>

controller

package com.company.demo.controller;


import com.company.demo.dao.EmployeeDao;
import com.company.demo.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Collection;

@Controller
public class EmployeeController {

    //
    @Autowired
    EmployeeDao employeeDao;

    @RequestMapping("/emps")
    public  String list(Model model){
        Collection<Employee> employees = employeeDao.getAll();
        model.addAttribute("emps",employees);
        return "/emp/list";
    }
}

th:each 取值

<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()}"></td>
									<td th:text="${emp.department.getDepartmentName()}"></td>
									<td th:text="${emp.getBirth()}"></td>
								</tr>

列表循环展示

<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?'女':'男'}"></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">编辑</button>
         <button class="btn btn-sm btn-danger">删除</button>
      </td>
   </tr>
</tbody>

员工管理系统:增加员工实现

按钮提交

跳转到添加页面

添加员工成功

返回首页

<form th:action="@{/emp}" th:method="post">
   <div class="form-group">
      <label >LastName</label>
      <input type="text" class="form-control" name="lastName" placeholder="Email">
   </div>
   <div class="form-group">
      <label >Email</label>
      <input type="email" class="form-control" name="email" placeholder="Email">
   </div>
   <div class="form-group">
      <label >Gender</label>
      <div class="form-check form-check-inline">
         <input class="form-check-input" type="radio" name="gender" value="1">
         <laber class="form-check-label">男</laber>
      </div>
      <div class="form-check form-check-inline">
         <input class="form-check-input" type="radio" name="gender" value="1">
         <laber class="form-check-label">女</laber>
      </div>
   </div>
   <div class="form-group">
      <label >Department</label>
      <select class="form-control" name="department.id">
         <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>

      </select>
   </div>
   <div class="form-group">
      <label >Birth</label>
      <input type="text" class="form-control" name="birth" placeholder="ming">
   </div>
   <button type="submit" class="btn btn-primary">添加</button>
</form>

controller

@GetMapping("/emp")
public String toAddPage(Model model){
    //查出所有部门信息
    Collection<Department> departments = departmentDao.getDepartment();
    model.addAttribute("departments",departments);
    return "emp/add";
}

@PostMapping("/emp")
public String addEmp(Employee employee){
    employeeDao.save(employee);//保存员工信息

    return "redirect:/emps";
}

注意spring.mvc.data-format的默认样式:yyyy-MM-dd

<select class="form-control" name="department.id">
   <!--我们在controller接收的是一个Employee,所以我们需要提交的是其中的一个属性-->
   <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>

</select>

员工管理系统:修改员工信息

前端

<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑</a>

controller

//去员工的修改页面
@GetMapping("/emp/{id}")
public String toUpdateEmp(@PathVariable("id")Integer id, Model model){
    //查出原来的数据
    Employee employee = employeeDao.getEmployeeById(id);
    model.addAttribute("emp",employee);
    Collection<Department> departments = departmentDao.getDepartment();
    model.addAttribute("departments",departments);
    return "emp/update";
}

//员工信息修改
@PostMapping("/updateEmp")
public String updateEmp(Employee employee){
    employeeDao.save(employee);
    return "redirect:/emps";
}

修改页面

<form th:action="@{/updateEmp}" th:method="post">
   <input type="hidden" name="id" th:value="${emp.getId()}">
   <div class="form-group">
      <label >LastName</label>
      <input type="text" th:value="${emp.getLastName()}" class="form-control" name="lastName" >
   </div>
   <div class="form-group">
      <label >Email</label>
      <input type="email" th:value="${emp.getEmail()}"class="form-control" name="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" name="gender" value="1">
         <laber class="form-check-label">男</laber>
      </div>
      <div class="form-check form-check-inline">
         <input th:checked="${emp.getGender()==0}" class="form-check-input" type="radio" name="gender" value="1">
         <laber class="form-check-label">女</laber>
      </div>
   </div>
   <div class="form-group">
      <label >Department</label>
      <select class="form-control" name="department.id">
         <!--我们在controller接收的是一个Employee,所以我们需要提交的是其中的一个属性-->
         <option th:selected="${department.getId()==emp.getDepartment().getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>

      </select>
   </div>
   <div class="form-group">
      <label >Birth</label>
      <input type="text" th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" class="form-control" name="birth" placeholder="ming">
   </div>
   <button type="submit" class="btn btn-primary">修改</button>
</form>

员工管理系统:删除及404处理

//删除员工
@GetMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id") Integer id){
    employeeDao.delete(id);
    return "redirect:/emps";
}
<a class="btn btn-sm btn-danger" th:href="@{/delemp/}+${emp.getId()}">删除</a>

把404.html放到error文件夹下就可以

员工管理系统:注销

<a class="nav-link" th:href="@{user/logout}">注销</a>
//注销
@GetMapping("/user/logout")
public String logout(HttpSession session){
    session.invalidate();
    return "redirect:/index.html";
}

如何写一个网站

前端:

  • 模板:别人写好的,我们拿来改成自己需要的
  • 框架:组件:自己手动组合拼接 Bootstrap Layui Semantic-ui,
    • 栅格系统
    • 导航栏
    • 侧边栏
    • 表单

快速搭建网站

  • 前端搞定:页面长什么样子:数据
  • 设计数据库(数据库设计难点)
  • 前端让它能够自动运行,独立化工程
  • 数据接口如何对接,json,对象 all in one
  • 前后端联调测试

有一套自己熟悉的后台模板,自己熟悉的

x-admin模板

前端界面:至少自己能够通过前端框架,组合出来一个网站页面

  • index
  • about
  • blog
  • post
  • user

让网站能够独立运行

整合JDBC使用

Spring Data

对应数据访问层,无论是SQL(关系型数据库)还是NOSQL(非关系型数据库),SpringBoot底层都是采用SpringData的方式进行统一处理

SpringBoot底层都是采用SpringData 的方式进行统一处理各种数据库,Spring Data也是Spring中与SpringBoot,SpringCloud等齐名的知名项目

Spring Data官网:https://spring.io/projects/spring-data

新建一个JDBC项目

  • spring-initializer 选择SQL 的JDBC 以及Mysql Driver
  • 创建项目

yml配置数据源

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver

查看数据源

package com.company;

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

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootTest
class Springboot04ApplicationTests {

    @Autowired
    DataSource dataSource;
    @Test
    void contextLoads() throws SQLException {

        //查看默认数据源  com.zaxxer.hikari.HikariDataSource
        System.out.println(dataSource.getClass());
        //获得数据库连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        
        
        //xxxx Template:SpringBoot已经配置好模板bean,拿来即用CRUD
        //jdbc 
        //redis
        connection.close();
    }

}

测试

package com.company.controller;


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

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;

@RestController
public class JDBCController {


    @Autowired
    JdbcTemplate jdbcTemplate;

    //查询数据库的所有信息
    //没有实体类,数据库中的东西,怎么获取,Map
    @GetMapping("/userList")
    public List<Map<String,Object>> userList(){
        String sql = "select * from 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 (4,'小明','e2ekrfj')";
        jdbcTemplate.update(sql);
        return "addUser-ok";
    }

    @GetMapping("/updateUser/{id}")
    public String updateUser(@PathVariable("id") int id){
        String sql = "update mybatis.user set name=?,pwd=? where id= "+id;
        //封装
        Object[] objects = new Object[2];
        objects[0]="dd";
        objects[1]="ggg";
        jdbcTemplate.update(sql,objects);
        return "updateUser-ok";
    }

    @GetMapping("/deleteUser/{id}")
    public String deleteUser(@PathVariable("id") int id){
        String sql = "delete from mybatis.user where id = ?";
        jdbcTemplate.update(sql, id);
        return "deleteUser-ok";
    }
}

整合Druid数据源

Druid简介

Druid是阿里巴巴开源平台上一个数据库连接池实现,结合了C3P0,DBCP,PROXOOL等DB池的优点,同时加入了日志监控

Druid可以很好的监控DB池连接和SQL的执行情况,天生就是针对监控而生的DB连接池

SpringBoot2.0以上默认使用Hikari数据源,可以说Hikari与Druid都是当前JavaWeb上最优秀的数据源

SpringBoot如何集成Druid数据源,如何实现数据库监控

导入依赖

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

yml改变数据源 type

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

测试,发现数据源已经改变

package com.company;

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

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootTest
class Springboot04ApplicationTests {

    @Autowired
    DataSource dataSource;
    @Test
    void contextLoads() throws SQLException {

        //查看默认数据源  com.zaxxer.hikari.HikariDataSource
        System.out.println(dataSource.getClass());
        //获得数据库连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        //xxxx Template:SpringBoot已经配置好模板bean,拿来即用CRUD
        //jdbc
        //redis

        connection.close();
    }

}

Druid的专有配置

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
      # 下面为连接池的补充设置,应用到上面所有数据源中
      # 初始化大小,最小,最大
    initial-size: 5
    min-idle: 5
    max-active: 20
      # 配置获取连接等待超时的时间
    max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
    min-evictable-idle-time-millis: 300000
    validation-query: SELECT 1 FROM DUAL
    test-while-idle: true
    test-on-borrow: false
    test-on-return: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
    pool-prepared-statements: true
      #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    max-pool-prepared-statement-per-connection-size: 20
    #配置监控统计拦截的filters,stat:监控统计,log4j:日志记录,wall:防御sql注入
    #如果允许时报错,java.lang.ClassNotFoundException:org.apache.log4j.Priority
    #则导入log4j依赖即可,Maven地址:https://mvnrepository.com//artifact/log4j/log4j
    filters: stat,wall,log4j
    use-global-data-source-stat: true
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

druid配置

进入localhost:/druid/

可以看到后台监控

package com.company.config;


import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;

@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource(){

        return new DruidDataSource();
    }

    //后台监控:web.xml,ServletRegistrationBean
     //因为SpringBoot内置了servlet容器,所以没有web.xml,替代方法:ServletRegistrationBean
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        //后台需要有人登录,账号密码配置
        HashMap<String, String> initParameters = new HashMap<>();
        //增加配置

        initParameters.put("loginUsername","admin");//登录key是固定的
        initParameters.put("loginPassword","123456");
        //允许谁可以访问
        initParameters.put("allow","");
        //禁止谁可以访问
//        initParameters.put("ming", "193,23.33.33");
        bean.setInitParameters(initParameters);//设置初始化参数
        return bean;
    }
    
    
    //filter
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();

        bean.setFilter(new WebStatFilter());
        //可以过滤哪些请求呢

        HashMap<String, String> initParameters = new HashMap<>();
        //这些东西不进行统计
        initParameters.put("exclussion","*.js,*.css,/druid/*");
        bean.setInitParameters(initParameters);
        return bean;
    }
}
log4j:WARN No appenders could be found for logger (druid.sql.Connection).
log4j:WARN Please initialize the log4j system properly.
    //未配置log4j配置文件
    //在resources下新建log4j.properties,并写入配置

整合mybatis框架

mybatis-spring-boot-starter

导入依赖

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver



#整合mybatis
mybatis.type-aliases-package=com.company.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

pojo

package com.company.pojo;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

mapper

package com.company.mapper;


import com.company.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

//这个注解表示了这是一个mybatis的mapper类
@Mapper
@Repository
public interface UserMapper {



    List<User> queryList();

    User queryUserById(int id);

    int addUser(User user);

    int updateUser(User user);

    int deleteUser(int id);
}

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.company.mapper.UserMapper">

    <select id="queryList" resultType="User">
        select * from user
    </select>

    <select id="queryUserById" resultType="User">
        select * from user where id = #{id}
    </select>

    <insert id="addUser" parameterType="User">
        insert into user(id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>

    <update id="updateUser" parameterType="User">
        update user set name=#{name},pwd=#{pwd} where id=#{id}
    </update>

    <delete id="deleteUser" parameterType="User">
        delete from user  where id=#{id}
    </delete>
</mapper>

测试controller

package com.company.controller;


import com.company.mapper.UserMapper;
import com.company.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @GetMapping("/queryUserList")
    public List<User> queryList(){
        List<User> users = userMapper.queryList();
        return users;
    }
}

步骤

  • 导入包
  • 配置文件
  • mybatis配置
  • 编写sql
  • service调用dao层
  • controller层调用service层

SpringSecurity

在web开发中,安全第一位!过滤器,拦截器~

功能性需求:否

做网站:安全应该在什么时候考虑

  • 漏洞,隐私泄露

  • 架构一旦缺点

shiro,SpringSecurity:很像,除了类不一样,名字不一样

认证,授权(vip1,vip2,vip3)

  • 功能权限
  • 访问权限
  • 菜单权限
  • 拦截器,过滤器:大量的原生代码~冗余

导入themeleaf

<dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>

简单路由控制

package com.company.controller;


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

@Controller
public class RouterController {

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

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

用户认证和授权

Spring Security是针对Spring项目的安全框架,也是SpringBoot底层安全模块默认的技术选型,它可以实现强大的web安全控制,对于安全控制,我们仅需要引入spring-boot-starter-security模块,进行少量的配置,即可实现强大的安全管理

记住几个类:

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

Spring Security的两个主要目标是认证和授权(访问控制)

认证(Authentication)

授权(Authorization)

这个概念是通用的,而不是在SpringSecurity中存在

导入spring-boot-start-security

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

安全设置

package com.company.config;


import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;


//AOP 拦截器
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //链式编程
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //首页所有人可以访问,功能页只有对应有权限的人才能访问
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("VIP1")
                .antMatchers("/level2/**").hasRole("VIP2")
                .antMatchers("/level3/**").hasRole("VIP3");
        //没有权限默认会到登录页面,需要开启登录的页面
        //   /login  security的登录页面
        http.formLogin();
        //防止网站攻击:get,post
        http.csrf().disable();
        //注销.开启了注销功能,跳到首页
http.logout().logoutSuccessUrl("/");
    }

    //认证
    //密码编码:PasswordEncoder
    //在spring Security  5.0+  新增了很多的加密方法
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        /*auth.jdbcAuthentication()
                .dataSource(dataSource)
                .withDefaultSchema()
                .withUser()*/
        //这些数据正常应该从数据库中读
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("ming").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP2","VIP3")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1","VIP2","VIP3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1");
    }
}

注销及权限控制

前端用到了semantic-ui样式

<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">

在web授权函数里开启注销功能

//注销.开启了注销功能,跳到首页
http.logout().logoutSuccessUrl("/");

前端

<!--注销-->
<a class="item" th:href="@{/logout}">
    <i class="sign-out icon"></i> 注销
</a>

导入security-thymeleaf整合包

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!--未登录-->
<div sec:authorize="!isAuthenticated()">
    <a class="item" th:href="@{/toLogin}">
        <i class="address card icon"></i> 登录
    </a>
</div>

<!--如果登录:用户名,注销-->
<div sec:authorize="isAuthenticated()">
    <a class="item">
        用户名:<span sec:authentication="name"></span>
        角色:<span sec:authentication="principal.authorities"></span>
    </a>
</div>
<div sec:authorize="isAuthenticated()">
    <!--如果登录:用户名,注销-->
    <a class="item" th:href="@{/logout}">
        <i class="sign-out icon"></i> 注销
    </a>
</div>

登录失败的原因,开启了csrf

//防止网站攻击:get,post
        http.csrf().disable();

判断是否有权限

<!--菜单根据用户的角色动态的实现-->
<div class="column" sec:authorize="hasRole('VIP2')">
</div>

分条件展示前端页面

记住我及首页定制

protected void configure(HttpSecurity http) throws Exception {
    //首页所有人可以访问,功能页只有对应有权限的人才能访问
    http.authorizeRequests().antMatchers("/").permitAll()
            .antMatchers("/level1/**").hasRole("VIP1")
            .antMatchers("/level2/**").hasRole("VIP2")
            .antMatchers("/level3/**").hasRole("VIP3");
    //没有权限默认会到登录页面,需要开启登录的页面
    //   /login  security的登录页面
    http.formLogin();

    //防止网站攻击:get,post
    http.csrf().disable();
    //注销.开启了注销功能,跳到首页
    http.logout().logoutSuccessUrl("/");

    //开启记住我功能
    http.rememberMe();
}
//开启记住我功能 cookie  默认保存两周 
http.rememberMe();

关闭浏览器,还会有缓存记住用户名及密码,有效期14天

首页定制

改到自己的login页面

//自己定制的表单要与security的表单参数一致 http.formLogin().loginPage("/toLogin").usernameParameter("username").passwordParameter("password").loginProcessingUrl("/login");
//开启记住我功能 cookie  默认保存两周  自定义接收前端的参数
http.rememberMe().rememberMeParameter("remember");

shiro快速开始

Apache Shiro是一个Java的安全(权限)框架

Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境

Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等

  • 导入依赖

  • <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.0</version>
        </dependency>
    
        <!--configure logging-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.30</version>
        </dependency>
    </dependencies>
    
  • 配置文件

    ​ log4j.properties

    ​ shiro.ini

  • HelloWorld

官网QuickStart 有demo可以看

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;

import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Simple Quickstart application showing how to use Shiro's API.
 *
 * @since 0.9 RC2
 */
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):
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        // 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(securityManager);

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

Shiro的Subject分析

shiro.ini

#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
# =============================================================================
# Quickstart INI Realm configuration
#
# For those that might not understand the references in this file, the
# definitions are all based on the classic Mel Brooks' film "Spaceballs". ;)
# =============================================================================

# -----------------------------------------------------------------------------
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[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

QuickStart

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;

import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Simple Quickstart application showing how to use Shiro's API.
 *
 * @since 0.9 RC2
 */
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):
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        // 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(securityManager);

        // Now that a simple Shiro environment is set up, let's see what you can do:

        // get the currently executing user:
        //获取当前的用户对象Subject
        Subject currentUser = SecurityUtils.getSubject();

        // Do some stuff with a Session (no need for a web or EJB container!!!)
        //通过当前用户拿到session
        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()) {

            //Token:令牌
            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);
    }
}
//获取当前的用户对象Subject
Subject currentUser = SecurityUtils.getSubject();


//通过当前用户拿到session
        Session session = currentUser.getSession();
        
        //当前用户是否被认证
        currentUser.isAuthenticated()
        
        currentUser.getPrincipal()
        
        currentUser.hasRole("schwartz")
        currentUser.isPermitted("lightsaber:wield")
        
        
        currentUser.logout();

SpringBoot整合Shiro环境搭建

导入shiro-springboot整合包

<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.7.0</version>
</dependency>

UserRealm

package com.company.config;


import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了--》授权doGETAuthorizationInfo");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了--》认证doGETAuthenticationInfo");
        return null;
    }
}

ShiroConfig

package com.company.config;


import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        return  bean;
    }
    //DefaultWebSecurityManager  2
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //创建realm对象,需要自定义 1

    @Bean(name="userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
}

myController

package com.company.controller;


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

@Controller
public class MyController {

    @RequestMapping({"/","/index"})
    public String hello(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";
    }
}

shiro实现登录拦截

shiroconfig

package com.company.config;


import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro的内置过滤器
        /*
        * anon:无需认证即可访问
        * authc:必须认证了才能访问
        * user:必须拥有 记住我 功能才能用
        * perms:拥有对某个资源的权限才能访问
        * role:拥有某个角色权限才能访问
        * */

        Map<String,String> filterMap = new LinkedHashMap<>();
        /*filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");*/
        filterMap.put("/user/*","authc");

        bean.setFilterChainDefinitionMap(filterMap);
        //设置登录的请求
        bean.setLoginUrl("/toLogin");
        return  bean;
    }
    //DefaultWebSecurityManager  2
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //创建realm对象,需要自定义 1

    @Bean(name="userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
}

Shiro实现用户认证

controller /login

package com.company.controller;


import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController {

    @RequestMapping({"/","/index"})
    public String hello(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){
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();

        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);

        try{
            subject.login(token);//执行登录方法,如果没有异常就说明ok了
            return "index";
        }catch (UnknownAccountException e){//用户名不存在
            model.addAttribute("msg","用户名错误");
            return "login";

        }catch (IncorrectCredentialsException e){//密码不存在
            model.addAttribute("msg","密码错误");
            return "login";

        }


    }

}

UserRealm

package com.company.config;


import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了--》授权doGETAuthorizationInfo");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了--》认证doGETAuthenticationInfo");


        //用户名,密码~            数据库取

        String name="root";
        String password = "123456";
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        if(!userToken.getUsername().equals(name)){
            return null;//抛出异常  UnknownAccountException
        }
        //密码认证,shiro做~
        return new SimpleAuthenticationInfo("",password,"");
    }
}

shiro整合mybatis

导入依赖包

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>8.0.21</version>
</dependency>

<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.2.2</version>
</dependency>

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

pojo

package com.company.pojo;


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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private int id;
    private String name;
    private String pwd;
}

userMapper

package com.company.mapper;

import com.company.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface UserMapper {

    User queryUserByName(String name);
}

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.company.mapper.UserMapper">

    <select id="queryUserByName" resultType="User" parameterType="String">
        select * from user where name=#{name}
    </select>


</mapper>

数据源配置

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
      # 下面为连接池的补充设置,应用到上面所有数据源中
    # 初始化大小,最小,最大
    initial-size: 5
    min-idle: 5
    max-active: 20
    # 配置获取连接等待超时的时间
    max-wait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    time-between-eviction-runs-millis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    min-evictable-idle-time-millis: 300000
    validation-query: SELECT 1 FROM DUAL
    test-while-idle: true
    test-on-borrow: false
    test-on-return: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    pool-prepared-statements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    max-pool-prepared-statement-per-connection-size: 20
    #配置监控统计拦截的filters,stat:监控统计,log4j:日志记录,wall:防御sql注入
    #如果允许时报错,java.lang.ClassNotFoundException:org.apache.log4j.Priority
    #则导入log4j依赖即可,Maven地址:https://mvnrepository.com//artifact/log4j/log4j
    filters: stat,wall,log4j
    use-global-data-source-stat: true
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

mybatis配置

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

userRealm

package com.company.config;


import com.company.pojo.User;
import com.company.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了--》授权doGETAuthorizationInfo");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了--》认证doGETAuthenticationInfo");





        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        //连接真实的数据库
        User user = userService.queryUserByName(userToken.getUsername());
        if(user==null){
            return null;//抛出异常  UnknownAccountException
        }

        //可以加密:MD5,MD5盐值加密
        //密码认证,shiro做~   加密了
        return new SimpleAuthenticationInfo("",user.getPwd(),"");
    }
}

shiro请求授权实现

shiroconfig 拦截

package com.company.config;


import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro的内置过滤器
        /*
        * anon:无需认证即可访问
        * authc:必须认证了才能访问
        * user:必须拥有 记住我 功能才能用
        * perms:拥有对某个资源的权限才能访问
        * role:拥有某个角色权限才能访问
        * */

        //拦截
        Map<String,String> filterMap = new LinkedHashMap<>();
        /*filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");*/


        //授权

        filterMap.put("/user/add","perms[user:add]");

        filterMap.put("/user/*","authc");

        bean.setFilterChainDefinitionMap(filterMap);
        //设置登录的请求
        bean.setLoginUrl("/toLogin");
        
        //未授权页面
        bean.setUnauthorizedUrl("/noauth");
        return  bean;
    }
    //DefaultWebSecurityManager  2
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //创建realm对象,需要自定义 1

    @Bean(name="userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
}
//未授权页面
        bean.setUnauthorizedUrl("/noauth");

//授权 正常情况下,没有授权会跳转到未授权页面
        filterMap.put("/user/add","perms[user:add]");

controller里

@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
    return "未经授权无法访问此页面";
}

user里面增加perms字段

package com.company.pojo;


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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private int id;
    private String name;
    private String pwd;
    private String perms;
}

userRealm

package com.company.config;


import com.company.pojo.User;
import com.company.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了--》授权doGETAuthorizationInfo");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();


//        info.addStringPermission("user:add");

        //拿到当前登录的对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser= (User) subject.getPrincipal();//拿到用户对象

        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());


        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了--》认证doGETAuthenticationInfo");





        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        //连接真实的数据库
        User user = userService.queryUserByName(userToken.getUsername());
        if(user==null){
            return null;//抛出异常  UnknownAccountException
        }

        //可以加密:MD5,MD5盐值加密
        //密码认证,shiro做~   加密了
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}

shiro整合Thymeleaf

导入依赖包

<!--shiro-Thymeleaf整合-->
<dependency>
   <groupId>com.github.theborakompanioni</groupId>
   <artifactId>thymeleaf-extras-shiro</artifactId>
   <version>2.0.0</version>
</dependency>

shiroConfig

package com.company.config;


import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro的内置过滤器
        /*
        * anon:无需认证即可访问
        * authc:必须认证了才能访问
        * user:必须拥有 记住我 功能才能用
        * perms:拥有对某个资源的权限才能访问
        * role:拥有某个角色权限才能访问
        * */

        //拦截
        Map<String,String> filterMap = new LinkedHashMap<>();
        /*filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");*/


        //授权 正常情况下,没有授权会跳转到未授权页面
        filterMap.put("/user/add","perms[user:add]");

        filterMap.put("/user/update","perms[user:update]");
        filterMap.put("/user/*","authc");

        bean.setFilterChainDefinitionMap(filterMap);
        //设置登录的请求
        bean.setLoginUrl("/toLogin");

        //未授权页面
        bean.setUnauthorizedUrl("/noauth");
        return  bean;
    }
    //DefaultWebSecurityManager  2
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //创建realm对象,需要自定义 1

    @Bean(name="userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }


    //整合shiroDialect:用来整合shiro thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
}

//整合shiroDialect:用来整合shiro thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

userRealm 登陆后加入session

package com.company.config;


import com.company.pojo.User;
import com.company.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了--》授权doGETAuthorizationInfo");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();


//        info.addStringPermission("user:add");

        //拿到当前登录的对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser= (User) subject.getPrincipal();//拿到用户对象

        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());


        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了--》认证doGETAuthenticationInfo");





        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        //连接真实的数据库
        User user = userService.queryUserByName(userToken.getUsername());
        if(user==null){
            return null;//抛出异常  UnknownAccountException
        }

        Subject currentSubject = SecurityUtils.getSubject();
        Session session = currentSubject.getSession();
        session.setAttribute("loginUser",user);
        //可以加密:MD5,MD5盐值加密
        //密码认证,shiro做~   加密了
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}

前端分权限显示页面

<!DOCTYPE html >
<html lang="en" xmlns:th="http://www.thymeleaf.org"
                xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
<!--从session中判断值-->
<div th:if="${session.loginUser==null}">
    <a th:href="@{/toLogin}">登录</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>

鸡汤分析开源项目

https://github.com/WinterChenS/my-site.git

七牛云云计算和数据服务提供商

layUI前端框架

自己设计一个博客系统

Swagger

学习目标:

  • 了解Swagger的作用和概念
  • 了解前后端分离
  • 在SpringBoot中集成Swagger

简介

Vue+SpringBoot

后端时代:前端只用管理静态页面 html--》后端。模板引擎 JSP -》后端是主力

前后端分离时代:

  • 后端:后端控制层,服务层,数据访问层【后端团队】

  • 前端:前端控制层,视图层【前端团队】

    • 伪造后端数据,json,已经存在了,不需要后端,前端工程依旧能够跑起来
  • 前后端如何交互?--》API

  • 前后端相对独立,松耦合

  • 前后端甚至都可以部署在不同的服务器上

产生一个问题:

  • 前后端集成联调,前端人员和后端人员无法做到,‘及时协商,尽早解决’,最终导致问题集中爆发

解决方案:

  • 首先制定schema【计划的提纲】,实时更新最新的API,降低集成的风险
  • 早些年:指定word计划文档,
  • 前后端分离:
    • 前端测试后端接口:postman
    • 后端提供接口,需要实时更新最新的消息及改动

Swagger

  • 号称世界上最流行的API框架

  • RestFul Api 文档在线自动生成工具--》Api文档与API定义同时更新

  • 直接运行,可以在线测试API接口:

  • 支持多种语言:(Java,Php。。。)

官网:https://swagger.io/

在项目使用Swagger需要springbox

  • swagger2
  • ui

SpringBoot集成Swagger

  1. 新建一个SpringBoot-web项目

  2. 导入相关依赖

     <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>3.0.0</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <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>
    
    
  3. 编写一个Hello工程

  4. 配置Swagger==》Config

    package com.company.config;
    
    
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    @Configuration
    @EnableSwagger2  //开启Swagger2
    public class SwaggerConfig {
    
        
    }
    

    测试运行

    访问:http://localhost:8080/swagger-ui/

    原 swagger-ui.html 已变更,
    当前版本url http://localhost:8080/swagger-ui/http://localhost:8080/swagger-ui/index.html

页面显示:

  • Swagger信息
  • 接口信息
  • 实体类信息

配置Swagger

Swagger的bean实例Docket

package com.company.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

@Configuration
@EnableSwagger2  //开启Swagger2
public class SwaggerConfig {


    //配置了Swagger的Docket的Bean实例
    @Bean
    public Docket docket(){



        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo());
    }

    //配置Swagger 信息=apiInfo
    private ApiInfo apiInfo(){

        Contact contact = new Contact("", "", ""); //作者信息
        return new ApiInfo(
                "Ming's Api Documentation",
                "Api Documentation",
                "1.0",
                "urn:tos",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());

    }


}

配置扫描接口及开关

Docket.select()

package com.company.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

@Configuration
@EnableSwagger2  //开启Swagger2
public class SwaggerConfig {


    //配置了Swagger的Docket的Bean实例
    @Bean
    public Docket docket(){



        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //RequestHandlerSelectors配置要扫描接口的方式
                //basePackage:指定要扫描的包
                //any():扫描全部
                //none():都不扫描
                //withClassAnnotation:扫描类上的注解,参数是一个注解的反射对象
                //withMethodAnnotation:扫描方法上的注解
                .apis(RequestHandlerSelectors.basePackage("com.company.controller"))
//                .apis(RequestHandlerSelectors.withMethodAnnotation(GetMapping.class))
                //paths() 指定扫描路径
                .paths(PathSelectors.ant("/ff/**"))
                .build();
    }

    //配置Swagger 信息=apiInfo
    private ApiInfo apiInfo(){

        Contact contact = new Contact("", "", ""); //作者信息
        return new ApiInfo(
                "Ming's Api Documentation",
                "Api Documentation",
                "1.0",
                "urn:tos",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());

    }


}

配置是否启动Swagger

enable(false)

false 不启动

package com.company.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

@Configuration
@EnableSwagger2  //开启Swagger2
public class SwaggerConfig {


    //配置了Swagger的Docket的Bean实例
    @Bean
    public Docket docket(){



        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(false)//enable 是否启动Swagger,如果未False,则Swagger不能再浏览器中访问
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.company.controller"))

                .build();
    }

    //配置Swagger 信息=apiInfo
    private ApiInfo apiInfo(){

        Contact contact = new Contact("", "", ""); //作者信息
        return new ApiInfo(
                "Ming's Api Documentation",
                "Api Documentation",
                "1.0",
                "urn:tos",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());

    }


}

问题:希望Swagger在生产环境中使用,在发布的时候不使用

  • 判断是不是生产环境 flag=false
  • 注入enable(flag)

配置三个properties文件

application-dev.properties

application-pro.properties

application.properties//确定激活哪个版本

spring.profiles.active=dev

利用Environment

package com.company.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

@Configuration
@EnableSwagger2  //开启Swagger2
public class SwaggerConfig {


    //配置了Swagger的Docket的Bean实例
    @Bean
    public Docket docket(Environment environment){

        //设置要显示的Swagger环境
        Profiles profiles = Profiles.of("dev","test");
        //获取项目的环境
        //通过environment.acceptsProfiles判断是否处在自己设定的环境当中
        boolean flag = environment.acceptsProfiles(profiles);


        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(flag)//enable 是否启动Swagger,如果未False,则Swagger不能再浏览器中访问
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.company.controller"))

                .build();
    }

    //配置Swagger 信息=apiInfo
    private ApiInfo apiInfo(){

        Contact contact = new Contact("name", "url", "email"); //作者信息
        return new ApiInfo(
                "Ming's Api Documentation",
                "Api Documentation",
                "1.0",
                "urn:tos",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());

    }


}

分组和接口注释及小结

Docket.groupName("")

.groupName("ming")

如何配置多个分组

  • 创建多个Docket实例

  •  @Bean
        public Docket docket1(){
            return new Docket(DocumentationType.SWAGGER_2)
                    .groupName("1");
        }
    
        @Bean
        public Docket docket2(){
            return new Docket(DocumentationType.SWAGGER_2)
                    .groupName("2");
        }
    
        @Bean
        public Docket docket3(){
            return new Docket(DocumentationType.SWAGGER_2)
                    .groupName("3");
        }
    

实体类配置

package com.company.pojo;


import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel("用户文档注释")
public class User {

    @ApiModelProperty("用户名")
    public String username;
    @ApiModelProperty("密码")
    public String password;

}

controller

package com.company.controller;


import com.company.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {


    @GetMapping(value = "/hello")
    public String hello(){
        return "hello";
    }


    //只要我们的接口中,返回值中存在实体类,他就会被扫描到Swagger中
    @PostMapping(value = "/user")
    public User user(){
        return new User();
    }
}

注释

package com.company.controller;


import com.company.pojo.User;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class HelloController {


    @GetMapping(value = "/hello")
    public String hello() {
        return "hello";
    }


    //只要我们的接口中,返回值中存在实体类,他就会被扫描到Swagger中
    @PostMapping(value = "/user")
    public User user() {
        return new User();
    }

        //Operation 接口,不是放在类上,是方法
    @ApiOperation("Hello控制类")
    @GetMapping("/hello2")
    public String hello2 (@ApiParam("用户名") String username){
            return "hello" + username;
    }

}

Swagger 后台 有Try it out 测试接口

总结:-

  • 我们可以通过Swagger给一些比较难理解的属性或者接口,增加注释信息
  • 接口文档实时更新
  • 可以在线测试

Swagger是一个优秀的工具,几乎所有大公司都有使用它

【注意点】

  • 在正式发布的时候,关闭Swagger,处于安全考虑,而且节省运行的内存

任务

异步任务~

定时任务~ timer

邮件发送~

异步任务

开启异步注解

package com.company;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;


//开启异步注解功能
@EnableAsync
@SpringBootApplication
public class Springboot09TestApplication {

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

}

异步方法,后台异步进行

package com.company.service;


import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {


    //告诉spring这是一个异步的方法

    @Async
    public void hello() throws InterruptedException {
        Thread.sleep(3000);
        System.out.println("数据正在处理。。。");
    }
}

controller调用

package com.company.controller;


import com.company.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AsyncController {

    @Autowired
    AsyncService asyncService;


    @RequestMapping("/hello")
    public String hello() throws InterruptedException {
        asyncService.hello();//停止3秒   转圈

        return "ok";
    }
}

邮件任务

导入spring-boot-starter-mail 包

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

配置

spring.mail.username=996287487@qq.com
spring.mail.password=xumlluvwfowhbcai
spring.mail.host=smtp.qq.com
#开启加密验证
spring.mail.properties.mail.smtp.ssl.enable=true

测试通过

package com.company;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;

@SpringBootTest
class Springboot09TestApplicationTests {

    @Autowired
    JavaMailSender mailSender;



    @Test
    void contextLoads() {

        //一个简单的邮件
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setSubject("小明你好呀~");
        mailMessage.setText("谢谢你的书");
        mailMessage.setTo("996287487@qq.com");
        mailMessage.setFrom("996287487@qq.com");
        mailSender.send(mailMessage);
    }

}

一个复杂的邮件

@Test
void contextLoads2() throws MessagingException {

    //一个复杂的邮件
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    //组装~

    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
    helper.setSubject("小明你好呀~");
    helper.setText("<p style='color:red'>谢谢你的书</p>",true);
    //附件

    helper.addAttachment("1.jpg",new File("src/main/resources/1.jpg"));

    helper.setTo("996287487@qq.com");
    helper.setFrom("996287487@qq.com");
    mailSender.send(mimeMessage);
}

定时执行任务

核心接口

TaskScheduler   任务调度
TaskExecutor    任务执行

@EnableScheduling  //开启定时功能的注解  


@Scheduled   //什么时候执行

cron表达式

开启注解

package com.company;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;



@EnableAsync//开启异步注解功能
@EnableScheduling  //开启定时功能的注解
@SpringBootApplication
public class Springboot09TestApplication {

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

}

测试,定时任务

package com.company.service;


import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class ScheduledService {


    //在一个特定的时间执行这行代码~  Timer

    /*
    * 30 15 10 * * ?   每天10点15分30 执行一次
    * 30 0/5 10,18 * * ? 每天10点和18点,每隔5分钟执一次
    * 
    * 0 15 10 ? * 1-6  每个月的周一到周六的10点15分执行
    * */

    //cron表达式
    //秒 分  时  日  月  星期
    @Scheduled(cron = "0 * * * * 0-7")
    public void hello(){
        System.out.println("你被执行了");
    }
}
posted @ 2020-11-09 14:26  yourText  阅读(157)  评论(2编辑  收藏  举报