SpringBoot2

一.Spring介绍

1.基本介绍:

优点:创建独立的Spring应用
           内嵌web服务器
           自动starter依赖,简化构建配置
           自动配置Spring以及第三方功能
           提供生产级别的监控,健康检查和外部化配置
           无代码生成,无需编写XML
缺点:版本迭代太快,内部原理复杂,不容易精通

微服务:微服务是一种架构风格
               一个应用拆分为一组小型任务
               每个服务运行在自己的进程内,也就是可以独立部署和升级
               服务之间使用轻量级HTTP交互
               服务围绕业务功能拆分
               可以由全自动部署机制独立部署
               去中心化,服务自治,服务可以使用不同的语言,不同的存储技术
分步式:远程调用,服务发现,负载均衡,服务容错,服务监控,链路追踪,日志管理,任务调度
             (可以用SpringBoot+Spring Cloud解决)
云原生:服务自愈,弹性伸缩,服务隔离,自动化部署,灰度发布,流量治理
              (使用Docker,星际级容器编排KUbernetes)

2.依赖管理:

父项目做依赖管理版本仲裁

   <!--这是我们导入的父项目-->
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.3.4.RELEASE</version>
   </parent>


  <!--这是上面那个玩意里面的父项目-->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
  </parent>

可以看到版本号都在这里规定好了,几乎声明了所有的常用的依赖的版本号,这就是自动版本仲裁机制。如果引入了非版本仲裁的jar包,还是要自己设置版本号的。

如果想要自己声明版本号:
1.查看spring-boot-dependencies中里面规定版本用的key,比如mysql的就是mysql.version
2.然后在当前项目里重写配置。例子如下:

    <properties>
        <mysql.version>5.1.43</mysql.version>
    </properties>

    <dependencies>
        <!--记得要在子工程中把依赖导进来,父工程只是规定,他没有引jar包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

场景启动器:

        1.我们之后会见到很多的spring-boot-starter-*  : *表示某种场景
        2.只要引入starter,这个场景的所有常规需要的依赖我们都能自动导入
        3.springboot支持所有的场景
        4.见到的*-spring-boot-starter  :  这些是第三方给我们提供的简化开发的场景启动器
        5.所有的场景启动器最底层的依赖都是:
                <dependency>
                         <groupId>org.springframework.boot</groupId>
                         <artifactId>spring-boot-starter</artifactId>
                         <version>2.3.4.RELEASE</version>
                         <scope>compile</scope>
                </dependency>

3.自动配置

        自动配置好tomcat:
                引入Tomcat依赖,配置Tocat
        自动配置好SpringMvc:
                引入SpringMVC全套组件,自动配置好了SpringMVC的常用组件(功能)
        自动配置好web常见功能,如字符编码问题:
                SpringBoot帮我们配置好了所有web开发的常见场景
        默认包结构:
                主程序所在包及其子包下面的左右组件都会被默认扫描进来
                无需以前的包扫描
                如果想改变扫描路径:@SpringBootApplication(scanBasePackages = "com.atguigu")
        各种配置都有默认值:
                默认配置最终都是映射到某个类上,如MultipartProperties
                配置文件的值最终会绑定在每个类上,这个类会在容器中创建对象
        按需加载所有自动配置项:
                有非常多的starter
                引入了哪些场景这些场景的自动配置才会开启
                SpringBoot所有的自动配置功能都在spring-boot-autoconfigure包里面               

二.SpringBoot快速入门

配置下maven的本地仓库:

来康康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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>shangguigu_SpringBoot2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>14</maven.compiler.source>
        <maven.compiler.target>14</maven.compiler.target>
    </properties>
    <!--第一步:导入一个springboot父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <!--第二步:我们要进行web开发就导入web的环境-->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!--引入一个插件,用来简化部署-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

先得有个springboot的启动类:

package com.atguigu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

然后再写个Controller层的代码:

package com.atguigu.controller;

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

@ResponseBody
@Controller//或者也可以直接把上面的两个注解合并成@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String show(){
        return "hello SpringBoot";
    }

}

可以在配置文件application.properties中改下配置,比如端口号

 看下结构目录:

 测试:ok测试成功

简化部署,不需要在目标服务器安装tomcat服务器:

 

 三.容器功能

1.组件添加 @Bean...

  1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的,如例1
  2、配置类本身也是组件,如例2
  3、proxyBeanMethods:代理bean的方法
        Full(proxyBeanMethods = true)【每个@Bean方法被调用多少次返回的组件都是单实例的】
       Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
       如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个
       组件是否在容器中有。如例3
  4、组件依赖必须使用Full模式默认(true)。其他默认是否Lite模式(false)。如例4
  5、用@Service  @Controller  @Repository  @Component  进行组件添加
  6、@Import导入组件,默认组件名字是全类名,这个注解要放到别的组件上。如例5
  7、@Conditional条件装配。只有满足条件的时候才会被注入进来。如例6

//用户的实体类
package com.atguigu.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    String username;
    int age;
    Pet pet;
    public User(String username, int age) {
        this.username = username;
        this.age = age;
    }
}

//宠物实体类
package com.atguigu.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Pet {
    String name;
    int age;
}

//配置类
package com.atguigu.config;
import com.atguigu.bean.Pet;
import com.atguigu.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = true)
public class MyConfig {
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user(){
        User xiao = new User("萧萧", 200);
        //User组件依赖了Pet组件
        xiao.setPet(pet());
        return xiao;
    }
    @Bean
    public Pet pet(){
        return new Pet("萧萧的狗",200);
    }
}

//测试类
package com.atguigu;
import ch.qos.logback.core.db.DBHelper;
import com.atguigu.bean.Pet;
import com.atguigu.bean.User;
import com.atguigu.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@Import({DBHelper.class})
@SpringBootApplication(scanBasePackages = "com.atguigu")
public class mainApplicatin {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(mainApplicatin.class, args);
        //例1.检测组件的单实例,这个是从容器里面拿,所以即使proxyBeanMethods=false,这里拿到的也是相同的
        User user1 = (User)run.getBean("user");
        User user2 = (User)run.getBean("user");
        System.out.println(user1==user2);
        //例2.证明配置类也是组件
        MyConfig myConfig = run.getBean(MyConfig.class);
        System.out.println(myConfig);
        //例3.验证proxyBeanMethods的属性值true和false的区别,false是轻量级
        User user3 = myConfig.user();
        User user4 = myConfig.user();
        System.out.println(user3==user4);//如果是true,则这里是一样的;如果是false,这里就不一样
        //例4.组件依赖
        User user5 = run.getBean("user", User.class);
        Pet pet2 = run.getBean("pet", Pet.class);
        System.out.println(user5.getPet()==pet2);//如果是true,则这里是一样的,说明用户的宠物就是容器中的宠物;如果是false,这里就不一样
        //例5.@Import导入组件验证
        DBHelper dbHelper = run.getBean(DBHelper.class);
        System.out.println(dbHelper);
    }
}
//宠物和人的实体类用上面的

//配置类
package com.atguigu.config;
import com.atguigu.bean.Pet;
import com.atguigu.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
    @Bean("pet1")
    public Pet pet(){//注意这个要放在user()方法的前面,先在容器里注册
        return new Pet("萧萧的狗",200);
    }
    @ConditionalOnBean(name = "pet1")
    @Bean("user1")
    public User user(){
        User xiao = new User("萧萧", 200);
        return xiao;
    }
}

//测试类
package com.atguigu;
import ch.qos.logback.core.db.DBHelper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@Import({DBHelper.class})
@SpringBootApplication(scanBasePackages = "com.atguigu")
public class mainApplicatin {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(mainApplicatin.class, args);
        //例6,测试@ConditionalOnBean注解
        boolean user6 = run.containsBean("user1");
        System.out.println(user6);
        boolean pet3 = run.containsBean("pet1");
        System.out.println(pet3);
    }
}

2.原生配置文件注入@ImportResource

<?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="haha" class="com.atguigu.bean.User">
        <property name="username" value="shiqikaungsan"></property>
        <property name="age" value="19"></property>
    </bean>
    <bean id="haihaihai" class="com.atguigu.bean.Pet">
        <property name="name" value="萧萧的狗"></property>
        <property name="age" value="9"></property>
    </bean>
</beans>
package com.atguigu;
import ch.qos.logback.core.db.DBHelper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
@Import({DBHelper.class})
@SpringBootApplication(scanBasePackages = "com.atguigu")
@ImportResource("classpath:beans.xml")
public class mainApplicatin {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(mainApplicatin.class, args);
        boolean user7 = run.containsBean("haha");
        System.out.println(user7);
        boolean pet4 = run.containsBean("haihaihai");
        System.out.println(pet4);
    }
}

3.配置绑定@ConfigurationProperties

1.第一种直接使用@ConfigurationProperties
          一定要在组件上写

//实体类
package com.atguigu.bean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "people")//一定要在组件上写
public class User {
    String username;//注意这里的属性名和配置文件中的名字是一样的
    int age;
}

//Controller层的测试类
import com.atguigu.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
    @Autowired
    User user;
    @RequestMapping("/p")
    public User show2(){
        return user;
    }
}

 2.@EnableConfigurationProperties(User.class)+@ConfigurationProperties(prefix = "people")
              前一个注解能自动将实体类注册进容器中,并且开启他的绑定功能

//测试Controller是不变的

//实体类(去掉了@Component)
package com.atguigu.bean;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "people")//一定要在组件上写
public class User {
    String username;//注意这里的属性名和配置文件中的名字是一样的
    int age;
}

//主类
package com.atguigu;
import com.atguigu.bean.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication(scanBasePackages = "com.atguigu")
@EnableConfigurationProperties(User.class)
public class mainApplicatin {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(mainApplicatin.class, args);
    }
}

四.自动配置原理入门

1.引导加载自动配置类

2.自动配置流程

我们以springframework-boot-aop   不生效举例

 再找个生效的案例

 总之就是
        1.SpringBoot会加载所有的配置类:xxxxAutoConfiguration
        2.每个自动配置类都按照条件进行生效,一般都会默认绑定配置文件中的值xxxxProperties里
           面拿,而这个xxxxProperties是和我们的application.properties进行绑定
        3.生效的配置类就会给容器添加很多的组件
        4.只要容器中有这些组件,相当于这些功能就有了
        5.只要用户自己配置的,就以用户配置的优先
        定制化组件:
                1.用户直接自己@Bean替换掉底层的组件
                2.用户自己去看这个组件获取的是配置文件的哪些值,我们直接去改配置文件

xxxxAutoConfiguration->创建组件->xxxxProperties里面拿值->application.properties

3.最佳实践-应用咋写

        1.引入场景依赖
            Developing with Spring Boot
        2.查看配置了哪些东西
                自己爬源码
                配置文件中添加debug=true,然后运行程序,就能打印自动配置报告
        3.是否需要修改?
                参照文档修改配置,自己分析咋配置Common Application Properties                    
                自定义加入或者替换组件
                自定义器xxxxCustomizer

五.核心功能

1.配置文件

yaml:YAML 是 "YAML Ain't Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。非常适合用来做以数据为中心的配置文件

基本语法:
        key: value  kv之间有空格
        大小写敏感
        使用缩进表示层级关系
        缩进时不是用tab,只允许使用空格
        缩进的空格数不重要,只要相同层级的元素左对齐就好
        #表示注释
        字符串不需要加引号,如果要加,‘’与‘’ ‘’表示字符串内容会被转义/不转义                

数据类型:
        字面量:单个的,不可再分的值date、boolean、string、number、null
                  
        对象:简直对的集合。map hash  set  object
                行内写法:   k: {k1:v1,k2:v2,k3:v3}
                #或
                k:       
                   k1: v1              
                   k2: v2
                   k3: v3
        数组:一次按次序排列的值,array、list、queue
                行内写法:k: [v1,v2,v3]
                #或
                k:
                  - v1
                  - v2
                  - v3
        实例:

@Data
public class Person {
    private String userName;
    private Boolean boss;
    private Date birth;
    private Integer age;
    private Pet pet;
    private String[] interests;
    private List<String> animal;
    private Map<String, Object> score;
    private Set<Double> salarys;
    private Map<String, List<Pet>> allPets;
}
person:
  userName: zhangsan    #这个是字面量
  boss: false
  birth: 2019/12/12 20:12:33
  age: 18
  pet:                  #对象  非行内写法
    name: tomcat
    weight: 23.4
  interests: [篮球,游泳] #数组  行内写法
  animal:               #数组  非行内写法
    - jerry
    - mario
  score:
    english:
      first: 30
      second: 40
      third: 50
    math: [131,140,148]
    chinese: {first: 128,second: 136} #对象  行内写法
  salarys: [3999,4999.98,5999.99]
  allPets:
    sick:                               #对象的非行内写法+数组非行内写法+对象的行内写法
      - {name: tom}
      - {name: jerry,weight: 47}
    health: [{name: mario,weight: 47}]  #对象的非行内写法+数组行内写法+对象的行内写法 

书写提示:   

    <!--为了让自定义的类在写yaml文件时有提示-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
  
    <!--引入一个插件,用来简化部署,打包一个小胖jar-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!--打包时候,别把这个辅助的jar包打进去-->
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.静态资源访问

(1)静态资源访问

        1.静态资源目录:/static 、 /public、 /resources 、 /META-INF/resources,将静态资源放到
            这些目录下就能直接访问到。例如:http://localhost:8080/2.jpg

           
        2.流程:静态映射是默认是/**,但是在进行请求是要先看Controller能不能解决问题,如果不
            能那就去上面的静态目录中找静态资源,如果连静态资源都没有就404
        3.修改静态资源的访问前缀,或者静态资源的默认目录      

        

  (2)支持欢迎页

        1. 静态资源路径下 index.html的会被当做欢迎界面,访问根路径就能看到
                此时可以配置静态资源的路径
                但不能配置静态资源的访问前缀,否则欢迎页效果会失效,报404
        2.controller能处理/index

 (3)自定义Favicon

        就是设置浏览器菜单栏的小图标,只要把favicon.ico放到静态资源目录下就好。
                同样也是不能配置静态资源的访问前缀,否则会失效。

(4)静态资源配置原理

找主类

找子类

 写错了...WebProperties绑定的是spring.web

 找到了上面子类的构造器 在子类中找组件

 3.请求处理

(1) 请求映射

        @xxxMapping
        Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
              以前:/getUser 获取用户  /deleteUser 删除用户   /editUser修改用户   /saveUser保存用户
              现在:/user GET-获取用户   DELETE-删除用户    PUT-修改用户          POST-保存用户

        Resrt风格请求实现:
        众所周知,在表单提交时只能提交两种方式,所以为了让我们的Rest风格能够生效,我们需
        要做一些配置:
                1.自动配置一下这个filter的开启

                

               2.这两种特殊的表单提交方式改一下  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>
<body>
测试rest风格
<form action="/user" method="get">
    <input value="GET提交" type="submit">
</form>
<form action="/user" method="post">
    <input value="POST提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="delete"><!--这个value大小写无所谓-->
    <input value="DELETE提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT">
    <input value="PUT提交" type="submit">
</form>
</body>
</html>

rest风格请求原理

修改默认的_method:原理是本来他给我们的那个OrderedHiddenHttpMethodFilter,是建立在HiddenHttpMethodFilter.class不存在的前提下。默认放的OrderedHiddenHttpMethodFilter这个组件继承了HiddenHttpMethodFilter,底层默认的是"_method",这个就是原理。
        我们修改的思路就是:那我们直接给工程里自己添加一个HiddenHttpMethodFilter不就行了吗

package com.example.webdemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@Configuration(proxyBeanMethods = false)
public class MyConfig {//修改你默认的_method
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_changedmethod");
        return hiddenHttpMethodFilter;
    }
}

请求映射原理(它是如何通过请求路径找到对应的方法)

本节讲的是浏览器是怎么知道我们要找的是哪个请求,也就是MVC的基本原理流程:

        SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
        1.SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
        2.请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
        3.如果有就找到这个请求对应的handler如果没有就是下一个 HandlerMapping
        4.我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义
                HandlerMapping

 processRequest方法又调用了doService方法,但是在FrameworkServlet类中并没有对这个doService的实现,所以我们去子类DispatcherServlet中去找实现,能找到!在这个方法前面都是一些初始化,然后他调用了一个方法doDispatch(request, response);这是最重要的。  

 (2)Controller里的参数问题

         @PathVariable("id") :
                路径:@GetMapping("/car/{id}/owner/{username}")
                形参:(@PathVariable("id") Integer id,@PathVariable("username") String name)
                作用:把路径中的值,赋值给参数中对应的值。
         @@RequestHeader("User-Agent") :
                路径:和路径无关
                形参:(@RequestHeader("User-Agent") String userAgent)
                作用:获得请求头中的信息
         @RequestParam("age")  :
                路径:localhost:8080/test?name=zhangsan
                形参:(@RequestParam("name") String  name)
                作用:获取get提交时后面跟着的信息
         @CookieValue("_ga") :
                路径:和路径无关
                形参:(@CookieValue("_ga")  String _ga)
                作用:获得session中的值
         @RequestBody :
                路径:和路径无关
                形参:(@RequestBody String content)
                作用:获得请求体中的数据
         @RequestAttribute:
                场景:在转发的时候使用,在转发时一般会使用setAttribte("msg","success")来设置值
                形参:(@RequestAttribute  String msg)
                作用:获得请求域对象中的属性
         @MatrixVariable:   
                localhost:8080/test?name=zhangsan  这个插查询字符串用 @RequestParam。
                localhost:8080/car;low=34;brand=byd,audi,yd  矩阵变量

                场景:我们进行页面开发,如果cookie禁用了,那么session中的内容怎么使用?答:有
                           一个原理,我们在session中保存了a的值"b",使用set(a,"b")。我们的每个人每个
                         session都有一个jsessionid,这个id会被保存在cookie里面,cookie每次发送请求都
                         会携带。在没禁用之前,每次发请求,都会在cookie里携带jsessionid,服务器根
                         据这个jsessionid找到session对象,然后就能获取其中的数据。但是我们可以使用
                         矩阵变量的方式,为请求带上jsessionid,比如localhost:8080/car;jessionid=xxx 。
                         这个行为被称之为路径重写,把cookie的值使用矩阵变量的方式进行重写。
                作用:矩阵变量的方式获得数据
                语法:分号前面是访问路径,分号后面是键值对
                路径:localhost:8080/{path};low=34;brand=byd,audi,yd           
                形参:(@MatrixVariable("low") Integer  low,  
                              @MatrixVariable("brand")  List<String>  brand)
                复杂路径:
@GetMapping("/boss/{bossId}/{empId}") 
                                  /boss/1;age=20/2;age=30
                                  (@MatrixVariable(value="age"  pathVar="bossId")   Integer  bossAge,  
                                   @MatrixVariable(value="age"  pathVar="empId")    Integer  empAge)

                特殊:默认会自动取消路径中分号后面的东西,所以这个矩阵变量的功能要手动开启。
                     对于路径的解析是通过UrlPathHelper进行解析,使用removeSemicolonContent(移
                      除分号内容)支持矩阵变量的。方法如下:

    
    //在配置类里面添加上这样一个组件,手动开启@MatrixVariable注解的解析
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }

        各种类型参数解析原理

        1.首先HandlerMapping中找能处理请求的Handler(其实就是找到某个Controller的方法)
        2.然后为当前的Handler找一个适配器HandlerAdapter

     
                

 

         Servlet API参数解析原理

         这原理和上面差不多,就是在挑选解析器的时候不一样,这个解析器用的是  
         ServletRequestMethodArgumentResolver

        复杂参数

        Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)
        Errors/BindingResult
        RedirectAttributes( 重定向携带数据)
        ServletResponse(response)
        SessionStatus
        UriComponentsBuilder
        ServletUriComponentsBuilder
        1.在map,model,request都可以给request域中放数据,可以通过getAttribute()方法得到
        2.目标方法执行后,所有的数据都会放在ModelAndViewContainer,即视图模型容器。这里面
           包含了我们要跳转的页面view,还有包含model数据

        原理:
        我们从找到正确的解析器,解析器开始解析这里开始分析。
         如果是Map类型:
                1.他的解析器是MapMethodProcessor
                2.那么解析器就会返回一个return mavContainer.getModel();
                3.getModel()的返回值类型是ModelMap,返回值是new BindingAwareModelMap();
                4.这个BindingAwareModelMap最终extends ModelMap implements Model


        如果是Model类型:
                1.它的解析器是ModelMethdProcessor
                2.解析器在解析时会返回return mavContainer.getModel();和Map类型一模一样
                3.所以在下面的例子中那个map和model是一个对象
        执行完方法体后,map和model的数据是如何加载到请求域中:
                1.图中的handleReturnValue就是在干这件事情,我们进去看看
                2.这个方法创建了一个HandleMethodReturnValueHandler对象,这个对象又调用了
                    handleReturnValue方法,参数里有mavContainer对象
                3.在该方法里mavContainer对象设置了viewName,就是视图的地址,然后返回了一个
                    对象,不过该对象是方法getModelAndView()。这个方法返回值类型ModelAndView。
                4.进入该方法,有一个modelFactory对象调用updateModel方法
                5. 该方法中将mavContainer中的model封装成了ModelAndView
                6.处理派发结果
                   processDispatchResult(processedRequest, response, mappedHandler, mv,                    dispatchException);
                   renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

package com.example.webdemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Controller
public class ParamController {
        //复杂参数
        @GetMapping("params")
        public String getCar(Map<String,Object> map,
                                         Model model,
                                         HttpServletRequest request,
                                         HttpServletResponse response){
            map.put("hello","world666");
            model.addAttribute("world","world555");
            request.setAttribute("message","helloworld");
            Cookie cookie = new Cookie("c1", "adwqdjowieh");
            cookie.setDomain("localhost");
            response.addCookie(cookie);
            return "forward:/success";
        }
        @ResponseBody
        @GetMapping("/success")
        public HashMap<String, Object> success(HttpServletRequest request){
            Object message = request.getAttribute("message");
            Object hello = request.getAttribute("hello");
            Object world = request.getAttribute("world");
            HashMap<String, Object> map = new HashMap<>();
            map.put("hello",hello);
            map.put("world",world);
            map.put("message",message);
            return map;
        }
}

 5.视图解析与模板引擎

 视图解析:
        SpringBoot默认不支持JSP,需要引进第三方的木板引擎技术实现页面渲染。在这我们使用thymeleaf这个服务器引擎。
基本语法
        1.表达式 

        2、字面量
                文本值: 'one text' , 'Another one!' ,…
                数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
                空值: null
                变量: one,two,.... 变量不能有空格
        3、文本操作
                字符串拼接: +
                变量替换: |The name is ${name}|
        4、数学运算
                运算符: + , - , * , / , %
        5、布尔运算
                运算符: and , or
                一元运算: ! , not
        6、比较运算
                比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )
        7、条件运算
                If-then: (if) ? (then)
                If-then-else: (if) ? (then) : (else)
                Default: (value) ?: (defaultvalue)
        8、特殊操作
                无操作: _
        9、设置属性值-th:attr      

<!--设置单个的值-->
<form action="subscribe.html" th:attr="action=@{/subscribe}">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
  </fieldset>
</form>

<!--设置多个的值-->
<img src="../../images/gtvglogo.png"  th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

<!--以上两个的代替写法-->
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
<form action="subscribe.html" th:action="@{/subscribe}">

        10. 迭代

<tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
        <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
  <td th:text="${prod.name}">Onions</td>
  <td th:text="${prod.price}">2.41</td>
  <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

      11.条件运算         

<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>

<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administrator</p>
  <p th:case="#{roles.manager}">User is a manager</p>
  <p th:case="*">User is some other thing</p>
</div>

         12.属性优先级:
thymeleaf使用

        1.导包

        <!--引入第三方页面渲染引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        2.SpringBoot已经把这个自动配置了
              1、所有thymeleaf的配置值都在 ThymeleafProperties
              2、配置好了 SpringTemplateEngine
              3、配好了 ThymeleafViewResolver
              4、我们只需要直接开发页面

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration { }


public static final String DEFAULT_PREFIX = "classpath:/templates/";//默认路径
public static final String DEFAULT_SUFFIX = ".html";  //xxx.html,这是默认的文件后缀

        3.举个例子

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><!--要加这个命名空间-->
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${message}">嗨嗨嗨</h1>
<a href="www.atguigu.com" th:href="${link}">去百度</a>
</body>
</html>
package com.example.webdemo.controller;

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

@Controller
public class ViewTestController {
        //复杂参数
        @RequestMapping("/atguigu")
        public Object getCar(Model model){
            model.addAttribute("message","你好呀萧炽凛");
            model.addAttribute("link","www.atguigu.com");
            return "success";//有前缀有后缀,所以我们直接写名字就好了
        }
}

(1)整合Thymeleaf后台管理系统

6.拦截器

     

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值