SpringBoot3笔记

一、JDK新特性

1.有用的新特性

JDK8-19新增了不少新特性:

  • Java Record
  • Swich 开关表达式
  • Text Block 文本块
  • var 声明局部变量
  • sealed 密封类

2.Java Record

java14中预览的新特性叫做Record,是一种特殊类型的java类。Record相当于一个内置的,语言级别的Lombok,可用来创建不可变类,语法简短。

当创建java类,都会创建大量的样板代码如set,get,构造方法,重新hashcode,tostring,equals方法。

java Record避免上述样板代码,如下特点:

  • 带有全部参数的构造方法
  • public访问器
  • toString(),hashCode(),equals()
  • 无get,set方法。没有遵循Bean的命名规范
  • final类,不能继承Record,Record为隐士的final类。除此之外与普通类一样
  • 不可变类,通过构造创建Record类
  • final属性,不可修改
  • 不能声明实例属性,能声明static成员

1.创建Record类

public record Student(Integer id,String name,String email,Integer age) {

}

//测试类
public class StudentTest {
    @Test
    public void test01() {
        //创建Record对象
        Student zhixun = new Student(1001,"zhixun","zhixun@qq.com",22);
        //这里的zhixun 其实调用了zhixun.toString()方法
        System.out.println("zhixun=" + zhixun);

        //通过public访问器,获取属性,只读。没用get,set方法
        Integer id = zhixun.id();
        String name = zhixun.name();
        String email = zhixun.email();
        //值不可变 再应用上更安全
        System.out.println("id = " + id);
        System.out.println("name = " + name);
        System.out.println("email = " + email);
        Student lisi = new Student(1002,"lisi","lisi@qq.com",22);
        
        System.out.println(lisi.equals(zhixun));
    }
}

2.Record类定义方法

Record与普通java类一样的方式定义方法。

public record Student(Integer id,String name,String email,Integer age) {
	
	//创建方法
    public String concat(){
        return String.format("姓名是%s,年龄是%d",this.name,this.age);
    }
    
    //静态方法
    public static String emailToUpperCase(String email){
        return  Optional.ofNullable(email).orElse("no email").toUpperCase();
    }
}


 @Test
    public void test02(){
        //创建Record对象
        Student zhixun = new Student(1001,"zhixun","zhixun@qq.com",22);
        //使用对象,调用方法
        String str = zhixun.concat();
        System.out.println("str=" + str);
    }

 @Test
    public void test03(){
        //使用类,静态方法
        String email = Student.emailToUpperCase("zhixun@qq.com");
        System.out.println("email=" + email);
    }

3.Record构造方法

Record分为三种类型,分别为:紧凑的,规范的和定制的构造方法

  • 紧凑型构造方法没有任何参数,甚至没用括号。
  • 规范构造方法是以所有成员作为参数。
  • 定制构造方法是自定义参数个数。
public record Student(Integer id, String name, String email, Integer age) {

    //紧凑型
    public Student {
        if (id < 1) {
            throw new RuntimeException("id < 1");
        }
         System.out.println(id);
    }

    //定制构造
    public Student(Integer id,String name) {
        this(id,name,null,null);
    }



}

    @Test
    public void test04(){
        //使用类,静态方法
        Student zhixun = new Student(1001,"zhixun");
        //会先执行紧凑
        System.out.println("zhixun=" + zhixun);
    }

输出结果:1001 
    	zhixun=Student[id=1001, name=zhixun, email=null, age=null]

4.Record类实现接口

Record与普通java类一样的方式实现接口,重写接口。

//商品接口
public interface PrintInterface {
    //输出自定义信息
    void print();
}

//record方法实现商品接口
public record ProductRecord(Integer id, String name, Integer qty) implements PrintInterface {
	
	//重写接口方法
    @Override
    public void print() {
        StringJoiner joiner = new StringJoiner("-");
        String s = joiner.add(id.toString()).add(name).add(qty.toString()).toString();
        System.out.println("商品信息 = " + s);
    }
}

//测试类
public class ProductTest {

    @Test
    public void test01(){
        //创建Record对象
        ProductRecord productRecord = new ProductRecord(1001,"手机",22);
        //测试print方法
        productRecord.print();
    }

}

输出结果:商品信息 = 1001-手机-22

5.Record可做局部对象使用

    @Test
    public void test03(){
        //定义局部的Local Record 别忘了{}
        record SaleRecord(Integer saleId,String productName,Double money){};
        //创建对象
        SaleRecord saleRecord = new SaleRecord(001,"显示器",3000.00);
        System.out.println("saleRecord=" + saleRecord);
    }

输出结果:saleRecord=SaleRecord[saleId=1, productName=显示器, money=3000.0]

6.嵌套Record

多个Record可以组合定义,一个Record能够包含其他的Record。

public record Address(String city,String address,String zipCode) {
}


public record PhoneNumber(String areaCode,String number) {
}


public record Customer(String id,String name,PhoneNumber phoneNumber,Address address) {

}

    @Test
    public void test04(){


        Address address = new Address("厦门","集美区","36001");
        PhoneNumber phoneNumber = new PhoneNumber("121","10086");
        Customer customer = new Customer("1001","zhixun",phoneNumber,address);
        System.out.println(customer);
    }
    
输出结果:Customer[id=1001, name=zhixun, phoneNumber=PhoneNumber[areaCode=121, number=10086], 			   	         address=Address[city=厦门, address=集美区, zipCode=36001]]  
        10086

7.instanceof判断Record类型

instanceof能够和java Record一起使用。编译器能够知道Record类型还是普通java类型。

public record Person(String name,Integer age) {
}


public class SomeService {

    //定义业务方法
    public boolean isEligible(Object obj){
        if(obj instanceof Person(String name,Integer age)){
            return age >= 18;
        }
        return false;
    }
}

    @Test
    public void test05(){
        Person person = new Person("zhixun",22);
        SomeService someService = new SomeService();
        System.out.println(someService.isEligible(person));

    }
输出结果:true

总结

在这里插入图片描述

3.switch

1.箭头表达式

switch新语法 case label -> 表达式|throw 语句|block

    @Test
    public void test01(){
        int week = 7;
        String memo = "";
        switch (week){
            case 1 -> memo = "星期日 休息";
            case 2,3,4,5,6 -> memo = "工作日";
            case 7 -> memo = "休息日 休息";
            default -> throw new RuntimeException("无效的日期");
        }
        System.out.println("week: " + memo);
    }
    
输出结果:week: 休息日 休息

2.支持yied返回值

yied让switch作为表达式,能够返回值

    @Test
    public void test02(){
        int week = 7;
        //注意写法 与case匹配,匹配成功后,将返回值赋给变量 并结束switch
        String memo = switch (week){
            case 1: yield "星期日 休息";
            case 2,3,4,5,6 : yield  "工作日";
            case 7 : yield"休息日 休息";
            default : yield ("无效的日期");
        };
        System.out.println("week: " + memo);
    }

输出结果:week: 休息日 休息

代码块形式:

使内容更丰富

  @Test
    public void test03(){
        int week = 7;
        String memo = switch (week){
            case 1 -> {
                yield "星期日 休息";
            }
            case 2,3,4,5,6 ->{
                yield  "工作日";
            }
            case 7 -> {
                yield "休息日 休息";
            }
            default -> {
                yield ("无效的日期");
            }
        };
        System.out.println("week: " + memo);
    }
    
输出结果:week: 休息日 休息

3.支持java Record

switch表达式中使用Record,结合case标签 -> 表达式,yield实现复杂的计算

//先准备三个record
public record Line(int x,int y) {
}

public record Rectangle(int width,int height) {
}

public record Shape(int width,int height) {
}

  @Test
    public void test04(){
        //创建对象
        Line line = new Line(1,1);
        Rectangle rectangle = new Rectangle(10,20);
        Shape shape = new Shape(20,30);

        Object obj = line;
        int result = switch (obj){
            case Line(int x,int y) ->{
                System.out.println("图形为Line   x:" + x + " y:" + y);
                yield x + y;
            }
            case Rectangle(int w,int h) -> 2 * (w+h);
            case Shape(int x,int y) -> {
                System.out.println("图形为shape   x:" + x + " y:" + y);
                yield x * y;
            }
            default -> throw new IllegalStateException("Unexpected value: " + obj);
        };
    }

输出结果:图形为Line   x:1 y:1

4.var的使用

var的特点

  • var是一个保留字,不是关键字(可以声明var变量)
  • 方法内声明的局部变量,必须有初值
  • 每声明一次变量,不可复合声明多个变量。 var s1 = a,b //Error
  • var动态类型是编译器根据变量所赋的值来判断类型
  • var代替显示类型,代码简洁,减少不必要的排版,混乱。

缺点:

  • 减低了代码的可读性
    @Test
    public void test05(){
        //创建对象
        var a = new Line(1,1);
        System.out.println(a);
    }
    
输出结果:Line[x=1, y=1]

5.sealed

1.sealed密闭类

sealed的特点:

  • 限制继承,java中通过继承增强,扩展了类的能力,复用某些功能。当这种能力不受控制与原有类的设计相违背,导致不预见的异常逻辑。

sealed作为关键字可在class和interface上使用,结合permits关键字。定义限制继承的密封类

permits:表示允许的子类,一个或者多个

子类的声明有三种”

  • final 终结,依然是密封的
  • sealed 子类是密封的,需要子类实现
  • non-sealed 非密封类,扩展使用,不受限制
//创建密闭类 permit允许三个子类Circle,Square, Rectangle
public sealed class Shape permits Circle,Square, Rectangle {

    private Integer width;
    private Integer height;

    public void draw(){
        System.out.println("画一个图形...");
    }

}

//final 到此类就不会再有子类了
public final class Circle extends Shape {
}

//sealed 还是密闭类 他得有允许的子类RoundSquare
public sealed class Square extends Shape permits RoundSquare {

    public void draw(){
        System.out.println("画一个Square图形...");
    }

}

//non-sealed非密闭类  可以被扩展,不受密闭限制
public non-sealed class Rectangle extends Shape {
}

public final class RoundSquare extends Square {
}

2.sealed interface密闭接口

用法和密闭类一样

//创建密闭接口
public sealed interface SomeService permits SomeServiceImpl {
    void doThing();
}

//创建接口实现类
public final class SomeServiceImpl implements SomeService {
    @Override
    public void doThing() {
        System.out.println("doThing...");
    }
}

    @Test
    public void test06(){
         SomeService someService = new SomeServiceImpl();
         someService.doThing();
    }
    
输出结果:doThing...

二、Spring Boot基础篇

1 SpringBoot是什么?

在这里插入图片描述

1.SpringBoot与SpringCloud关系

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.SpringBoot3的新特性

在这里插入图片描述

2.代码结构

1.单一模块

在这里插入图片描述

2.包和主类

在这里插入图片描述

3.spring-boot-starter-parent

在这里插入图片描述

4.pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- parent:表示父项目 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <!-- 当前项目坐标 -->
    <groupId>com.example</groupId>
    <artifactId>day_02</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>day_02</name>
    <description>day_02</description>

    <!-- jdk版本 -->
    <properties>
        <java.version>17</java.version>
    </properties>

    <!-- 依赖列表 -->
    <dependencies>

        <!-- SpringWeb依赖
             带有starter单词被叫做启动器(启动依赖)
             spring-boot-starter开头的 是spring官方推出的启动器
             xxx-starter 非spring推出的 有其他组织提供的

             starter:是一组依赖描述,应用包含starter,可以获取spring相关技术的一站式依赖和版本。
                     通过starter能够跟快速的启动运行项目。
                     * 依赖坐标,版本
                     * 传递依赖坐标,版本
                     * 配置类,配置项
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

    <build>
        <!-- 打包名称 myWeb.jar -->
      <finalName>myWeb</finalName>
        <plugins>
            <plugin>
                <!-- springboot项目插件 负责打包,编译等 -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.core注解

/*
* @SpringBootApplication 源码!
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

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

/*
核心注解
@SpringBootConfiguration :包含@Configration注解的功能,所一有这个注解标注的类是配置类
    	@Configration:javaConfig的功能,主要用于配置类,结合@Bean能够将对象注入到SpringIoc的容器。
@EnableAutoConfiguration:包含@AutoConfigurationPackage
		@AutoConfigurationPackage:开启自动配置,将spring和第三方库中的对象创建好并注入到容器中。避免写xml去掉样例代码,需要使用的对象由框架提供。
@ComponentScan:组件扫描器:<context:component-scan base-package="xxx"/>
			主要用来扫描 @Controller,@Service,@Repository,@Component注解,创建他们的对象注入到容器
			springboot约定:启动类作为扫描包的根(起点),@ComponentScan:扫描当前Day02Application类存在的包与此包的子包中所有的类。
*/    
@SpringBootApplication
public class Day02Application{
    
    @Bean
    public void myDate(){
        return new Date();
    }
    
     public static void main(String[] args) {
         //run方法第一个参数 源(配置类),从这里加载bean,找到bean注入到容器
         //run源码 可以执行多个配置类
         //return run(new Class<?>[] { primarySource }, args);
         
         //也就是说 首先通过run找到配置类Day02Application,找到配置类就能找到@SpringBootApplication注解从而实现以上注解的功能
        SpringApplication.run(Day02Application.class, args);
         
         //run方法的返回值是容器对象
         /*源码
         *
		public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
			return run(new Class<?>[] { primarySource }, args);
		}
			**ConfigurableApplicationContext继承了ApplicationContext**
		public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
			....
		}
		也就是说 可以用ApplicationContext来接收run方法的返回值
         */
         ApllicationContext ctx = SpringApplication.run(Day02Application.class, args);
         //然后可以通过容器获取对象
         Date date = ctx.getBean(Date.class);
    }
    
}

4.如何打包

1.配置pom文件的build属性

通过pom.xml的build属性

 <build>
        <!-- 打包名称 myWeb.jar -->
      <finalName>myWeb</finalName>
        <plugins>
            <plugin>
                <!-- springboot项目插件 负责打包,编译等 -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.点击maven工具

在这里插入图片描述

先点击clean 然后点击package进行打包。

3.运行打包文件

打包成功后会生成jar包,再该路径下运行cmd,输入java -jar day_02-0.0.1-SNAPSHOT.jar

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.springboot 的jar有哪些区别

在这里插入图片描述

5.外部化配置

应用程序 = 代码 + 数据(数据库,文件,url)

应用程序的配置文件:Spring Boot允许在代码块外,提供应用程序运行的数据,以便在不同环境中使用相同的应用程序代码。避免硬编码,提供系统的灵活性。可以使用各种外部配置源,包括java属性文件,yaml文件,环境变量和命令行参数。

在项目中经常使用yaml和properties文件,其次是命令行参数。

1.配置文件格式

在这里插入图片描述

2.application

在这里插入图片描述

1.获取properties文件属性

首先现在properties文件中创建值

#默认的配置文件
app.name = day_02
app.owner = zhixun
app.port = 8001

创建SomeService类 通过@Value("${key:默认值}")将属性注入

@Service
public class SomeService {

    //使用@Value("${key:默认值}")
    /*
    *  @Value只能获取当个值
    *   如果默认值不写的话,找不到对应的key将会报错。写了则会将默认值赋值给属性。 
    */

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

    @Value("${app.owner}")
    private String owner;

    @Value("${app.port}")
    private String port;

    public void printValue(){
        System.out.println(name + "," + owner + "," + port);
    }

在测试方法中,创建SomeService变量并通过@Autowire进行自动注入。

    @Autowired
    private SomeService someService;

    @Test
    void test01() {
        someService.printValue();
    }

输出结果: day_02,zhixun,8001

2.获取yml文件属性

app:
  #name:空格value
  name: day
  owner: zhixun
  port: 9001

运行:采用properties的测试模块
输出结果: day_02,zhixun,8001
2.Environment

Environment是外部化的抽象,是多种数据来源的集合。从中可以读取application配置文件,环境变量,系统属性。使用方式在Bean中注入Environment。调用它的getProperty(key)方法。

创建ReadConfig类,并注入Environment对象

@Service
public class ReadConfig {

    //注入环境对象
    @Autowired
    private Environment environment;

    public void print(){
        //获取某个key的值
        String name = environment.getProperty("app.name");

        //判断key是否存在
        if (environment.containsProperty("app.owner")){
            System.out.println("app.owner");
        }

        //读取key的值,转为期望的类型,同时提供默认值。
        Integer port = environment.getProperty("app.port", Integer.class, 9001);
        String format = String.format("读取的key值,name=%s, port=%s", name, port);
        System.out.println(format);
    }

}
   //测试方式

    @Autowired
    private ReadConfig config;
    
    @Test
    void test02() {
        config.print();
    }

输出结果:app.owner
		读取的key值,name=day, port=9001
3.组织多文件

大型集成的第三方框架,中间件较多,配置细节复杂。如果配置在同一个application文件不易阅读。需每个框架独立一个配置文件,最后通过导入功能,将多个配置文件集中于application。

首先先创建两个yml文件

#db.yml
spring:
  dataSource:
    url: jdbc:mysql://localhost:3306/db
    username: admin
    password: 123456
#redis.yml
spring:
  redis:
    host: 192.168.1.1
    port: 6379
    password: 123456
//MultiConfigService.class
@Service
public class MultiConfigService {

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.dataSource.url}")
    private String jdbcUrl;

    public void print(){
        System.out.println("redisHost = " + redisHost + "," + "jdbcUrl = " + jdbcUrl);
    }

}
//测试方法
    @Test
    void test03() {
        multiConfigService.print();
    }

输出结果:redisHost = 192.168.1.1,jdbcUrl = jdbc:mysql://localhost:3306/db
4.多环境配置

在这里插入图片描述

创建两个yml文件,一个开发环境,一个测试环境

#application-dev.yml
myapp:
  memo: 这是开发环境

#指定环境名称
spring:
  config:
    activate:
      on-profile: dev
#application-test.yml
myapp:
  memo: 这是测试的配置文件

#指定环境名称
spring:
  config:
    activate:
      on-profile: test

并在application中激活环境

#编写配置 key:value
app:
  name: day
  owner: zhixun
  port: 9001

#导入其他的配置文件,多个文件可使用逗号当分割符。
spring:
  config:
    import: config/db.yml,config/redis.yml
#激活某个配置文件(环境)
  profiles:
    active: dev

EnvService.class

@Service
public class EnvService {

    @Value("${myapp.memo}")
    private String memo;

    public void print(){
        System.out.println(memo);
    }

}
  //测试方法
  @Test
    void test04() {
        envService.print();
    }

输出结果:这是开发环境
5.总结:
文件格式: properties(优先),yml(yaml)
		内容: key = value
		yml:  key: value
文件名称: 默认application
环境对象: Environment: 表示抽象的所有key和value。方法getProperty(key)

多文件: 自定义独立的配置文件,使用spring.config.import = 文件路径。
		导入多个文件。
多环境: 开发环境,测试环境,上线,特性,bug等
		名称: application-profile.yml(properties),可以用多个环境文件
		创建环境文件: 必须得通过spring.config.activate.on-profile指定文件名
		spring:
          config:
            activate:
              on-profile: test
激活环境: spring.profiles.active:环境名称
	#激活某个配置文件(环境)
      profiles:
        active: dev
读取数据: @Value("${key:默认值}"),使用Environment.getProperty(key) 获取的是单个值。

3.绑定Bean:

1.简单的属性Bean绑定

在这里插入图片描述

创建AppBean获取 application以app开头的属性

/*
*
* @ConfigurationProperties: 表示使用Bean对象读取配置项
*   prefix:表示配置文件中多个key的公共开始部分。
*
* */
//需要加入Component/Configration 因为ConfigurationProperties不会将类注入到容器中
@Component
//匹配相同的前缀
@ConfigurationProperties(prefix = "app")
public class AppBean {
    //key的名称于属性名相同,调用属性setXXXX方法给属性赋值
    private String name;
    private String owner;
    private Integer port;

    public String getName() {
        return name;
    }

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

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    @Override
    public String toString() {
        return "AppBean{" +
                "name='" + name + '\'' +
                ", owner='" + owner + '\'' +
                ", port=" + port +
                '}';
    }
}
    @Autowired
    private AppBean appBean;

    @Test
    void test05() {
        System.out.println(appBean.toString());
        System.out.println(appBean.getClass());
    }

输出结果:AppBean{name='day', owner='zhixun', port=9001}
如果用@Configration 通过反射获取的类信息是经过容器处理过的代理对象,并不是对象本身。
输出结果:class com.example.day_02.pk5.AppBean$$SpringCGLIB$$0
如果需要变为普通bean 不使用代理需要修改为 @Configuration(proxyBeanMethods = false)

在这里插入图片描述

2.嵌套Bean

Bean中包含其他Bean作为属性,将配置文件中的配置项绑定到Bean以及引用类型的成员。

@Component
@Data
public class Security {
    private String username;
    private String password;
}

@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "app")
public class NestAppBean {
    private String name;
    private String owner;
    private Integer port;
    private Security security;

}
app:
  name: day
  owner: zhixun
  port: 9001
  #新属性
  security:
    username: root
    password: 123456
@Test
void test06() {
    System.out.println(nestAppBean.toString());
}

输出结果:NestAppBean(name=day, owner=zhixun, port=9001, security=Security(username=root, password=123456))
3.扫描注解

@ConfigurationProperties注解起作用,还需要@EnableConfigrationProperties或者@ConfigrationPropertiesScan。这个注解是专门找@ConfigurationProperties注解,将其注入到spring容器中。在启动类上使用扫描注解。

//@ConfigurationPropertiesScan扫描到@ConfigurationProperties会将其注入到spring容器中
@ConfigurationPropertiesScan("com.example.day_02.pk6")
@SpringBootApplication
public class Day02Application {

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

}
4.处理第三方库对象

上面的例子都是在源代码中使用@ConfigurationProperties注解,如果是第三方库的对象是没有源代码的。此时需要@ConfigurationProperties于@Bean一起在方法上面使用。

@Data
public class Security {
    private String username;
    private String password;
}
@Configuration(proxyBeanMethods = false)
public class ApplicationConfig {

    //创建bean对象,属性值来自配置文件
    @ConfigurationProperties(prefix = "security")
    @Bean
    public Security createSecurity(){
        return new Security();
    }

}
#第三方库对象,没有源代码
security:
  username: root
  password: 123456
    @Autowired
    private Security security;

    @Test
    void test07() {
        System.out.println(security);
    }

输出结果:Security(username=root, password=123456)
5.集合Map,List以及Array

创建一个保存数据的Bean

@Data
public class MyServer {

    private String title;
    private String ip;
}

@Data
public class User {

    private String name;
    private String sex;
    private Integer age;

}

@Configuration
@ConfigurationProperties
@Data
public class CollectionConfig {

    private List<MyServer> servers;
    private Map<String,User> users;
    private String[] names;


}

配置yml 添加其对应数据

#配置集合
#数组 和 List一样的,使用“-” 一个成员
names:
  - lisi
  - zhixun

#List<MyServer> server
servers:
  - title: 华北
    ip: 202.1.25.33
  - title: 西南
    ip: 226.6.5.32


#Map<String,User> users
users:
  user1:
    name: zhixun
    sex: nan
    age: 22
  user2:
    name: lisi
    sex: nan
    age: 55

测试方法

    @Autowired
    private CollectionConfig collectionConfig;

    @Test
    void test08() {
        System.out.println(collectionConfig);
    }

输出结果:CollectionConfig(servers=[MyServer(title=华北, ip=202.1.25.33), MyServer(title=西南, ip=226.6.5.32)], users={user1=User(name=zhixun, sex=nan, age=22), user2=User(name=lisi, sex=nan, age=55)}, names=[lisi, zhixun])

6.指定数据源文件

application做配置是经常使用的,我们可以指定某个文件作为数据来源。@PropertySource是主力,用于加载指定的properties文件。也可以是xml文件。@PropertySource与@Configuration一同使用,其他注解还有@Value,@ConfigrationProperties。

创建properties文件

#默认的配置文件
group.name = ZHIXUN
group.leader = zz
group.members = 20
@Configuration
@ConfigurationProperties(prefix = "group")
@PropertySource(value = "classpath:/group.properties")
@Data
public class Group {
    private String name;
    private String leader;
    private Integer members;
}
    @Test
    void test09() {
        System.out.println(group);
    }

输出结果:Group(name=ZHIXUN, leader=zz, members=20)

7.总结
绑定Bean: 用于多个属性
注解:@ConfigurationProperties
	位置:1) 在类的上面,需要有源代码
		 2) 方法的上面,使用第三方对象。配合@Bean注解
数据来源 application文件
		指定数据的来源:@PropertySource(value = "classpath:/group.properties")

注意:1) 类中有无参构造方法
	 2) 属性有setxxx方法
	 3) static属性是无效的
	

@ConfigurationProperties使用需要配合其他注解
1) @Configration
2) @EnableConfigrationProperties
3) @ConfigurationPropertiesScan


配置文件application 名称和位置都是可以改变的
application配置的文件位置:
1) 项目的根目录下
2) 项目根目录下的/config目录
3) resource/config
4) resource目录下

4.创建对象的三种方式

1.传统的xml配置文件
@Data
public class Person {
    private String name;
    private Integer age;
}
#applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <!--声明对象-->
    <bean id="myPerson" class="com.example.day_02.pk10.Person">
        <property name="name" value="zhixun"/>
        <property name="age" value="22"/>
    </bean>

</beans>
//要导入传统的xml文件,需要在配置类中使用@ImportResource
//在配置类加入注解@ImportResource
@ImportResource(locations = {"classpath:/applicationContext.xml"})

//@ConfigurationPropertiesScan扫描到@ConfigurationProperties会将其注入到spring容器中
@ConfigurationPropertiesScan("com.example.day_02.pk6")
@SpringBootApplication
public class Day02Application {
    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(Day02Application.class, args);
        Person bean = context.getBean(Person.class);
        System.out.println(bean);

    }
}

输出结果:Person(name=zhixun, age=22)

5.AOP

在这里插入图片描述

在使用AOP之前,首先得先导入相对应的依赖!

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
//创建service接口,并对其实现接口

public interface SomeService {

    void query(Integer id);
    void save(String name,Integer age);

}

@Service
public class SomeServiceImpl implements SomeService {
    @Override
    public void query(Integer id) {
        System.out.println("-----SomeService的query方法-----");
    }

    @Override
    public void save(String name, Integer age) {
        System.out.println("-----SomeService的save方法-----");
    }
}


编写切面类

@Component
@Aspect
public class LogAspect {


    //功能增强的方法
    //切点 execution(* com.example.day_02_aop.service..*.*(..))
    @Before("execution(* com.example.day_02_aop.service..*.*(..))")
    public void sysLog(JoinPoint jp) {
        StringJoiner log = new StringJoiner("|", "{", "}");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        log.add(formatter.format(LocalDateTime.now()));

        //当前执行的业务方法的名称
        String methodName = jp.getSignature().getName();
		log.add(methodName);
        //方法的参数
        Object[] args = jp.getArgs();
        for (Object arg : args) {
            log.add(arg == null ? "-" : arg.toString());
        }

        System.out.println("日志:" + log);
    }
}

测试方法

    @Autowired
    private SomeServiceImpl someService;

    @Test
    public void testLog(){
        someService.query(1);
        someService.save("zhixun",22);
    }

输出结果:
        日志:{2023-10-13 15:24:47|query|1}
        -----SomeService的query方法-----
        日志:{2023-10-13 15:24:47|save|zhixun|22}
        -----SomeService的save方法-----

三、自动配置

在这里插入图片描述在这里插入图片描述

四、访问数据库

在这里插入图片描述

1.DataSource

在这里插入图片描述
在这里插入图片描述

2.轻量的jdbc Template

在这里插入图片描述

导入对应依赖

<!-- JdbcTemplate 连接池: HikariCP -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>

		<!-- mysql驱动 -->
		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>

在application中配置mysql

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: root

#设置执行脚本 always:总是执行  never:不执行
  sql:
    init:
      mode: never

1.JdbcTemplate访问Mysql

在这里插入图片描述

创建article类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article {

    private Integer id;
    private String title;
    private String content;
    private String creat;
    private Integer uId;
    private String category;

}

注入jdbcTemplate,计算article表中的有多少行数据

	//注入jdbcTemplate
	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Test
	void test01() {
		String sql = "select count(*) as ct from article";
		Long count = jdbcTemplate.queryForObject(sql,Long.class);
		System.out.println("article表中有 " + count + "条记录");
	}

输出结果:article表中有 21条记录

	//查询结果为单行记录,使用?作为参数的占位符
      	@Test
	void test02() {
		String sql = "select * from article where id = ?";
    //queryForObject方法必须得有查询结果,如果没找到会抛异常
		Article article = jdbcTemplate.queryForObject(sql,
				new BeanPropertyRowMapper<>(Article.class),3);
		System.out.println("查询结果为 " + article);
	}
输出结果:查询结果为 Article(id=3, title=求助万能墙的微信, content=有没有漂亮妹妹, creat=2023-05-23 22:38:06, uId=1, category=111)

测试自定义RowMapper

@Test
	void testRowMapper() {
		String sql = "select * from article where id = 3";
		Article article1 = jdbcTemplate.queryForObject(sql, (rs, rownum) -> {
			System.out.println("rs=" + rs);
			var id = rs.getInt("id");
			var title = rs.getString("title");
			var content = rs.getString("content");
			var creat = rs.getString("creat");
			var uId = rs.getInt("uId");
			var category = rs.getString("category");

			return new Article(id, title, content, creat, uId, category);
		});

		System.out.println("查询结果为 " + article1);
	}

输出结果:查询结果为 Article(id=3, title=求助万能墙的微信, content=有没有漂亮妹妹, creat=2023-05-23 22:38:06, uId=1, category=111)

测试List集合

@Test
	void testList() {

		String sql = "select * from article  order by id";
		//一个list成员为一行记录,map为列名和值
		List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);

		maps.forEach( el -> {
			el.forEach((filed,value)->{
				System.out.println("字段名称:" + filed + " 值:" + value);
			});
		});

	}

输出结果:
    字段名称:id 值:3
    字段名称:title 值:求助万能墙的微信
    字段名称:content 值:有没有漂亮妹妹
    字段名称:creat 值:2023-05-23 22:38:06
    字段名称:uid 值:1
    字段名称:category 值:111

更新数据

	@Test
	void testUpdate() {

		String sql = "update article set title = ? where id = ? ";
		int result = jdbcTemplate.update(sql, "Java实现编程", 3);
		System.out.println(result);


	}

2.NamedParameterJdbcTemplate

在这里插入图片描述

 @Test
    void testNamedParameter() {

        String sql = "select * from article where id>:id and uid=:uid";
		
        //将参数封装到map中
        Map<String,Integer> map = new HashMap<>();
        map.put("id",5);
        map.put("uid",1);
        List<Map<String, Object>> maps = namedParameterJdbcTemplate.queryForList(sql, map);

        maps.forEach( el ->{
            el.forEach((filed,value)->{
                System.out.println("字段名:" + filed + " 值 " + value);
            });
            System.out.println("----------------");
        });

    }

3.多表查询

在这里插入图片描述

4.总结

在这里插入图片描述

3.Mybatis

在这里插入图片描述

1.增删改查

1.@Select查询

先导入对应依赖

		<!-- mybatis启动器(由mybatis团队提供的) -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.2</version>
		</dependency>
		
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.2</version>
			<scope>test</scope>
		</dependency>

配置yml文件

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    
#mybatis
mybatis:
  configuration:
    #支持驼峰命名
    map-underscore-to-camel-case: true
    #日志 控制台输出
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

创建实体类,mapper接口

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article {

    private Integer id;
    private String title;
    private String content;
    private String creat;
    private Integer uId;
    private String category;

}
public interface ArticleMapper {

    //按主键查询
    @Select("""
            select  * from article where id = #{articleId};
            """)
    Article selectById(@Param("articleId") Integer id);
	
            
    //(当属性名与字段名匹配不上,比如数据库叫Tid,属性叫id)可使用ResultSet 和 对象的属性进行映射
        @Results(id = "BaseArticleMap" ,value = {
            //@Result(id = true(是否是主键),column = "数据库的字段名",property = "实体类的属性名"),
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "title",property = "title"),
            @Result(column = "content",property = "content"),
            @Result(column = "creat",property = "creat")
            @Result(column = "uid",property = "uId"),
            @Result(column = "category",property = "category")

    })
    Article selectById(@Param("articleId") Integer id);

}

在启动类上添加mapperScan,进行mapper扫描

//扫描Mapper接口的位置
@MapperScan(basePackages = "com.zhixun.day_03_mybatis.mapper")
@SpringBootApplication
public class Day03MybatisApplication {

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

}

测试方法

	@Autowired
	private ArticleMapper mapper;

	@Test
	void test01() {

		Article article = mapper.selectById(3);
		System.out.println(article);


	}

输出结果:Article(id=3, title=Java实现编程, content=有没有漂亮妹妹, creat=2023-05-23 22:38:06, uId=1, category=111)
2.@Insert插入
    @Insert("""
            insert into article(id, title, content, creat, uid, category) 
            values (#{id},#{title},#{content},#{creat},#{uId},#{category})
            """)
    Integer insertArticle(Article article);
	@Test
	void test02() {
		Article article = new Article(59,"Java编程开发","qqqqqq","qqqqqq",1,"qqqqqq");
		System.out.println(mapper.insertArticle(article));
	}
	
输出结果:
==>  Preparing: insert into article(id, title, content, creat, uid, category) values (?,?,?,?,?,?)
==> Parameters: 60(Integer), Java编程开发(String), qqqqqq(String), qqqqqq(String), 1(Integer), qqqqqq(String)
<==    Updates: 1
3.@Update
    @Update("""
            update article set content = #{content}  where id=#{id};
            """)
    Integer updateArticleContentById(Integer id,String content);
	@Test
	void test03() {
		System.out.println(mapper.updateArticleContentById(60,"java是一种面向对象的编程"));
	}

输出结果:
==>  Preparing: update article set content = ? where id=?;
==> Parameters: java是一种面向对象的编程(String), 60(Integer)
<==    Updates: 1
4.@Delete
    @Delete("""
            delete from article where id = #{id};
            """)
    Integer deleteArticleById(Integer id);
	@Test
	void test04() {
		System.out.println(mapper.deleteArticleById(60));
	}
	
输出结果:
==>  Preparing: delete from article where id = ?;
==> Parameters: 60(Integer)
<==    Updates: 1
5.注解总结

MyBatis注解开发

1.加入mybatis的starter, mysql驱动(8.0以上)

2.创建实体类

3.创建Mapper接口,在接口定义方法,并使用合适的注解。

​ 1.@Select: 查询 (可使用@Results做结果映射)

​ 2.@Insert:新增

​ 3.@Update:更新

​ 4.@Delete:删除

4.在启动器上面,加入MapperScan

//扫描Mapper接口的位置
@MapperScan(basePackages = "com.zhixun.day_03_mybatis.mapper")

5.在application中配置mybatis

  1. 定义数据库链接

  2. mybatis设置:日志,驼峰命名

2.结果映射ResultMap

1.@ResultMap
public interface ArticleDao {

    //1.查询某个用户的所有文章
    @Select("""
            select * from article where uid = #{uid}
            """)
    @Results(id = "BaseArticleMap" ,value = {
            //@Result(id = true(是否是主键),column = "数据库的字段名",property = "实体类的属性名"),
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "title",property = "title"),
            @Result(column = "content",property = "content"),
            @Result(column = "creat",property = "creat"),
            @Result(column = "uid",property = "uId"),
            @Result(column = "category",property = "category")

    })
    List<Article> selectList(Integer uid);


    @Select("""
            select * from article where id = #{id}
            """)
    //为了不必要的频繁写Results进行结果映射
    //在同一个类中
    //可通过@ResultMap(ResultsID) 调用之前已经写好的Results结果映射
    @ResultMap("BaseArticleMap")
    //2.查询某个文章
    Article selectArticleById(Integer id);
}
    @Autowired
    private ArticleDao articleDao;

    @Test
    void test05() {
        List<Article> articles = articleDao.selectList(1);
        articles.forEach(System.out::println);
        System.out.println(articleDao.selectArticleById(5));

    }
    
输出结果:
Article(id=3, title=Java实现编程, content=有没有漂亮妹妹, creat=2023-05-23 22:38:06, uId=1, category=111)
Article(id=4, title= 大家都会迷茫吗?对自己有什么规划, content=如题。以后要做什么工作,自己擅长什么?毕业意味着失业的问题你怎么看待?有没有什么是现在可以准备的?, creat=2023-05-23 22:38:06, uId=1, category=python)
Article(id=5, title=福建工程学院更名, content=福建工程学院更名成福建理工大学, creat=2023-05-23 22:38:06, uId=1, category=springboot)

2.通过XML文件

当Result映射很多的时候,可以使用xml文件来对其进行映射

首先先在资源文件下创建mappers/XXX.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.zhixun.day_03_mybatis.mapper.ArticleDao">

    <!-- 定义ResultMap -->
    <resultMap id="ArticleMapper" type="com.zhixun.day_03_mybatis.pojo.Article">
        <id column="id" property="id"/>
        <result column="title" property="title"/>
        <result column="content" property="content"/>
        <result column="creat" property="creat"/>
        <result column="uid" property="uId"/>
        <result column="category" property="category"/>
    </resultMap>


</mapper>

并在application中配置路径

#mybatis
mybatis:
  configuration:
    #支持驼峰命名
    map-underscore-to-camel-case: true
    #日志 控制台输出
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #指定自定义mapper文件的位置

  #设置执行脚本 always:总是执行  never:不执行
    #classpath:/mappers/**/*.xml
    #表示mappers任意子目录下的xml文件
  mapper-locations: classpath:/mappers/**/*.xml

并在接口中引用 resultMap id="ArticleMapper"的id,并进行测试

    @Select("""
            select * from article where id = #{id}
            """)
    //为了不必要的频繁写Results进行结果映射
    //在同一个类中
    //可通过@ResultMap(ResultsID) 调用之前已经写好的Results结果映射
    @ResultMap("ArticleMapper")
    //2.查询某个文章
    Article selectArticleById(Integer id);

测试

    @Autowired
    private ArticleDao articleDao;

    @Test
    void test05() {
        System.out.println(articleDao.selectArticleById(5));

    }

输出结果:
    Article(id=5, title=福建工程学院更名, content=福建工程学院更名成福建理工大学, creat=2023-05-23 22:38:06, uId=1, category=springboot)

3.SQL的提供者

在这里插入图片描述

创建提供者类

public class SqlProvider {

    //定义静态方法
    public static String selectArticle(){
        return """
                select  * from article where id = #{articleId};
                """;
    }

    public static String updateArticleContentById(){
        return """
                update article set content = #{content}  where id=#{id};
                """;
    }

    public static String insertArticle(){
        return """
                insert into article(id, title, content, creat, uid, category) 
            values (#{id},#{title},#{content},#{creat},#{uId},#{category})
                """;
    }

    public static String deleteArticleById(){
        return """
                delete from article where id = #{id};
                """;
    }

}

创建接口

public interface ArticleRepository {


    //使用提供者
    @SelectProvider(type = SqlProvider.class,method = "selectArticle")
    Article selectArticle(Integer id);

    @UpdateProvider(type = SqlProvider.class,method = "updateArticleContentById")
    Integer updateArticleContentById(Integer id,String context);

    @InsertProvider(type = SqlProvider.class,method = "insertArticle")
    Integer insertArticle(Article article);

    @DeleteProvider(type = SqlProvider.class,method = "deleteArticleById")
    Integer deleteArticleById(Integer id);

}

测试方法

    @Autowired
    private ArticleRepository articleRepository;

    @Test
    void test06() {
        System.out.println(articleRepository.selectArticle(5));

    }

4.@One一对一

在这里插入图片描述

创建一对一关系的两个类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer userId;
    private String name;
    private String gender;
    private Integer age;
    private String school;
    private String identity;
    private String userAccountUsername;

}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserAccount {
    private String userAccountUsername;
    private String password;
    private String email;

    //一对一的关系
    private User user;
}
public interface UserOneToOne {


    //查询账户信息
    @Select("""
            select * from user_account where user_account_username = #{username}
            """)
    @Results({
            @Result(id = true,column = "user_account_username",property = "userAccountUsername"),
            @Result(column = "password",property = "password"),
            @Result(column = "email",property = "email"),
            //将column字段的值传给selectUserInfo方法 
        	//等selectUserInfo方法查询完之后 在将查询的值赋给user
            @Result(column = "user_account_username",property = "user",
            //一对一的关系 select是 查询user方法的全类名
            one = @One(select = "com.zhixun.day_03_mybatis.mapper.UserOneToOne.selectUserInfo"
                    //fetchType检索 FetchType.LAZY懒加载模式 当需要了在加载
                    ,fetchType = FetchType.LAZY))
    })
    UserAccount selectUserAccount(@Param("username") String username);


    //查询用户信息
    @Select("""
            select * from user where user_account_username = #{username}
            """)
    @Results({
            @Result(id = true,column = "user_id",property = "userId"),
            @Result(column = "name",property = "name"),
            @Result(column = "gender",property = "gender"),
            @Result(column = "age",property = "age"),
            @Result(column = "school",property = "school"),
            @Result(column = "identity",property = "identity"),
            @Result(column = "user_account_username",property = "userAccountUsername")
    })
    User selectUserInfo(@Param("username") String username);
}

测试

    @Test
    void test07() {
        UserAccount userAccount = userOneToOne.selectUserAccount("zzx");
        System.out.println(userAccount);

    }
    
输出结果:
<==    Columns: user_id, name, gender, age, school, identity, user_account_username
<==        Row: 6, 张志勋,, 32, 北京大学, 教师, zzx
<==      Total: 1
UserAccount(userAccountUsername=zzx, password=$2a$10$WUw/JV88.r98LHS6W46KRurAtC4GDf9sQMnBWnQE2h0ZWWb62gRQC, email=4@qq.com, user=User(userId=6, name=张志勋, gender=, age=32, school=北京大学, identity=教师, userAccountUsername=zzx))

5.@Many一对多

创建一个一对多的关系类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer userId;
    private String name;
    private String gender;
    private Integer age;
    private String school;
    private String identity;
    private String userAccountUsername;

    private List<Article> articleList;

}

一对多用法其实跟一对一一样,只是一个是对应一条数据,一个是对应多条数据。

    public interface UserOneToMany {

    @Select("""
            select * from user where user_id = #{id}
            """)
    @Results({
            @Result(id = true,column = "user_id",property = "userId"),
            @Result(column = "name",property = "name"),
            @Result(column = "gender",property = "gender"),
            @Result(column = "age",property = "age"),
            @Result(column = "school",property = "school"),
            @Result(column = "identity",property = "identity"),
            @Result(column = "user_account_username",property = "userAccountUsername"),
            @Result(column = "user_id",property = "articleList",
            many = @Many(select = "com.zhixun.day_03_mybatis.mapper.UserOneToMany.selectByUid")
                   ,fetchType = FetchType.LAZY)
    })
    User selectUserById(Integer id);


    @Select("""
            select * from article where uid = #{id}
            """)
    List<Article> selectByUid(Integer id);

测试

    @Autowired
    private UserOneToMany userOneToMany;

    @Test
    void test08() {
        User user = userOneToMany.selectUserById(1);
        System.out.println(user);

    }


输出结果:
    User(userId=1, name=行太官方, gender=, age=30, school=行太学堂, identity=教师, userAccountUsername=xt, 	
    articleList=[
        Article(id=3, title=Java实现编程, content=有没有漂亮妹妹, creat=2023-05-23 22:38:06, uId=1, category=111), 
        Article(id=4, title= 大家都会迷茫吗?对自己有什么规划, content=如题。以后要做什么工作,自己擅长什么?毕业意味着失业的问题你怎么看待?有没有什么是现在可以准备的?, creat=2023-05-23 22:38:06, uId=1, category=python), 
        Article(id=5, title=福建工程学院更名, content=福建工程学院更名成福建理工大学, creat=2023-05-23 22:38:06, uId=1, category=springboot)

6.MybatisAutoConfigration

在这里插入图片描述

7.连接池

Hikari 连接池 默认的
连接池分三种:Tomcat,DBCP,Hikari

在这里插入图片描述

mysql优化设置

在这里插入图片描述

4.声明式事务

在这里插入图片描述

在这里插入图片描述

@Service
public class UserAccountServiceImpl implements UserAccountService {

    //注入mapper
    @Autowired
    private userMapper userMapper;


    /**
     * 
     * @Transactional:事务控制注解
     *  位置: 方法上/类上
     *  事务回滚:
     *      1.默认对运行时异常,执行回滚 rollback
     *       @Transactional(rollbackFor = {IOException.class})
     *      2.rollbackFor: 是需要回滚异常类的列表
     *		3.spring框架中发现是运行时异常时,如果有事务注解时,此行为会被取消,事务将会执行回滚
     * */
    @Transactional
    @Override
    public boolean insertUserAccount(UserAccount userAccount) {

        int row = userMapper.userAccountInsert(userAccount);

        //抛出异常
        if (row < 1){
            throw new RuntimeException("账号添加失败");
        }
        User user = new User();
        user.setUserAccountUsername(userAccount.getUserAccountUsername());
        int userRow = userMapper.userInsert(user);


        return (row + userRow) >= 2;
    }

}


// 开启事务管理器 可加可不加
@EnableTransactionManagement
@MapperScan("com.example.day_03_trans.mappers")
@SpringBootApplication
public class Day03TransApplication {

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

}

在这里插入图片描述在这里插入图片描述

事务的回滚原则

在这里插入图片描述

5.总结

=====================
提供sql语句,使用提供者类
@SelectProvider
@InsertProvider
@UpdateProvider
@DeleteProvider
提供者类:定义一个普通类,定义一个静态方法,放的放回置是要执行的sql语句
=====================
关系 @One一对一 @Many一对多
=====================
MybatisAutoConfiguration 自动配置类
1. SqlsessionFactory 能够获取Sqlsession
2. SqlSessionTemplate 用于执行sql语句 mybatis和spring整合时,使用的模板对象
3. MapperFactoryBean:创建dao接口的代理对象
=====================
连接池,连接的大小控制
connections = ((cpu 核心数 * 2) + 磁盘数量) 近似值。 默认10
=====================
事务:spring的事务管理
采用声明式事务:注解方式@Transaction,在public方法上面加入注解
控制事务: 1.传播行为  2.隔离级别  3.只读  4.超过时间
回滚规则:
	1.业务方法发送RunTimeException 和 Error 将会回滚事务
	2.@Transactional的rollbackFor控制事务的回滚类型

五、Web服务

在这里插入图片描述

1.页面视图

导入对应依赖

<!-- ThymeLeaf视图依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!-- Spring Web依赖 包含:SpringMVC,Tomcat服务器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

创建一个controller类

//@Controller:创建控制对象,控制器能够接收请求,响应结果给浏览器
@Controller
public class QuickController {

    //定义方法处理请求,方法叫做控制器方法(处理器方法)
    //Model表示模型,存储数据。这个数据最后放在request作用域
    //HttpServletRequest这个作用域中
    @RequestMapping("/quick")
    public String quick(Model model){
        //调用Service,处理请求,获取数据
        model.addAttribute("title","quick");
        model.addAttribute("time", LocalDateTime.now());

        //指定视图显示数据
        return "quick";
    }

}

在templates文件夹下创建对应的html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <p th:text="${title}"></p>
    <p th:text="${time}"></p>
</body>
</html>

启动类运行

浏览器显示结果:
浏览器请求:http://localhost:8080/quick
quick
2023-10-15T10:14:21.671589300

2.Json视图

在这里插入图片描述

创建一个User类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private Integer age;
}

创建Controller测试

@Controller
public class JsonViewController {

    //显示Json视图
    @RequestMapping("/json")
    public void responseJson(HttpServletResponse response) throws IOException {
        String json = "{\"name\":\"lisi\"}";
        //应答,通过HttpServletResponse输出
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println(json);
        out.flush();
        out.close();
    }

    //SpringMVC支持控制器放回对象,由框架将对象使用jackson转为json,并输出
    // @ResponseBody 返回json数据
    //User -> Jackson工具库的ObjectMapper对象,将数据转为json格式
    @ResponseBody
    @RequestMapping("/user")
    public User getUserJson(){
        return new User("zhixun",22);

    }

}


浏览器显示结果:{"name":"zhixun","age":22}

3.SpringMVC

1.Controller控制器

在这里插入图片描述

2.匹配请求路径到控制器方法

在这里插入图片描述

在application中配置

#路径匹配的策略
spring:
  mvc:
    pathmatch:
      matching-strategy: path_pattern_parser

创建controller进行测试

@RestController
public class ExamPathController {
    //?可代表1个字符
    @GetMapping("/uz?.html")
    public User path1(){
        return new User("uz",12);
    }

    //* 可代表0-多个字符
    @GetMapping("/u*er.html")
    public User path2(){
        return new User("zhixun",12);
    }

    //** 可代表0-多个字符
    //代表以pic开头的请求都是正确的
    @GetMapping("pic/**")
    public User path3(){
        return new User("pic",12);
    }
    
    //路径变量
    //{*myname}:匹配多个路径一直到url的结尾
    //注意:@GetMapping("order/{*id}/{*date}")无效的,{*..}后面不能在有匹配规则了
    @GetMapping("order/{*id}")
    public String path4(@PathVariable("id") String orderId, HttpServletRequest request){
        return "path4: " + request.getRequestURI() + ", id = " + orderId;
    }
   
    请求:GET http://localhost:8080/order/1001/2023-10-15
    输出结果:path4: /order/1001/2023-10-15, id = /1001/2023-10-15
        
    //正则表达式
    //必须以.log结尾
    @GetMapping("/pages/{fname:\\w+}.log")
    public String path5(@PathVariable("fname") String fName, HttpServletRequest request){
        return "path5: " + request.getRequestURI() + ", fName = " + fName;
    }
    
    请求:GET http://localhost:8080/pages/req.log
    输出结果:path5: /pages/req.log, fName = req
        
    请求:GET http://localhost:8080/pages/###.log
    输出结果:报错 404
    
}
3.@RequestMapping

在这里插入图片描述

4.控制器方法参数类型与可用返回值类型

在这里插入图片描述
在这里插入图片描述

5.接收请求

在这里插入图片描述

创建@Controller测试简单数据,和对象如何接收参数。

@RestController
public class ParameterController {
	
    //GET http://localhost:8080/param?name=lisi&age=20&sex男
    //一一对应,适合接收简单类型数据String,int,long,double,float,参数数量较少情况下
    @GetMapping("/param")
    public String p1(String name,Integer age,String sex){
        return "接收参数:" + name + "," + age + "," + sex;
    }

	
    //GET http://localhost:8080/param/p2?name=lisi&age=20
    //使用对象接收参数,要求对象的属性名称和请求中的参数名一样,属性有set方法,类有无参构造方法
    @GetMapping("/param/p2")
    public String p2(User user){
        return user.toString();
    }

    
    //使用HttpServletRequest接收参数
    @GetMapping("/param/p3")
    public String p3(HttpServletRequest request) {
        String name = request.getParameter("name");
        String age = request.getParameter("age");
        String sex = request.getParameter("sex");
        return "name = " + name + ", age = " + age + ", sex = " + sex;
    }
    
    //使用@RequestParam接收参数
    //@RequestParam(value = "name",required = true) 表示请求中name必须存在
    //如果required = false 则可以写默认值当不存在时 将使用默认值来进行传值
    @GetMapping("/param/p4")
    public String p4(@RequestParam(value = "name",required = true) String name,
                     @RequestParam(value = "age",required = false,defaultValue = "22")String age,
                             @RequestParam(value = "sex",required = true) String sex) {

        return "name = " + name + ", age = " + age + ", sex = " + sex;
    }
    
        /**
     *  当前端数据为json格式的时候
     *  @RequestBody:从请求体中读取json数据,将数据转为形参对象的属性值
     */
    @PostMapping("/param/json")
    public String p5(@RequestBody User user) {

        return "json user:" + user.toString();
    }
    
    注意传的时候 需要添加头
    POST http://localhost:8080/param/json
    Content-Type: application/json

    {"name":"张三","age": "22"}


}
6.数组接收多个参数
   POST http://localhost:8080/param/vals?id=11&id=12&id=31
POST http://localhost:8080/param/vals?id=1,2,3,4,5

	@PostMapping("/param/vals")
    public String p6(Integer[] id) {
        List<Integer> list = Arrays.stream(id).toList();
        return list.toString();
    }

2.Java Bean Validation

在这里插入图片描述
在这里插入图片描述

1.快速上手

创建一个类,并用注解进行验证

import jakarta.validation.constraints.*;
import lombok.Data;

/**
 * @author zhixun
 * @date 2023/10/16 9:06
 * @describe
 */
@Data
public class ArticleVo {


    private Integer id;
    //不能为空 否则提示message
    @NotNull(message = "必须有作者")
    private Integer userId;

    //不能为空串
    @NotBlank(message = "文章必须有空格")
    //@Size 认为 null是有效值 所以需要加一个@NotBlank
    @Size(min = 3,max = 30,message = "文章标题必须在3个字符以上")
    private String title;

    @NotBlank(message = "必须有副标题")
    //@Size 认为 null是有效值 所以需要加一个@NotBlank
    @Size(min = 5,max = 30,message = "文章副标题必须在5个字符以上")
    private String summary;

    //阅读数量不能小于零,@DecimalMin支持小数
    @DecimalMin(value = "0",message = "阅读数量不能小于零")
    private Integer readCount;

    @Email(message = "邮箱不符合规则")
    private String email;

}

创建controller类进行测试

@RestController
public class ArticleController {

    //发布文章
    //加入@Validated注解表示 验证后面这个BeanArticleVo article
    //BindingResul 存储的就是验证Bean的结果数据
    @PostMapping("/article/add")
    public Map<String, Object> addArticle(@Validated @RequestBody ArticleVo article, BindingResult br) {

        Map<String, Object> map = new HashMap<>();

        if (br.hasErrors()) {
            //获取没有通过属性的字段
            List<FieldError> fieldErrors = br.getFieldErrors();
            for (int i = 0; i < fieldErrors.size(); i++) {
                FieldError field = fieldErrors.get(i);
                map.put(i + "-" + field.getField(), field.getDefaultMessage());
            }
        }

        return map;

    }

}

测试数据

###
POST http://localhost:8080/article/add
Content-Type: application/json

{
  "id": 0,
  "userId": 10,
  "title": "title_bba28cf2ad9e",
  "summary": "summary_46964894e1fd",
  "readCount": 1,
  "email": "aaa163.com"
}

输出结果:
{
  "0-email": "邮箱不符合规则"
}
2.分组校验

在这里插入图片描述

@Data
public class ArticleVo {


    //组就是接口名
    public static interface AddArticleGroup {
    }

    ;

    public static interface EditArticleGroup {
    }

    ;

    //当是编辑组的时候 这段注解才生效
    @NotNull(message = "文章id必须有值", groups = {EditArticleGroup.class})
    @Min(value = 1, message = "id大于0", groups = {EditArticleGroup.class})
    private Integer id;

    //不能为空 否则提示message
    @NotNull(message = "必须有作者", groups = {AddArticleGroup.class, EditArticleGroup.class})
    private Integer userId;

    //不能为空串
    @NotBlank(message = "文章必须有空格", groups = {AddArticleGroup.class, EditArticleGroup.class})
    //@Size 认为 null是有效值 所以需要加一个@NotBlank
    @Size(min = 3, max = 30, message = "文章标题必须在3个字符以上", groups = {AddArticleGroup.class, EditArticleGroup.class})
    private String title;

    @NotBlank(message = "必须有副标题", groups = {AddArticleGroup.class, EditArticleGroup.class})
    //@Size 认为 null是有效值 所以需要加一个@NotBlank
    @Size(min = 5, max = 30, message = "文章副标题必须在5个字符以上", groups = {AddArticleGroup.class, EditArticleGroup.class})
    private String summary;

    //阅读数量不能小于零,@DecimalMin支持小数
    @DecimalMin(value = "0", message = "阅读数量不能小于零", groups = {AddArticleGroup.class, EditArticleGroup.class})
    private Integer readCount;

    @Email(message = "邮箱不符合规则", groups = {AddArticleGroup.class, EditArticleGroup.class})
    private String email;

}

测试

###
POST http://localhost:8080/article/add
Content-Type: application/json

{
  "id": null,
  "userId": 10,
  "title": "title_bba28cf2ad9e",
  "summary": "summary_46964894e1fd",
  "readCount": 1,
  "email": "aaa@163.com"
}

###
POST http://localhost:8080/article/edit
Content-Type: application/json

{
  "id": 2,
  "userId": 10,
  "title": "title_bba28cf2ad9e",
  "summary": "summary_46964894e1fd",
  "readCount": 1,
  "email": "aaa@163.com"
}
3.ValidationAutoConfiguration

在这里插入图片描述

3.Model

在这里插入图片描述

//@Controller:创建控制对象,控制器能够接收请求,响应结果给浏览器
@Controller
public class QuickController {

    //定义方法处理请求,方法叫做控制器方法(处理器方法)
    //Model表示模型,存储数据。这个数据最后放在request作用域
    //HttpServletRequest这个作用域中
    @RequestMapping("/quick")
    public String quick(Model model){
        //调用Service,处理请求,获取数据
        model.addAttribute("title","quick");
        model.addAttribute("time", LocalDateTime.now());

        //指定视图显示数据
        return "quick";
    }
}

4.视图 View

在这里插入图片描述

5.总结

在这里插入图片描述
在这里插入图片描述

4.SpringMVC请求流程

在这里插入图片描述

1.DispatcherServlet是一个Servlet

在这里插入图片描述
在这里插入图片描述

2.SpringMVC 完整流程

在这里插入图片描述

5.自动配置

在这里插入图片描述

1.服务器配置

在这里插入图片描述

server:
  #服务器端口
  port: 8088
  #上下文访问路径
  servlet:
    context-path: /api
  #字符编码
    encoding:
      charset: utf-8
      #强制 request,response设置charset字符编码
      force: true
  #Tomcat日志路径
  tomcat:
    accesslog:
      #日志路径
      directory: D:/logs
      #访问日志权限
      enabled: true
      #日志文件名前缀
      prefix: access_log
      #日志文件信息
      file-date-format: yyyt-MM-dd
      #日志名称后最
      suffix: .log
    #post 请求内容最大值,默认2M
    max-http-form-post-size: 2000000
    #服务器最大连接
    max-connections: 8192

2.DispatcherServlet

#路径匹配的策略
spring:
  mvc:
    pathmatch:
      matching-strategy: path_pattern_parser
    #配置DispatcherServlet
    servlet:
      path: /course
      load-on-startup: 0
    #全局的日期格式 但是日期格式必须为2023-10-17这种
    format:
      date-time: yyyy-MM-dd HH:mm:ss

3.总结

在这里插入图片描述

6.Servlets,Filters,and Listeners

在这里插入图片描述

1.@WebServlet

创建类

/*
* @WebServlet:等同于web.xml中 有关servlet的声明
* <servlet>
*   <servlet-name>HelloServlet</servlet-name>
*   <servlet-class>xxxx</servlet-class>
* </servlet>
*
* <servlet-mapping>
*   <url-pattern>/helloServlet</url-pattern>
*</servlet-mapping>
*
* */
@WebServlet(urlPatterns = "/helloServlet",name = "HelloServlet")
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;character=utf-8");
        PrintWriter out = resp.getWriter();
        out.println("这是Sptingboot中的servlet");
        out.flush();
        out.close();
    }
}

在启动项配置扫描信息

//扫描@WebServlet注解
@ServletComponentScan(basePackages = "com.example.day_05_servletfilter")
@SpringBootApplication
public class Day05ServletFilterApplication {

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

}

测试请求:http://localhost:8080/helloServlet

1.编码创建servlet

创建类

public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;character=utf-8");
        PrintWriter out = resp.getWriter();
        out.println("这是login的servlet");
        out.flush();
        out.close();
    }
}

创建一个配置类

@Configuration
public class webAppConfig {

    @Bean
    public ServletRegistrationBean servletRegistrationBean(){
        //创建ServletRegistrationBean对象 登记一个或者多个Servlet
        ServletRegistrationBean registrationBean = new ServletRegistrationBean();
        registrationBean.setServlet(new LoginServlet());
        registrationBean.addUrlMappings("/user/login");
        registrationBean.setLoadOnStartup(1);
        return registrationBean;
    }

}

测试请求:http://localhost:8080/user/login

2.创建Filter (过滤器)

Filter对象是使用频率比较高,比如日志记录,权限验证,敏感字符过滤等。Web框架中包含内置的Filter,SpringMvc中也包含较多的内置Filter,比如CommonsRequestLoggingFilter(记录请求日志),CorsFilter(全局跨域处理),FormContentFilter(用来支持put,delete)…

1.@WebFilter

创建Filter类

// “/*” 访问web应用请求时都要经过过滤器
@WebFilter(urlPatterns = "/*")
public class LogFilter implements Filter {


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String url = ((HttpServletRequest)request).getRequestURI().toString();
        System.out.println("过滤器-------" + url);
        chain.doFilter(request,response);

    }
}

在启动器上加入扫描注解

@ServletComponentScan(basePackages = "com.example.day_05_servletfilter")
@SpringBootApplication
public class Day05ServletFilterApplication {

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

}

因为设置的是‘/ *’ 所以当浏览器不管发送扫描请求都会经过过滤器
    控制台输出:过滤器------- /user/login

2.FilterRegistrationBean

FilterRegistrationBean和ServletRegistrationBean用法相同,无需注解。

创建WebConfig类

@Configuration
public class webAppConfig {

    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        //登记filter对象
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new LogFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }

}

因为设置的是‘/ *’ 所以当浏览器不管发送扫描请求都会经过过滤器
    控制台输出:过滤器------- /user/login
3.Filter排序

在这里插入图片描述

如果不在WebConfig中设置的话,是通过类名按顺序执行

WebConfig.class
    @Bean
	//登录Filter 指定顺序
    public FilterRegistrationBean addLogFilter(){
        FilterRegistrationBean logFilter = new FilterRegistrationBean<>();
        logFilter.setFilter(new LogFilter());
        logFilter.addUrlPatterns("/*");
        //设置顺序
        logFilter.setOrder(2);
        return logFilter;
    }
	@Bean
    public FilterRegistrationBean addAuthFilter(){
        FilterRegistrationBean authFilter = new FilterRegistrationBean<>();
        authFilter.setFilter(new LogFilter());
        authFilter.addUrlPatterns("/*");
        //设置顺序 顺序越小 越先执行
        authFilter.setOrder(1);
        return authFilter;
    }
4.使用Filter框架

在这里插入图片描述

    @Bean
    //登记框架内置的Filter
    public FilterRegistrationBean addCommonLogFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        CommonsRequestLoggingFilter commonsRequestLoggingFilter = new CommonsRequestLoggingFilter();
        //记录请求的url地址
        commonsRequestLoggingFilter.setIncludeClientInfo(true);
        registrationBean.setFilter(commonsRequestLoggingFilter);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

在这里插入图片描述

因为commons是debug级别,所以需要在配置文件中进行级别配置。

#设置日志为debug
logging.level.web=debug

3.Listeners (监听器)

在这里插入图片描述

7.WebMvcConfigurate

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.页面控制器

/**
 * @author zhixun
 * @date 2023/10/17 10:43
 * @describe SpringMVC的配置类:使用JavaConfig框架的方式配置SpringMVC,代替原来的xml配置文件
 */

@Configuration
public class MvcSettings implements WebMvcConfigurer {

    //页面跳转控制器,从请求直达视图页面(无需controller)
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //页面配置控制:addViewController("请求uri地址"),指定他的视图setViewName("目标视图")
        //请求/welcome   文件路径index.html
        registry.addViewController("/welcome").setViewName("index");
    }
}

请求:http://localhost:8080/welcome

2.数据格式化

在这里插入图片描述
在这里插入图片描述

创建DeviceInfo类

@Data
public class DeviceInfo {
    private String item1;
    private String item2;
    private String item3;
    private String item4;
    private String item5;


}

创建转换器 当控制器接收到device类型的数据要进行解析时,会先找有没有相对应转换器。如果有就将拿去到值,去转换器调用parse方法,对其进行解析。

public class DeviceFormatter implements Formatter<DeviceInfo> {


    //text表示请求参数的值
    @Override
    public DeviceInfo parse(String text, Locale locale) throws ParseException {
        DeviceInfo info = null;
        if (StringUtils.hasLength(text)) {
            String[] items = text.split(";");
            info = new DeviceInfo();
            info.setItem1(items[0]);
            info.setItem2(items[1]);
            info.setItem3(items[2]);
            info.setItem4(items[3]);
            info.setItem5(items[4]);

        }
        return info;
    }

    @Override
    public String print(DeviceInfo object, Locale locale) {
        StringJoiner joiner = new StringJoiner("#");
        joiner.add(object.getItem1()).add(object.getItem2()).add(object.getItem3())
                .add(object.getItem4()).add(object.getItem5());
        return joiner.toString();
    }
}

创建controller

@RestController
public class DeviceController {

    @PostMapping("/device/add")
    public String addDeviceInfo(@RequestParam("device")DeviceInfo info){
        return "接收的设备信息" + info.toString();
    }

}

测试:
    ###
	POST http://localhost:8080/device/add
	Content-Type: application/x-www-form-urlencoded

	device = 1111; 2222; 333,NF; 4; 561
        
输出结果:接收的设备信息DeviceInfo(item1=1111, item2= 2222, item3= 333,NF, item4= 4, item5= 561)

3.拦截器

在这里插入图片描述

一个拦截器

创建controller

@RestController
public class ArticleController {

    @PostMapping("/article/add")
    public String addArticle(){
        return "发布新的文章";
    }

    @PostMapping("/article/edit")
    public String editArticle(){
        return "编辑文章";
    }

    @GetMapping("/article/query")
    public String queryArticle(){
        return "查询文章";
    }

    @DeleteMapping("/article/remove")
    public String removeArticle(){
        return "删除文章";
    }

}

创建拦截器

//拦截器
public class AuthInterceptor implements HandlerInterceptor {

    private static final String COMMON_USER = "ZhiXun";

    //判断登录的用户能否执行相应的动作
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("==========AuthInterceptor权限拦截器");

        //登录的用户
        String loginUser = request.getParameter("loginUser");
        //获取请求的uri地址
        String requestURI = request.getRequestURI();
        //判断用户和操作
        //当用户是zhixun时 只能使用查询功能
        if (COMMON_USER.equals(loginUser) &&
                (requestURI.startsWith("/article/add")||
                requestURI.startsWith("/article/edit")||
                requestURI.startsWith("/article/remove"))){
            return false;
        }
        return true;
    }
}

在实现WebMvcConfiguere中重写addInterceptors方法

//拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        AuthInterceptor authInterceptor = new AuthInterceptor();
        registry
                .addInterceptor(authInterceptor) //添加拦截器
                .addPathPatterns("/article/**")     //添加拦截器地址
                .excludePathPatterns("/article/query"); //排除拦截地址
    }
多个拦截器

在这里插入图片描述

在创建一个拦截器类

public class LoginInterceptor implements HandlerInterceptor {

	
    private List<String> permitUser = new ArrayList<>();
	
    public LoginInterceptor(){
        this.permitUser = Arrays.asList("zhangsan","lisi","admin");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("===========登录拦截器");
        //获取的用户名
        String loginUser = request.getParameter("loginUser");

        //只有三个用户能够访问系统
        if (StringUtils.hasText(loginUser) && permitUser.contains(loginUser)){
            return true;
        }
            return false;
    }
}

在一个拦截器的基础上,再加一个拦截器

    //拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //权限拦截器
        AuthInterceptor authInterceptor = new AuthInterceptor();
        registry
                .addInterceptor(authInterceptor) //添加拦截器
                .addPathPatterns("/article/**")     //添加拦截器地址
                .excludePathPatterns("/article/query"); //排除拦截地址

        //登录拦截器
        LoginInterceptor loginInterceptor = new LoginInterceptor();
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") //拦截所有controller
                .addPathPatterns("/article/query");
    }
}

8.文件上传

1.MultipartResolver

在这里插入图片描述

创建文件上传的html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>文件上传成功</h1>
    <form action="uploadFile" enctype="multipart/form-data" method="post">
        选择文件:<input type="file" name="upfile"><br>

        <input type="submit" value="上传">
    </form>
</body>
</html>

创建Controller

Controller
public class UploadFileController {


    //上传文件的控制器方法
    @PostMapping("/uploadFile")
    public String uploadFile(@RequestParam("upfile")MultipartFile multipartFile){
        System.out.println("开始处理上传文件");
        Map<String,Object> info = new HashMap<>();

        try {
            //判读上传了文件
            if (!multipartFile.isEmpty()){
                info.put("上传文件的参数名称",multipartFile.getName()); //upfile
                info.put("内容类型",multipartFile.getContentType());

                var ext = "unknown";
                var filename = multipartFile.getOriginalFilename();  //原始文件的名称,例如a.gif
                if (filename.indexOf(".") > 0 ){
                    ext = filename.substring(filename.lastIndexOf("."));

                }

                //生成服务器使用的文件名称
                var newFileName = UUID.randomUUID().toString() + ext;
                var path = "D://upload//" + newFileName; //存储服务器的文件

                //把文件保存到path目录
                multipartFile.transferTo(new File(path));

                info.put("文件名称",newFileName);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        //重定向到index
        return "redirect:/index.html";
    }

}

再springboot中还能创建5xx.html,4xx.html等,默认就是当发送5xx错误会自动跳转到5xx页面。

2.Servlet规范

在这里插入图片描述

现在使用的是基于Servlet6

创建controller

@Controller
public class UploadAction {

    @PostMapping("/files")
    public String upload(HttpServletRequest request) {
        try {
            for (Part part : request.getParts()) {
                String filename =  extractFileName(part);
                //将文件写入到服务器的目录
                part.write(filename);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "redirect:/index.html";
    }


    private String extractFileName(Part part) {
        String contentDisp = part.getHeader("content-disposition");
        String[] items = contentDisp.split(";");
        for (String item : items) {
            if (item.trim().startsWith("filename")) {
                return item.substring(item.indexOf("=") + 2, item.length() - 1);
            }
        }

        return "";
    }

}

需要再application配置存储路径

#配置上传文件的输出目录
spring.servlet.multipart.location=D://upload

3.多文件上传

多文件上传,再接收文件参数部分有所改变MultipartFile[] files。循环遍历数组解析每个上传的文件。

创建html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>文件上传成功</h1>
    <form action="files" enctype="multipart/form-data" method="post">
        选择文件:<input type="file" name="upfile"><br>
        选择文件:<input type="file" name="upfile"><br>
        选择文件:<input type="file" name="upfile"><br>
        <input type="submit" value="上传">
    </form>
</body>
</html>

创建controller

@Controller
public class UploadFileMultiController {


    //上传文件的控制器方法
    @PostMapping("/uploadFile")
    public String uploadFile(@RequestParam("upfile") MultipartFile[] multipartFiles) {
        System.out.println("开始处理上传文件");
        Map<String, Object> info = new HashMap<>();

        try {
            for (MultipartFile multipartFile : multipartFiles) {
                //判读上传了文件
                if (!multipartFile.isEmpty()) {
                    info.put("上传文件的参数名称", multipartFile.getName()); //upfile
                    info.put("内容类型", multipartFile.getContentType());

                    var ext = "unknown";
                    var filename = multipartFile.getOriginalFilename();  //原始文件的名称,例如a.gif
                    if (filename.indexOf(".") > 0) {
                        ext = filename.substring(filename.lastIndexOf("."));

                    }

                    //生成服务器使用的文件名称
                    var newFileName = UUID.randomUUID().toString() + ext;
                    var path = "D://upload//" + newFileName; //存储服务器的文件

                    //把文件保存到path目录
                    multipartFile.transferTo(new File(path));

                    info.put("文件名称", newFileName);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        //重定向到index
        return "redirect:/index.html";
    }

}

9.全局异常

在这里插入图片描述

1.全局异常处理器

创建controller

@RestController
public class NumberController {

    @PostMapping("/divide")
    public String some(Integer n1,Integer n2){
        int result = n1/n2;
        return  "n1 / n2 = " + result;
    }

}

创建异常处理器

/*
*  1. 在类的上面加入@ControllerAdvice,@RestControllerAdvice
*       灵活组合@ControllerAdvice,@ResponseBody
*  2. 在类中自定义方法,处理各种异常
*       方法定义同Controller类中的方法的定义
*
*/

//Advice在AOP中 叫增强
//控制器增加,给Controller增强异常处理功能。类似AOP的思想
@ControllerAdvice
public class GlobalExceptionHandle {

    //定义方法处理:数学异常
    /*
    *
    *@ExceptionHandler 指定处理异常的方法
    *   位置:在方法上面
    *   属性:是异常类型class数组,如果你的系统抛出的异常类型与@ExceptionHandler声明的相同,由当前的方法来处理异常
    * */
    @ExceptionHandler({ ArithmeticException.class})
    public String handlerArtithmeticException(ArithmeticException e, Model model){
        String error = e.getMessage();
        model.addAttribute("error",error);
        return "exp";//视图
    }

}

创建html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <form action="/divide" method="post">
    除数:<input type="text" name="n1"><br>
    被除数:<input type="text" name="n2"><br>
    <input type="submit" value="计算">
  </form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>显示异常信息</h1>
    <p th:text="${error}"></p>
</body>
</html>

在这里插入图片描述

2.异常处理返回json数据

    @ExceptionHandler({ ArithmeticException.class})
    @ResponseBody public Map<String,String> handlerArtithmeticException(ArithmeticException e, Model model){
        Map<String,String> error = new HashMap<>();
        error.put("msg",e.getMessage());
        error.put("tips","被除数不能为0");
        return error;//json 数据
    }

3.BeanValidator异常处理

在这里插入图片描述

创建实体类

@Data
public class OrderVo {

    @NotBlank(message = "订单名称不能为空")
    private String name;

    @NotNull(message = "商品必须有数量")
    @Range(min = 1,max = 99,message = "一个订单的商品数量在{min} -- {max}")
    private Integer amount;

    @NotNull(message = "用户不能为空")
    @Min(value = 1,message = "从1开始")
    private Integer userId;


}

创建controller类

@RestController
public class OrderController {

    @PostMapping("/order/new")
    public String createOrder(@Validated  @RequestBody OrderVo orderVo){
        return "订单信息:" + orderVo.toString();
    }

}
   //处理JSR303 验证参数的异常
    @ExceptionHandler({BindException.class})
    @ResponseBody public Map<String, Object> handlerJSR303Exception(BindException e){
        System.out.println("===============J303=============");
        //BindException是MethodArgumentNotValidException父类
        Map<String,Object> map = new HashMap<>();
        BindingResult result = e.getBindingResult();
        if (result.hasErrors()){
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (int i = 0; i < fieldErrors.size(); i++) {
                FieldError field = fieldErrors.get(i);
                map.put(i + "-" + field.getField(),field.getDefaultMessage());
            }
        }
        return map;
    }

测试方法

###
POST http://localhost:8080/order/new
Content-Type: application/json

{
  "name": "每日订单",
  "amount": 0,
  "userId": 10
}

输出结果:
{
  "0-amount": "一个订单的商品数量在1 -- 99"
}

4.ProblemDetail

在这里插入图片描述

1.RFC 7807

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.自定义异常处理器ProblemDetail

在这里插入图片描述

10.远程访问@HttpExchange

在这里插入图片描述

六、AOT

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.AOT操作

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值