SpringBoot 2.x学习笔记

1、快速入门

springboot是一个整合其他东西的总框架!

新建Maven项目--配置项目名--建成项目后可以修改group-id、artifactid、version

修改pom.xml配置文件

导入父工程:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

添加系统依赖(做web开发添加的依赖):

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

编写代码:

编写主程序类

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

创建Controller类

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String handle01(){
        return "Hello,Spring Boot 2!";
    }

}

运行主程序类

简化配置

springboot是一个整合其他东西的总框架!所以为了简化配置,将未来所有配置文件整合到一个文件中,固定名字,application.properties,可以修改Tomcat、springMVC的一些设置。

不配置时有默认值。

简化部署:

创建一个可执行的jar包,引入boot提供的插件,可以直接把项目打包成jar包。

只需要引入一个插件即可:

 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

把项目打包成jar包,直接在目标服务器上运行。

1.2、自动配置

当我们引入web开发场景下的依赖时:

spring-boot-starter-web

  • 自动配好Tomcat
  •        引入Tomcat依赖。
    • 配置Tomcat
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
</dependency>
  • 自动配好SpringMVC
    • 引入SpringMVC全套组件
    • 自动配好SpringMVC常用组件(功能)
  • 自动配好Web常见功能,如:字符编码问题
    • SpringBoot帮我们配置好了所有web开发的常见场景
/*
主程序类,相当于所有启动的入口
@SpringBootApplication:这是一个springboot应用
 */
@SpringBootApplication
public class MainAppliaction {
    public static void main(String[] args) {
        //返回我们的IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainAppliaction.class, args);
        //获取IOC容器中的所有组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        
    }
}
  • 默认的包结构
    • 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
    • 无需以前的包扫描配置
    • 想要改变扫描路径,@SpringBootApplication(scanBasePackages="com.atguigu")
  •         或者@ComponentScan 指定扫描路径

@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.the.boot")

  • 各种配置拥有默认值
    • 默认配置最终都是映射到某个类上,如:MultipartProperties(配置类)
    • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
  • 按需加载所有自动配置项
    • 非常多的starter
    • 引入了哪些场景这个场景的自动配置才会开启
    • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
  • ......

2、容器功能

2.1、组件添加

1、@Configuration

  • 基本使用

在相关类上添加这个注解,告诉SpringBoot这是一个配置类  == 配置文件(.xml)

在配置类中添加方法,方法名作为相关组件的id,返回类型即组件类型,方法返回对象就是IOC中的bean实例。@bean(value = "") 可以为bean实例设置名称。配置类也是一个组件。

在配置类中创建的对象默认都是单实例的,无论获取多少次都是同一个对象。

//配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
    //配置类本身也是bean实例对象
    //外部类无论对配置类里的这个bean注册方法,调用多少次获取的都是之前注册容器中的单实例对象
    //Configuration新增proxyBeanMethods = true默认为true,
    //proxyBeanMethods:代理bean的方法
    //全模式Full(proxyBeanMethods = true)   轻量级模式Lite(proxyBeanMethods = false)  组件依赖
    //false则不会检查容器中是否有相应的组件,true则会检查
    //如果没有依赖false,否则改成true
@Configuration(proxyBeanMethods = true)  //告诉springboot这是一个配置类,相当于bean.xml
public class MyConfig {

    @Bean
    public User user01(){
        return new User("zhangsan",18, tomcatPet());
        //User组件依赖了pet组件
    }

    @Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

/*
主程序类,相当于所有启动的入口
@SpringBootApplication:这是一个springboot应用
 */
@SpringBootApplication
public class MainAppliaction {
    public static void main(String[] args) {
        //1、返回我们的IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainAppliaction.class, args);
        //2、查看容器里面的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        //3、从容器中获取组件
        Pet tom01 = run.getBean("tom", Pet.class);
        Pet tom02 = run.getBean("tom", Pet.class);
        System.out.println(tom01 == tom02);
        //我们获取到的Myconfig对象本身就是代理对象
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);
        //如果@Configuration(proxyBeanMethods = true) 代理对象调用方法,Springboot总会检查这个组件是否在容器内
        //保持组件单实例
        User user1 = bean.user01();
        User user2 = bean.user01();
        System.out.println(user1 == user2);

        User user01 = run.getBean("user01",User.class);
        Pet tom = run.getBean("tom", Pet.class);
        System.out.println(user01.getPet() == tom);
    }
}

  • Full模式与Lite模式
    • 示例
    • 最佳实战

      • 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
      • 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式

2、@Bean、@Component、@Controller、@Service、@Repository

均可用来创建组件

3、@Import导入组件

在容器的组件上添加@Import注解,可以在容器中导入非常多的组件

@Import({User.class, DBHelper.class。。。})   //调用类的无参构造器中,在IOC容器中创建这两个类的bean实例。

Import导入容器的bean实例,默认bean实例的名字是全类名。

4、@Conditional

条件装配:满足conditional满足的条件才,则进行bean实例注入

2.2、原生配置文件引入

1、@ImportResource

之前的老项目采用xml文件配置,而非配置类,为了解决这一问题,如果不想一点一点迁移。

在配置类上写@ImportResource("classpath:beans.xml"),相当于将xml文件解析翻译成配置类中的形式。

2、@EnableConfigurationProperties + @ConfigurationProperties

在配置文件中,写相应的属性:在要被注入属性的类上添加注解@ConfigurationProperties(prefix  = "xxx")  只会使用xxx开头的项。

并且添加@Component

想让一个javabean和配置文件中的属性进行绑定,要在目标类上添加@Component和@ConfigurationProperties(prefix="xxx")

第二种方法:在配置类上面添加@EnableConfigurationProperties

@EnableConfigurationProperties(Car.class)
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig {
}

在Car上添加@ConfigurationProperties(prefix="")  Car上面不需要开启@Component

3、最佳实践

1、引入场景依赖(例如,web开发采用springboot-starter-web)

2、查看自动配置了哪些

         application.properties  中添加debug=true,negative生效、positive生效

3、是否需要修改

               参照文档修改配置项

                自定义加入或者替换组件

4、开发小技巧

3.1 Lombok

简化java bean开发,pom.xml引入Lombok

   1、引入依赖

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

   2、安装idea lombok插件

   3、在bean上添加注解@DATA,帮助我们生成getter和setter方法

         添加@ToString注解,可以帮助我们生成tostring方法

         添加@NoArgConstructor,帮助我们生成无参构造器

         添加@AllArgsConstructor,帮助我们生成带所有参数的构造器, 如果是带部分属性的构造器,则无法使用,需要自己手写。       

         添加@EqualsAndHashCode,重写hashcode方法

        添加@slf4j可以引入日志,log.info("");

         在编译的时候执行。

3.2 dev-tools

1、开发中经常使用,引入依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
可以实现热更新,在项目里面做什么改变,只要ctrl + f9即可更新

ctrl+f9会帮我们重新编译,dev-tool会帮我们热更新

3.3 spring initializr

使用idea的spring initializr

会自动完成一些配置

以后创建项目,都是用spring初始化向导

5、yaml配置文件

与application.properties功能一样

  • key: value   k与v之间有空格
  • 使用空格缩进表示层级关系
  • 缩进不允许使用tab,只允许使用空格,idea可以用tab,只要相同层级的元素左对齐即可
  • #表示注释
  • ''和""表示字符串
  • 字符串无需加引号
  • 字面量:单个的、不可再分的值。date、boolean、string、number、null
  • k: v
  • 对象:键值对的集合。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;
}

@Data
public class Pet {
    private String name;
    private Double weight;
}

# yaml表示以上对象
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}]

6、Web开发

spring Initilaizr中勾选Developer Tools中的工具和Web开发中的Spring Web

6.1 静态资源

静态资源放在 /static   /public  /resources /META-INF/resources下  都可以通过/**路径访问

静态资源会自动映射到url路径中的  /**

假如有一个动态请求名  /**  和静态资源冲突了,请求进来先去找Controller看能不能处理,不能处理的所有请求又都交给静态资源处理器。静态资源能找到就返回,找不到返回404.

为了使拦截器配置方便,例如登录时,把姿态资源访问路径放行,所以最好设置静态资源访问前缀,默认是无前缀的。

在yaml文件下配置 static-path-pattern: "/res/**"

改变默认的静态资源路径:static-locations: [classpath: /haha]

6.2 Rest映射及源码解析

Rest风格:

以前:/getUser  /deleteUser  /putUser   /postUser

现在    /user    GET-获取用户   DELETE-删除用户  PUT-修改用户  POST-保存用户

表单提交没有“put”和“delete”请求方式!!!

HiddenHttpMethodFilter 

SpringBoot在WebMvcAutoConfiguration完成对Spring MVC的配置。

在WebMvcAutoConfiguration中使用的是OrderedHiddenHttpMethodFilter,它是对HiddenMethodFilter的继承。

 

在doHttpFilter中只有表单是POST方式,才会去取methodParam(真正提交的请求参数)

_method作为真正的请求方式。在表单的提交项中,需要使用如下方式

<form action="/user" method="post">

        <input name="_method" type="hidden" value="PUT"/>

        <input value="REST-PUT 提交" type="submit"/>

</form>

但是HiddenHttpMethodFilter需要一定的条件:

表单REST风格的原理(表单提交要使用REST的时候)

  • 表单提交会提交_method=PUT、DELETE参数
  • 请求过来会被Filter拦截
  • 判断表单提交的方式是否为POST并且获取当前请求有无错误,如果满足条件,然后获取请求参数_method的值,判断是否为空,如果不为空则将_method参数转化为大写,所以表单里的delete和put的大写和小写都可以
  • 然后判断他们判断的请求包不包括该请求:
    HttpMethod.PUT.name(),
          HttpMethod.DELETE.name(), HttpMethod.PATCH.name())
  • 原生request(post),包装模式requestWrapper重写了getMethod()方法,然后返回的是传入的值,传入的值又是在请求中传入的_method方法
  • 过滤器链放行的时候用wrapper,以后调用getMethod是调用requestWrapper的方法
  • 如果没有进入,就把原生的放出去

上面针对是表单,Rest如果使用客户端工具(postman)发请求,从Http层就不是了,不会通过filter。如POSTMan直接发送put、delete等方式请求,无需filter

如果不做表单提交,不用在yaml配置

@RequestMapping(value="/user",method=RequestMethod.GET)

可以简写为@GetMapping("/user")  注解内部就是上面的

同理:@PutMapping("/user")  、 @DeleteMapping("user") 、@PostMapping("user")

6.3 请求映射原理

SpringMVC功能分析都从DispatcherServlet类中的doDispatch()方法开始分析。

6.4 普通参数与基本注解

注解

@PathVariable  (获取路径变量)

@RequestHeader (获取请求头)

@RequestParam (获取请求参数)

url :   /car/{id}/owner/{username}?age=18&inters=basketbann&inters=game

@CookieValue (获取cookie值)

获取请求的cookie值

@RequestBody(获取请求体)请求体只有post请求才有请求体,一般是表单提交

@RequestAttribute(获取request域属性)

@Controller
public class RequestController {

    @GetMapping("/goto")
    public String goToPage(HttpServletRequest request){
        request.setAttribute("msg","成功了");
        request.setAttribute("code",200);
        return "forward:/success";  //转发到 /success请求
    }

    @ResponseBody
    @GetMapping("/success")
    public Map succese(@RequestAttribute("msg",可选required=false) String msg,
                       @RequestAttribute("code") Integer code,
                       HttpServletRequest request){
        Object msg1 = request.getAttribute("msg");

        Map<String,Object> map = new HashMap<>();
        map.put("reqMethod_msg",msg1);
        map.put("annotation",msg);
        map.put("code",code);
        return map;
    }
}

@Controller与@RestController的区别?

@Servlet API

@复杂参数

@自定义对象参数

6.5 各种类型参数解析原理

只要给方法参数位置标注注解,SpringMvc就会自动地在调用方法时确定他们的值。

请求进入之后,过filter,进入dispatcherServlet,在所有的默认HandlerMapping中查找能处理该请求的handlerMapping(一般是RequestMappingHandlerMapping),然后返回对应的handler,所谓的handler就是我们说的能处理请求的controller的哪个方法。然后再为找到的handler找到合适的handler适配器(默认有4种),第一种支持方法上标注@RequestMapping方法的,第二种支持函数式编程的,其他的2种。

找到handler的适配器之后,执行目标方法。然后设置参数解析器(参数解析器共有26个,对每个参数,遍历每个解析器看哪个解析处理这种类型的参数((1看有没有标注相应的注解,2看是否支持参数的类型)),如果支持就调用相应解析器的resolveArgument),确定我们将要执行的目标方法的每个参数的值是多少。另外我们看到returnValueHandler返回值处理器,确定我们我们返回值的类型。然后真正执行目标方法。目标方法执行完成,会将所有的数据放在ModelAndViewContainer,包含要去的页面地址View,还有Model数据(我们操作Map和Model所保存的数据)。

// Determine handler adapter for the current request.

每个参数最终都要落脚到Resolver(参数解析器)

 ② servlet API

 ③复杂参数

参数如果是Map<>类型,Model类型,或者是HttpServletRequest、HttpServletResponse类型,实质上都会添加到request作用域。

 ④自定义对象参数  封装POJO(例如  Person person)

自定义类型参数使用ServletModelAttributeMethodProcessor解析器,该解析器判断是否是简单类型,不是就符合。然后进行解析,然后创建一个参数的实例(new Person())空的person对象,然后创建一个webbinder【web数据绑定器】,将请求参数的值绑定到指定的java bean,(HTTP超文本传输协议,利用转换服务,把string转化为各种类型),在webBinder里面利用类型转换器(converters)把string转化为指定的类型,再封装到java bean里。

数据绑定:页面提交的请求数据(GET  POST)都可以和对象属性进行绑定

可以自定义converter,实现对象属性的类型转换,将字符串转换为对象属性类型

 //1、WebMvcConfigurer定制化SpringMVC的功能
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                // 不移除;后面的内容。矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }

            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String, Pet>() {

                    @Override
                    public Pet convert(String source) {
                        // 啊猫,3
                        if(!StringUtils.isEmpty(source)){
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }
        };
    }

7、数据响应与内容协商

1、响应页面

2、响应数据:json、xml、xls、图片音视频、自定义协议数据

1、响应json

web开发场景自动引入了json场景,json场景引入了json相关依赖,jackson.jar

在类上面添加@Controller注解,然后在相应的方法上添加@responsebody注解,给前端返回json

@Controller
public class ResponseTestController {

    @ResponseBody
    @GetMapping("/test/person")
    public Person getPerson(){
        Person person = new Person("zhangsan1",23);
        return person;
    }
}

原理:

执行完目标方法后,会得到一个返回值,如果有返回值并且返回的不是一个字符串,第一步根据返回值的值和类型找到合适的返回值处理器,找到能处理它的处理器(是否支持这种类型的返回值,或者是否有相关的注解),然后调用处理器的处理方法进行处理handleReturnValue()。

SpringMVC支持哪些返回值

ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;

标注了@ResponseBody会使用RequestResponseBodyMethodProcessor这个处理器进行处理,利用MessageConvetor进行转换,最后以json的方式写出去。

标注了@ResponseBody会使用RequestResponseBodyMethodProcessor这个处理器进行处理,然后在底层找到合适的MessageConvetor进行转换,然后写出去(不一定是json,最终由返回类型决定)。找messageconvetor时涉及到内容协商:

内容协商

根据客户端的请求头中的Accept字段,确定返回的类型

只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

  • 1、判断当前响应头中是否已经有确定的媒体类型。MediaType
  • 2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】
    • contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略

8、拦截器-登录检查与静态资源放行

访问任何请求,登录之后才可以访问。session太麻烦,每个页面都需要判断,所以需要使用拦截器。

HandlerInterceptor拦截器接口:

preHandle():预先处理,随便发一个请求,在Controller处理之前,我们进行preHandle处理,目标方法执行以后,在还没有进入页面,可以调用postHandle(),在页面渲染完成后,可以调用afterCompletion()。

/**
 * @date 2022/4/5 - 15:07
 *
 * 登录检查
 * 1、配置拦截器要拦截哪些请求
 * 2、把这些配置放在容器中
 * 3、
 */
@Slf4j
public class LonginInterceptor implements HandlerInterceptor {
    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("拦截的请求是" + request.getRequestURI());
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("loginUser");
        if(loginUser!=null){
            return true;
        }
//        session.setAttribute("msg","请先登录");
        request.setAttribute("msg","请先登录");
//        response.sendRedirect("/");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     * 目标方法执行完成以后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器(实现WebMvcConfigurer的addInterceptor方法
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LonginInterceptor())
                .addPathPatterns("/**")  //所有请求都会被拦截,包括我们的静态资源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**");  //放行的请求

    }
}

preHandle---目标方法---postHandle---AfterCompletion

9、文件上传(单文件与多文件)

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值