SpringBoot

SpringBoot

主程序

//@SpringBootApplication 标注这个类是一个springboot的应用
@SpringBootApplication
public class Springboot01HellowordApplication {

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

}

注解@SpringBootApplication

@SpringBootApplication

标注这个类是一个springboot的应用

自动配置

​ 自动配置真正实现是从 classpath 中搜寻所有的 META-INF/spring.factories 配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration 的 JavaConfig 形式的 IOC 容器配置类 , 然后将这些都汇总成为一个实例并加载到 IOC 容器中。

结论:

  1. SpringBoot在启动的时候从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 ,自动配置类就生效 , 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 ,并配置好这些组件 ;
  5. 有了自动配置类 ,免去了我们手动编写配置注入功能组件等的工作;

SpringApplication

SpringApplication类主要做了以下4个事情:

  1. 推断应用的类型是普通的项目还是Web项目
  2. 查找并加载所有可用初始化器 , 设置到initializers属性中
  3. 找出所有的应用程序监听器,设置到listeners属性中
  4. 推断并设置main方法的定义类,找到运行的主类

yaml文件

yaml文件给属性赋值

给类中的属性赋值,用yaml文件

dog:
  name: wangjian
  age: 12

把yaml文件绑定到类中用@ConfigurationProperties(prefix = “dog”):

注意:必须有set方法

@ConfigurationProperties(prefix = "dog")
public class Dog {

    private String name;
    private int age;

        public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

占位符

dog:
  name: wangjian
  age: $(randon.int) #生成一个随机数

松散绑定

在yaml文件中用 - 连接属性名,java中驼峰命名,可以自动转换

yaml:dog-name

java:dogName

JSR303校验

@Validated开启数据校验

空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.

Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false

长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) Validates that the annotated string is between min and max included.

日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
@Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期
@Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。

数值检查
建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为”“,Integer为null
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=) 被指定的元素必须在合适的范围内
@Range(min=10000,max=50000,message=”range.bean.wage”)
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)

properties给属性赋值

  1. 用properties文件给属性赋值:键值对形式

  2. 把properties文件绑定到类中:用@PropertySource(value = “classpath:application.properties”),

    给value指定properties文件路径

  3. 用@Value( n a m e ) 给 属 性 映 射 值 , {name})给属性映射值, name)()spring的el表达式

@PropertySource(value = "classpath:application.properties")
public class Dog {

    @Value(${name})
    private String name;
    private int age;
}

多环境配置及配置文件

springboot加载文件的优先级访问位置

  1. 项目路径下的config文件夹配置文件
  2. 项目路径下配置文件
  3. 资源路径(classpath)下的config文件夹配置文件
  4. 资源路径(classpath)下配置文件

properties配置文件文件

如果有多个配置文件,可以用spring.profiles.active来选择配置文件

  • 比如有多个配置见文件,名称如下:

    • application-test.properties
    • application-dev.properties

    只需要这方法后面写出-后边的名就行,这就代表激活了这个配置文件

#springboot的多环境配置:可以选择激活哪一个配置文件
spring.profiles.active=test

yaml配置文件

可以实现多文档模块,用 — 三条杠分割多个配置

server:
	prot: 8080
#选择模块
spring:
	profiles: 
		active: dev
		#激活了下边的dev配置模块
	
	
---
server:
	prot: 8081
#可以起名
spring:
	profiles: dev
	
---
server:
	prot: 8082
spring:
	profiles: test

自动配置再理解

@Conditional派生注解

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置里的内用才生效.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyGAzcNP-1652586668987)(C:/Users/hasee/Pictures/哔哩哔哩动画/@Conditional派生注解,作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置里的内用才生效.png)]

spring.factories自动配置文件

spring里配置文件和META-INF/spring.factories的联系

配置文件中配置的所有东西都是META-INF/spring.factories里边的

在我们这配置文件中能配置的东西,都存在一个固有的规律:

xxxAutoConfiguration:默认值 xxxProperties和配置文件绑定,我们就可以使用自定义的配置了!

每一个xxxAutoConfiguration都是一个组件,加载到spring容器中

总结:根据当前不同的条件判断,决定这个配置类是否生效!

一旦这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;这样的话我们的配置文件就可以动态的修改springboot的一些配置内容。

精髓:

  1. springboot启动会加载大量的自动配置类;
  2. 我们看我们需要的功能有没有在springboot默认写好的自动配置类当中;
  3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
  4. 给容器中加载配置类添加组件的时候,会从properties类中获取某些属性。我们只需要再配置文件中指定这些属性的值即可;

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

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

SpringBoot Web开发

静态资源导入

在WebMvcAutoConfiguration中查看源码:

如果在配置文件中设置了静态资源导入路径,那么默认的路径就会失效

  1. 在springboot,我们可以使用以下方式处理静态资源
    • webjars localhost:8080/webjars
    • public,static,/**,resoutces localhost:8080/
  2. 优先级问题
    • resoutces > static > public

首页配置

注意点,所有页面的静态资源都需要使用thymeleaf接管;

路径用 url :@{}

国际化

  • 导入支持:xmlns:th=“http://www.thymeleaf.org”

切换中英文

  1. 创建文件 i18n 目录,代表国际化

  2. 创建三个子目录

    • login.properties 默认目录

      login.btn=登录
      login.password=密码
      login.remember=记住我
      login.tip=请登录
      login.username=用户名
      
    • 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=用户名
      

    或者idea左下角有Resource Bundle可视化创建

在页面中绑定:th:标签属性=“#{login.btn}”

<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>

或者直接在标签外边

[[#{login.remember}]]

看情况决定

前端接受参数

<a class="btn btn-sm" th:href="@{/index.html(language='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(language='en_US')}">English</a>

自定义一个组件,继承LocaleResolver,解析请求

package com.dragon.config;

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

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

public class MyLocaleResolver implements LocaleResolver {

    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        //获取请求中的语言参数
        String language = request.getParameter("language");
        System.out.println("q1232242");

        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 httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }
}

在mvc扩展中把组件配置spring容器中@Bean

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

注意:切记一个子也不能做配置的bean

总结:

  1. 我们需要配置i18n文件;
  2. 我们如果需要在项目中进行按钮切换,就要自定义一个组件,继承LocaleResolver
  3. 记得将自己写的组件配置到spring中@Bean,切记一个字母也不能错
  4. #{}

thymeleaf

简介:

Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎。

Thymeleaf的主要目标是为您的开发工作流程带来优雅的自然模板-HTML可以在浏览器中正确显示,也可以作为静态原型工作,从而可以在开发团队中加强协作。

Thymeleaf拥有用于Spring Framework的模块,与您喜欢的工具的大量集成以及插入您自己的功能的能力,对于现代HTML5 JVM Web开发而言,Thymeleaf是理想的选择-尽管它可以做很多事情。

导入依赖

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

显示后端传值

  • 导入支持:xmlns:th=“http://www.thymeleaf.org”
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--<h1 th:each="list:${msg}" th:text="${list}"></h1>-->
<h1 th:each="list:${msg}">[[${list}]]</h1>
</body>
</html>

提取公共页面

  1. th:fragment=“sidebar”
  2. th:replace=“~{commons/commons::topbar}”
  3. 如果要传递参数,可以直接使用()传参,接受判断即可

整合JDBC

导入依赖

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

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

在application配置文件中,配置数据库jdbc链接信息spring.datasource.。。。。。

查看datasource数据池

    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        System.out.println(dataSource.getClass());
        System.out.println("===============================");
        System.out.println(dataSource.getConnection());
    }

class com.zaxxer.hikari.HikariDataSource为springboot默认数据池

JdbcTemplate

    @Autowired
    JdbcTemplate jdbcTemplate;

    @RequestMapping("/userList")
    public List<Map<String,Object>> userList() {
        List<Map<String, Object>> list_maps = jdbcTemplate.queryForList("select * from mybatis.user");
        return list_maps;
    }

作用是执行一些crud语句,参数传一个sql语句就ok!

测试:

package com.dragon.jdbcController;

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

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

@RestController
public class jdbcController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    //查询全部
    @RequestMapping("/userList")
    public List<Map<String,Object>> userList() {
        List<Map<String, Object>> list_maps = jdbcTemplate.queryForList("select * from mybatis.user");
        return list_maps;
    }

    //增加
    @RequestMapping("/insertUser/{id}/{name}/{password}")
    public String insertUser(@PathVariable int id,@PathVariable String name,@PathVariable String password) {
        Object[] object = new Object[3];
        object[0] = id;
        object[1] = name;
        object[2] = password;

        int update = jdbcTemplate.update("insert into user (id,name,password) values (?,?,?)", object);
        if (update > 0) {
            return "insertUser-ok";
        } else {
            return "insertUser-no";
        }
    }

    //修改
    @RequestMapping("/updateUser/{id}/{name}/{password}")
    public String updateUser(@PathVariable int id,@PathVariable String name,@PathVariable String password) {
        Object[] object = new Object[3];
        object[0] = name;
        object[1] = password;
        object[2] = id;

        int update = jdbcTemplate.update("update user set name=?,password=? where id=?",object);

        if (update > 0) {
            return "updateUser-ok";
        } else {
            return "updateUser-no";
        }
    }

    //删除
    @RequestMapping("/deleteUser/{id}")
    public String deleteUser(@PathVariable int id) {
        Object[] object = new Object[1];
        object[0] = id;

        int update = jdbcTemplate.update("delete from user where id=?",object);

        if (update > 0) {
            return "deleteUser-ok";
        } else {
            return "deleteUser-no";
        }
    }
}

自定义数据源DruidDataSource

简介Druid

Java程序很大一部分要操作数据库,为了提高性能操作数据库的时候,又不得不使用数据库连接池。 Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入 了日志监控。

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

Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。

Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web 上最优秀的 数据源,我们来重点介绍 Spring Boot 如何集成 Druid 数据源,如何实现数据库监控。

导入依赖

        <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!--Log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

配置数据池设置

设置数据源连接初始化大小、最大连接数、等待时间、最小连接数 等设置项;可以查看源码

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

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

测试

现在需要程序员自己为 DruidDataSource 绑定全局配置文件中的参数,再添加到容器中,而不再使 用 Spring Boot 的自动生成了;我们需要 自己添加 DruidDataSource 组件到容器中,并绑定属性;

@SpringBootTest
class Springboot02DataApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        //看默认数据源
        System.out.println(dataSource.getClass());
        System.out.println("===============================");
        //得到连接
        Connection connection = dataSource.getConnection();
        DruidDataSource druidDataSource = (DruidDataSource) this.dataSource;
        System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
        System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());
        connection.close();
    }

}

配置 Druid 数据源监控

Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装 路由器 时,人家也提 供了一个默认的 web 页面。

package com.dragon.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;
import java.util.Map;

@Configuration
public class DruidConfig {

    ///绑定全局配置文件中druid数据源属性,到DruidDataSource中,从而让他们生效
    @ConfigurationProperties(prefix = "spring.datasource")
    //將自定的druid数据源添加到容器中,不在springboot自动创建
    @Bean
    public DataSource druidDatasource() {
        return new DruidDataSource();
    }

    //配置Druid监控管理后台的servlet
    //内置servlet容器时没有web.xml文件,所以使用springboot的注册selvlet
    @Bean
    public ServletRegistrationBean statViewServlet() {
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
        // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
        Map<String,String> initParams = new HashMap<>();
        initParams.put("loginUsername", "admin");//后台管理界面的登录账户
        initParams.put("loginPassword", "1234");//后台管理界面的登录密码

        //设置后台谁允许访问
        //initParams.put("allow", "localhost");只可以本机访问
        //initParams.put("allow", "");为空或者为null时,允许所有访问
        initParams.put("allow", "");
        //deny:拒绝谁访问
        //initParams.put("dragon", "192.168.1.1");

        //设置初始化参数
        bean.setInitParameters(initParams);
        return bean;
    }
}

配置完毕后,我们可以选择访问 : http://localhost:8080/druid/login.html

配置 Druid web 监控 filter 过滤器

        //WebStatFilter:用于配置Druid和web之间的管理管理监控系统
        @Bean
        public FilterRegistrationBean webStatFilter() {
            FilterRegistrationBean bean = new FilterRegistrationBean();
            bean.setFilter(new WebStatFilter());

            Map<String,String> initParams = new HashMap<>();

            //设置一些请求过滤排除,从而不进行统计
            initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
            bean.setInitParameters(initParams);

            //设置过滤所有请求
            bean.setUrlPatterns(Arrays.asList("/*"));
            return bean;
        }

整合Mybatis

导入依赖

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

mapper层

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

    List<User> queryAll();

    User getById(int id);
}

UserMapper.xml配置文件放在resources文件下,mybatis/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.dragon.mapper.UserMapper">

    <select id="queryAll" resultType="user">
        select * from user
    </select>

    <select id="getById" resultType="user">
        select * from user where id=#{id}
    </select>
</mapper>

Controller层文件

package com.dragon.controller;

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

import java.util.List;

@RestController
public class MyController {

    @Autowired
    UserMapper userMapper;

    @GetMapping("/queryAll")
    public List<User> queryAll() {
        List<User> list = userMapper.queryAll();
        for (User user : list) {
            System.out.println(user);
        }
        return list;
    }

    @GetMapping("/getById/{id}")
    public User getById(@PathVariable int id) {
        User byId = userMapper.getById(id);
        return byId;
    }
}

application配置文件

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

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5 #初始化时建立物理连接的个数,初始化发生在显示调用init方法,或者第一次getConnection时
    minIdle: 5 #最小连接池数量
    maxActive: 20 #最大连接池数量
    maxWait: 60000 #获取连接时最大等待时间,单位毫秒
    timeBetweenEvictionRunsMillis: 60000 #
    minEvictableIdleTimeMillis: 300000 #
    validationQuery: SELECT 1 FROM DUAL #用来检测连接是否有效的sql
    testWhileIdle: true #
    testOnBorrow: false #
    testOnReturn: false #
    poolPreparedStatements: true #是否缓存preparedStatement

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

mybatis:
  #mapper文件映射路径
  mapper-locations: classpath:mybatis/mapper/*.xml
  #实体类别名
  type-aliases-package: com.dragon.pojo

SpringSecurity(安全)

简介:

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

记住几个类:

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

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

“认证”(Authentication)

身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。

身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。

“授权” (Authorization)

授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置, 几乎任何内容)的完全权限。 简单来说,授权决定了您访问系统的能力以及达到的程度。

这个概念是通用的,而不是只在Spring Security 中存在。

自定义Security策略

继承WebSecurityConfigurerAdapter: 自定义Security策略

package com.dragon.config;

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

@EnableWebSecurity //开启Security模式
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 请求来到登录页
        // /login?error 重定向到这里表示登录失败
        http.formLogin();

        //开启自动配置的注销功能,/logout 注销请求
        //.logoutSuccessUrl("/");注销成功,跳转页面”/“
        http.logout().logoutSuccessUrl("/");
    }


    //定制认证权限规则
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        //在内存中拿,也可以去jdbc中拿
        //设置对应的用户可以访问的权限
        //Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
        //要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
        //spring security 官方推荐的是使用bcrypt加密方式。
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("dragon").password(new BCryptPasswordEncoder().encode("1234")).roles("vip1", "vip2").and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("1234")).roles("vip1", "vip2", "vip3").and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("1234")).roles("vip1");
    }
}

注意:密码必须进行加密

现在要加一些别的需求,用户拥有什么权限,页面就显示此用户的权限页面;

这样需要整合thymeleag-security,导入依赖

        <!--security-thymeleaf整合-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
            <version>3.0.2.RELEASE</version>
        </dependency>

修改前端页面即可;

  1. 导入命名空间:

    xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
    
  2. 修改导航栏,增加认证判断

    <!--登录注销-->
    <div class="right menu">
        <!--如果未登录,只显示下边内容-->
        <div sec:authorize="!isAuthenticated()">
            <a class="item" th:href="@{/login}">
                <i class="address card icon"></i> 登录
            </a>
        </div>
        <!--如果已登录,则显示内容-->
        <div sec:authorize="isAuthenticated()">
            <a class="item">
                <i class="address card icon"></i>
                用户名: <span sec:authentication="principal.username">
    </span>
                角色:<span sec:authentication="principal.authorities">
    </span>
            </a>
        </div>
        <div sec:authorize="isAuthenticated()">
            <a class="item" th:href="@{/logout}">
                <i class="address card icon"></i> 注销
            </a>
        </div>
    </div>
    
  3. 如果注销失败

    如果注销404了,就是因为它默认防止csrf跨站请求伪造,因为会产生安全问题,我们可以将请求 改为post表单提交,或者在spring security中关闭csrf功能;我们试试:在 配置中增加http.csrf().disable();

    http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout
    请求
    http.logout().logoutSuccessUrl("/");
    
  4. 继续将下边用户权限显示的功能认证完成!

    sec:authorize=“hasRole(‘vip1’)” 指定该模块为那个权限

            <div class="column" sec:authorize="hasRole('vip2')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon">
                            </i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon">
                            </i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon">
                            </i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>
    

记住我remember-me功能

现在的情况,我们只要登录之后,关闭浏览器,再登录,就会让我们重新登录,但是很多网站的情况, 就是有一个记住密码的功能,这个该如何实现呢?很简单

开启记住我功能

//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
    //。。。。。。。。。。。
    //记住我
    http.rememberMe();
}

完整配置代码

package com.dragon.config;

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

@EnableWebSecurity //开启Security模式
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 请求来到登录页
        // /login?error 重定向到这里表示登录失败
        http.formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginPage("/toLogin")
                .loginProcessingUrl("/login"); // 登陆表单提交请求
        
        
        //关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
        http.csrf().disable();		
        
        //开启自动配置的注销功能,/logout 注销请求
        //.logoutSuccessUrl("/");注销成功,跳转页面”/“
        http.logout().logoutSuccessUrl("/");
        //记住我
        http.rememberMe().rememberMeParameter("remember");
    }


    //定制认证权限规则
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        //在内存中拿,也可以去jdbc中拿
        //设置对应的用户可以访问的权限
        //Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
        //要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
        //spring security 官方推荐的是使用bcrypt加密方式。
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("dragon").password(new BCryptPasswordEncoder().encode("1234")).roles("vip1", "vip2").and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("1234")).roles("vip1", "vip2", "vip3").and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("1234")).roles("vip1");
    }
}

Shiro

简介:

什么是shiro?

  • Apache Shiro 是一个Java 的安全(权限)框架。
  • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。
  • Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。

Shiro具备的功能

  • Authentication:身份认证、登录,验证用户是不是拥有相应的身份;
  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否 进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!
  • Session Manager:会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都 在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;
  • Web Support:Web支持,可以非常容易的集成到Web环境;
  • Caching:缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高 效率
  • Concurrency:Shiro支持多线程应用的并发验证,即,如在一个线程中开启另一个线程,能把权限 自动的传播过去
  • Testing:提供测试支持;
  • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

Shiro架构(外部)

  • subject: 应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject, Subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其实是一个门面,SecurityManageer 才是实际的执行者
  • SecurityManager:安全管理器,即所有与安全有关的操作都会与SercurityManager交互,并且它管理着所有的Subject,可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
  • Realm:Shiro从Realm获取安全数据(如用户,角色,权限),就是说SecurityManager 要验证 用户身份,那么它需要从Realm 获取相应的用户进行比较,来确定用户的身份是否合法;也需要从 Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把Realm看成 DataSource;

Shiro架构(内部)

  • Subject:任何可以与应用交互的 ‘用户’;
  • Security Manager:相当于SpringMVC中的DispatcherServlet;是Shiro的心脏,所有具体的交互都通过Security Manager进行控制,它管理者所有的Subject,且负责进行认证,授权,会话,及缓存的管理。
  • Authenticator:负责Subject认证,是一个扩展点,可以自定义实现;可以使用认证策略 (Authentication Strategy),即什么情况下算用户认证通过了;
  • Authorizer:授权器,即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的那些功能;
  • Realm:可以有一个或者多个的realm,可以认为是安全实体数据源,即用于获取安全实体的,可以用JDBC实现,也可以是内存实现等等,由用户提供;所以一般在应用中都需要实现自己的realm
  • SessionManager:管理Session生命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在普通的JavaSE环境中
  • CacheManager:缓存控制器,来管理如用户,角色,权限等缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能;
  • Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于密码加密,解密等

整合Shiro

回顾核心API:

  1. Subject:用户主体
  2. SecurityManager:安全管理器
  3. Realm:Shiro 连接数据

导入依赖

<dependencies>
        <!--thymeleaf模板-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>

        <!--shiro-spring整合-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.0</version>
        </dependency>

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


        <!-- 引入 myBatis,这是 MyBatis官方提供的适配 Spring Boot 的,而不是SpringBoot自己的-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </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.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

application.yaml配置文件

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

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5 #初始化时建立物理连接的个数,初始化发生在显示调用init方法,或者第一次getConnection时
    minIdle: 5 #最小连接池数量
    maxActive: 20 #最大连接池数量
    maxWait: 60000 #获取连接时最大等待时间,单位毫秒
    timeBetweenEvictionRunsMillis: 60000 #
    minEvictableIdleTimeMillis: 300000 #
    validationQuery: SELECT 1 FROM DUAL #用来检测连接是否有效的sql
    testWhileIdle: true #
    testOnBorrow: false #
    testOnReturn: false #
    poolPreparedStatements: true #是否缓存preparedStatement

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

mybatis:
  #mapper文件映射路径
  mapper-locations: classpath:mybatis/mapper/*.xml
  #实体类别名
  type-aliases-package: com.dragon.pojo

Shiro配置类

package com.dragon.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
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        /*
        添加Shiro内置过滤器,常用的有如下过滤器:
        anon: 无需认证就可以访问
        authc: 必须认证才可以访问
        user: 如果使用了记住我功能就可以直接访问
        perms: 拥有权某个资源限才可以访问
        role: 拥有某个角色权限才可以访问
        */
        Map<String, String> filterMap = new LinkedHashMap<String, String>();

        //授权过滤器
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");

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

        //未授权的请求页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
        //跳转登录界面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        return shiroFilterFactoryBean;
    }

    //创建 DefaultWebSecurityManager
    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //关联realm
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }

    //创建 realm 对象
    @Bean(name = "userRealm")
    public UserRealm userRealm() {
        return new UserRealm();
    }

    配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }
}

设置Realm认证授权类

package com.dragon.config;

import com.dragon.pojo.User;
import com.dragon.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;

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

    @Autowired
    UserService userService;

    //执行授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行了=>授权逻辑PrincipalConllection");

        //给资源进行授权
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //添加资源授权字符串
        //simpleAuthorizationInfo.addStringPermission("user:add");

        //获取当前对象,得到user对象
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        simpleAuthorizationInfo.addStringPermission(user.getPerms());//设置权限
        return simpleAuthorizationInfo;
    }

    //执行认证逻辑
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=>认证逻辑AuthenticationToken");

        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        //真实连接数据库
        User user = userService.queryUserByName(userToken.getUsername());

        if (user == null) {
            //用户不存在!
            return null; //shiro底层就会抛出 UnknownAccountException
        }
        //我们在用户登录后应该把信息放到Session中
        Subject subject = SecurityUtils.getSubject();
        subject.getSession().setAttribute("loginUser",user);

        //验证密码,我们可以使用y一个AuthenticationInfo显示类SimpleAuthenticationInfo
        //shiro会自动帮我们验证!重点是第二个参数就是要验证的密码!
        //我们现在需要再自定义的授权认证中,获取登录的用户,从而实现动态认证授权操作!
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

实体类,Mapper,Service

package com.dragon.controller;

import org.apache.catalina.security.SecurityUtil;
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;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

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

    @RequestMapping("/user/add")
    public String toAdd(){
        return "user/add";
    }
    @RequestMapping("/user/update")
    public String toUpdate(){
        return "user/update";
    }

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

    //登录操作
    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
        //使用shiro编写认证操作
        //获取subject
        Subject subject = SecurityUtils.getSubject();
        //封装用户的数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //执行登录的方法,只要没异常就代表登录成功!
        try {
            subject.login(token);  //登录成功返回首页
            return "index";
        }catch (UnknownAccountException e){ //用户不存在异常
            model.addAttribute("msg","用户名不存在");
            return "login";
        }catch (IncorrectCredentialsException e) { //密码错误
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }

    @RequestMapping("/noauth")
    @ResponseBody
    public String noAuth(){
        return "未经授权不能访问此页面";
    }
}

@Repository
@Mapper
public interface UserMapper {
    User queryUserByName(String name);
}
<?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.dragon.dao.UserMapper">
    <select id="queryUserByName" parameterType="String" resultType="User">
    select * from user where name = #{name}
    </select>
</mapper>
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }

}

Controller

package com.dragon.controller;

import org.apache.catalina.security.SecurityUtil;
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;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

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

    @RequestMapping("/user/add")
    public String toAdd(){
        return "user/add";
    }
    @RequestMapping("/user/update")
    public String toUpdate(){
        return "user/update";
    }

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

    //登录操作
    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
        //使用shiro编写认证操作
        //获取subject
        Subject subject = SecurityUtils.getSubject();
        //封装用户的数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //执行登录的方法,只要没异常就代表登录成功!
        try {
            subject.login(token);  //登录成功返回首页
            return "index";
        }catch (UnknownAccountException e){ //用户不存在异常
            model.addAttribute("msg","用户名不存在");
            return "login";
        }catch (IncorrectCredentialsException e) { //密码错误
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }

    @RequestMapping("/noauth")
    @ResponseBody
    public String noAuth(){
        return "未经授权不能访问此页面";
    }
}

html页面

index.html

<!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</title>
</head>
<body>

<h1>首页</h1>

<p th:if="${session.loginUser==null}">
    <a th:href="@{/toLogin}">登录</a>
</p>

<p style="color:red;" th:text="${msg}"></p>

<div shiro:hasPermission="user:add">
    <a th:href="@{/user/add}">add</a>
</div>

<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
</div>

</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<hr>
<form th:action="@{/login}">
    <p>
        用户名: <input type="text" name="username">
    </p>
    <p>
        密码: <input type="text" name="password">
    </p>
    <p>
        <input type="submit">
    </p>
</form>
</body>
</html>

add/update页面

</head>
<body>
<h1>add</h1>
</body>

<body>
<h1>update</h1>
</body>

Swagger

springboot集成Swagger ==>web项目

简单入门

  1. 导入依赖

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>3.0.0</version>
    </dependency>
    
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>3.0.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  2. 简单配置swaggerConfig

    @Configuration
    @EnableSwagger2  //开启swagger2
    public class SwaggerConfig {
    }
    
  3. 访问页面

    http://localhost:8080/swagger-ui/index.html

配置Swagger

配置作者信息

@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("王建", "http://www.baidu.com", "2390721223@qq.com");
        return new ApiInfo(
                "王建的Swagger API文档",
                "王建有点东西昂",
                "1.0",
                "urn:tos",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList()
        );
    }
}

配置扫描接口

package com.dragon.swagger.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.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(Environment environment) {

        //设置要显示swagger的环境
        Profiles profiles = Profiles.of("dev");
        //获取项目的环境
        boolean b = environment.acceptsProfiles(profiles);
        //把值赋给下边的enable,ok 

        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(b)  //enable 是否启动swagger
                .select()
                /*RequestHandlerSelectors:配置要扫描接口的方式
                * basePackage:指定要扫描的包
                * any():扫描全部
                * none():不扫描
                * withClassAnnotation:扫描类上的注解,参数是一个注解的反射类
                * withMethodAnnotation:扫描方法上的注解
                */
                .apis(RequestHandlerSelectors.basePackage("com.dragon.swagger.controller"))
                //设置需要过滤的路径
                .paths(PathSelectors.ant("/dragon/**"))
                .build();
    }

    //配置swagger信息apiInfo
    private ApiInfo apiInfo() {
        //设置作者信息
        Contact contact = new Contact("王建", "http://www.baidu.com", "2390721223@qq.com");
        return new ApiInfo(
                "王建的Swagger API文档",
                "王建有点东西昂",
                "1.0",
                "urn:tos",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList()
        );
    }

}

配置多环境配置文件

application-dev.properties application-pro.properties

在主文件中选择激活文件

#设置需要激活的环境
spring.profiles.active=dev

配置api文档分组

return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .groupName("王建")  //配置分组
    ..............

配置多个分组(多个Docket实例)

    @Bean
    public Docket docket1() {
        return new Docket(DocumentationType.SWAGGER_2).groupName("A");
    }

    @Bean
    public Docket docket2() {
        return new Docket(DocumentationType.SWAGGER_2).groupName("B");
    }

    @Bean
    public Docket docket3() {
        return new Docket(DocumentationType.SWAGGER_2).groupName("C");
    }

实体类Model配置

@Api给属性上加注解

1.创建一个实体类

package com.dragon.swagger.pojo;

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

@ApiModel("用户实体类")
public class User {
    @ApiModelProperty("用户id")
    private int id;
    @ApiModelProperty("用户名")
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

必须有get和set方法

2.在controller中添加一个返回对象的请求,自动被swagger扫描

    //只要我们的返回值为实力类,swagger就会扫描到
    @PostMapping(value = "/user")
    public User user() {
        return new User();
 	}

总结:

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

【关注点】在正式发布的时候,关闭Swagger!!!出于安全考虑。而且节省运行的内存;

任务

异步任务

@Async 和 @EnableAsync

在存在多线程的方法上添加**@Async注解,然后在springboot主程序中添加@EnableAsync**开启异步任务

service

@Service
public class AsyncService {

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

controller

@RestController
public class AsyncController {

    @Autowired
    AsyncService asyncService;

    @RequestMapping("/hello")
    public String hello() {
        asyncService.hello();
        return "OK";
    }
}

邮件任务

导入以来

        <!--邮件发送依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

测试

package com.dragon;

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;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;

@SpringBootTest
class Springboot05TaskApplicationTests {

    @Autowired
    JavaMailSenderImpl mailSender;

    @Test
    //邮件发送1:设置一个简单的邮件
    void contextLoads() {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("springboot学习"); //主题
        message.setText("东西不多,拓展多!"); //内容

        message.setTo("2390721223@qq.com");  //发送给谁
        message.setFrom("2390721223@qq.com");//从哪发送

        mailSender.send(message);
    }

    @Test
        //邮件发送1:设置一个复杂的邮件
    void contextLoads2() throws MessagingException {
        //创建一个复杂的邮件
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        //组装
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

        //正文
        helper.setSubject("springboot学习"); //主题
        helper.setText("<p style='color:red'>东西不多,拓展多!</p>",true); //内容

        //附件
        helper.addAttachment("1.jpg",new File("C:\\Users\\hasee\\Pictures\\My photo\\1.jpg"));

        helper.setTo("2390721223@qq.com");  //发送给谁
        helper.setFrom("2390721223@qq.com");//从哪发送

        mailSender.send(mimeMessage);
    }
}

定时任务

使用的两个注解:

  • @EnableScheduling //开启定时任务
  • @Scheduled //标注在方法上,表示定时执行

cron表达式:

(* * * * * ?) ====> (秒 分 时 日 月 星期)

测试:

package com.dragon.service;

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

@Service
public class ScheduledService {
    //秒 分 时 日 月 周几
    //0 * * * * MON-FRI
    //注意cron表达式的用法;
    @Scheduled(cron = "0/2 * * * * ?")  //表示每两秒执行一次
    public void hello(){
        System.out.println("hello.....");
    }
}

Dubbo+Zookeeper+SpringBoot

Zookeeper3.4.11下载:http://archive.apache.org/dist/zookeeper/zookeeper-3.4.11/

配置好Dubbo+Zookeeper的环境后

provider-service接口实现类

public class TicketServiceImpl implements TicketService {
    @Override
    public String getTicket() {
        return "和狂神学java";
    }
}

导入依赖

        <!--dubbo+zookeeper-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>

        <!--zkclient-->
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>

        <!--zookeeper及其依赖包,解决日志冲突,还需要剔除日志依赖-->
        <!-- 引入zookeeper -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.14</version>
            <!--排除这个slf4j-log4j12-->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

在springboot配置文件中配置dubbo相关属性

server.port=8002

#配置当前应用名字
dubbo.application.name=provider-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#扫描指定包下服务
dubbo.scan.base-packages=com.dragon.provider.service

微服务架构:一通百通

  1. API网关,服务路由
  2. HTTP , RPC框架,一部调用
  3. 服务注册于发现,高可用
  4. 熔断机制,服务降级
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值