Spring,SpringMVC总结


Spring、SpringMVC总结

> 小编最近二刷Spring,SpringMVC,之前第一次接触Spring,对Spring一知半解,总感觉差点意思,听了郝老师的讲解,真的是豁然开朗,黑马郝老师的功底是非常深厚的,对源码的认识也非常到位,听了之后绝对会有很大收获。开发知识浩如烟海,一起努力吧,加油!

Spring经典教程——B站空降:黑马程序员新版Spring零基础入门到精通,一套搞定spring全套视频教程(含实战源码)_哔哩哔哩_bilibili

0. Spring 容器Bean基础知识

0.1 创建Bean的三种方式(Bean的实例化方式)

  1. 通过构造方法创建(无参/有参)有参需要添加参数<contrutor_arg name=“” vlaue = “”(如果参数未引入数据类型ref=“”)>

  2. 通过实例工厂创建对象

  3. 通过静态工厂创建对象

0.2 依赖注入的种类

  1. 自动装配 antowire = “byType(该类型bean实例只能有一个,否则将会报错)/byName/no(默认)” bean的no == name(后期no属性会转为name)

  2. 注入基本数据类型

  3. 注入引入数据类型

0.3 BeanFactory和Application Context的关系

前者是后者的父类,前者延迟加载,后者立即加载。其中Appltication Context又称为Spring容器

1. Spring 容器Bean加载执行顺序:

​​​​​​​

2. spring后处理器(spring注解开发原理/spring功能扩展原理)

视频来源: 45-Spring的Bean后处理器-再次完善实例化基本流程图哔哩哔哩bilibili

作用:修改或添加BeanFactory对象中的BeanDefinitionMap中的内容,使得单例池中的对象发生变化

BeanFactoryPostProcessor: 也叫Bean工厂后处理器,在BeanDefinitionMap初始化后调用(对Bean定义的操作) ---->spring注解开发原理

BeanPostPorcessor: 也叫Bean后处理器,在Bean对象创建之后,存入SingletonObjets之前调用(对Bean对象的操作)----->Spring功能扩展原理,通过动态代理得到Bean增强对象

3. Bean的生命周期

  1. 实例化阶段:创建半成品的Bean对象(不完整,缺少依赖注入、AOP等拓展)

  2. 初始化阶段:Bean功能实现的核心阶段,包括依赖注入、Spring后处理器、AOP等操作实现Bean对象功能的拓展。详细的初始化阶段步骤见下图:

  3. 对象的属性列表也会以Map集合的形式存储在BeanDefintion中的propertyValues,Spring先检查propertyValues是否有内容,然后才会进行依赖注入。

  4. Spring依赖注入时的循环引用问题(Spring三级缓存)

SpringIOC整体流程总结:

Bean定义----> Bean实例化阶段----->Bean初始化阶段(依赖注入、增强)----->Bean的存储阶段

4. 注解开发

  1. Component注解:相当与xml的<bean>标签,用于标注Bean,其有3个子标签,分别对应三层架构的不同层

① Controller:表示层、表现层、web层

② Service:业务逻辑层

③ Repository: 持久化层DAO

  1. 依赖注入

@Autowired注解:默认通过类型注入,如果该类型有不止一个实例,则默认查找同名对象注入,如果无同名对象则会报错。

@Qualifier注解:与Autowired注解一起使用,根据名称注入相应的Bean。

@Resource注解:是javax的注解,不是spring的注解,Spring对其进行了解析。不指定名称参数时,按照类型注入,指定名称时,按照名称注入,(综合了Qualifier注解和@Autowired注解)一般不用。

  1. Bean注解->非自定义Bean(第三方类):对于一些非自定以的第三方类,我们无法在源码上添加注解,需要使用如下方法。原理:以工厂方式配置Bean

① 提供一个方法,返回值设为第三方类的对象。

② 在方法上添加@Bean(“beanName”)注解标注。

③ ①中方法所在的类必须交给Spring管理(必须使用Component注解)

④ 如果方法需要提供形参,则注入的格式如下:

 @Component
 public class OtherBean{
 @Bean("dataSource")//如果设置名字,就是所设的名字,否则默认为方法的名字
 public DataSource dataSource(
     /*方法形参*/
   @Value("${jdbc.driver}") String dirverClassName,
   @Autowired @Qualifier("userDao2") UserDao userDao,
   UserService userService
 ){
  //...
  return new DataSource(); 
 }
 }
 //注:@Autowired @Qualifier("userDao2") UserDao userDao当为形参自动注入时,@Autowired可以省略。@Qualifier("userDao2") UserDao userDao即可。形参按照类型注入时,@Autowired可以省略,如果按照名字注入时,需要用Qualifier(“xxx”)根据名称匹配
  1. 使用配置类替代xml,用注解标注xml中的非Bean内容

 @Configuration //标注当前类是一个配置类,并交给Spring容器管理
 @ComponentScan({"com.itheima","xxx"})  //需要扫描的包(扫描带Component的包)
 @PropertySource({"classpath:jdbc.properties"}) //属性资源位置
 @Import(OtherBean.class) //导入其他配置类
 public class SpringConfig{
 ​
 }
  1. Spring整合Mybatis

 @Configuration  //声明该类是核心配置类                         
 @ComponentScan("com.itheima")  //开启spring注解扫描
 @PropertySource("classpath:db.properties")  //引入properties文件
 @MapperScan("com.itheima.dao")   //MyBatis扫描dao接口
 public class Application {
 //定义属性 为属性注入数据(数据的来源上面引入的db.properties文件)
 @Value("${db.driverClass}")
 private String driverClass;
 @Value("${db.url}")
 private String url;
 @Value("${db.username}")
 private String username;
 @Value("${db.password}")
 private String password;
 ​
 //创建数据源返回数据源,Spring会自动调用该方法,并将该对象交给IOC容器管理
 @Bean  
 public DataSource dataSource(){
   DruidDataSource druidDataSource = new DruidDataSource();
   druidDataSource.setDriverClassName(driverClass);
   druidDataSource.setUrl(url);
   druidDataSource.setUsername(username);
   druidDataSource.setPassword(password);
   return druidDataSource;
 }
 ​
 //创建SqlSessionFactoryBean对象,设置形参,Spring会自动去调用IOC容器中已有的数据源
 @Bean
 public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
   SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
   sqlSessionFactoryBean.setDataSource(dataSource);
   retun sqlSessionFactoryBean;
 }
 ​
 }

5.Spring整合web环境

  1. Listener在框架中主要监听域的创建与销毁。

  2. 在web层(Controller)需要调用Spring容器中的Service和Dao层代码,如果每个Servlet都在运行时创建一个Spring容器,那么会导致内存消耗太大。正确的做法是将Spring容器放在web三大作用域(application域、session域、request域)的application中(也叫ServletContext域),使得所有servlet都能访问到同一个Spring容器。

  3. 现在的目标如下:

    • ApplicationContext(Spring容器)创建一次,配置类加载一次

    • 在web服务器启动时,就执行Spring容器的创建,后续直接从容器中获取Bean使用(监听ServletContextListener创建Spring容器)

    • ApplicationContext的应用需要在web层任何位置都可以获取到(application域)

  4. Web层不使用框架开发Servlet(Web)程序的缺点(Servlet充当Controller的问题):

  • 将每个Servlet的共有行为和特有行为进行拆分:

共有:解析请求参数、封装数据、放到域中、指派视图 --->前端控制器

特有行为放到后端控制器中

  • 使用框架实现前端控制器,封装共有行为

  • 向web层中注入业务层的Bean

  1. SpringMVC框架

见 6.

6.SpringMVC

  1. SpringMVC概述

  1. SpringMVC环境搭建步骤

编写的Controller层示例代码:

 ​
 @Controller
 public class QuickController{
     
     @RequestMapping("/show")//请求路径(前缀)
     public void show(){
         System.out.println("hello...");//控制台打印
     }
 }
 //报500错误分析:ViewResolver....
 //默认后端控制器Controller返回视图名字,由ViewResolver视图解析器解析,现在返回的是空,会报错,可以创建一个界面,并将界面名字作为方法的返回内容即可。

注解分析:

@Controller注解:将该Controller交给Spring容器管理

@RequestMapping:请求路径,可以放在方法上,默认为get请求路径,Request.GET,也可以放在类上,作为请求路径的默认前缀。

  1. Controller中如何直接注入Service

SpringMVC是Spring的子容器,当程序中配置了SpringMVC和Spring容器后,SpringMVC中的Controller就可以调用Spring容器中的对象,从而实现了Contoller中注入Service对象。

  1. SpringMVC相关组件

① HandlerMapping(处理器映射器):匹配映射路径对应的Handler,

② HandlerAdapter(处理器适配器) :执行①中匹配的方法

③ ViewResolver(视图解析器) :处理视图模型对象

  1. SpringMVC请求处理:

    1. 请求映射路径的配置

    ① @RequestMapping(“/quick”)请求路径,可以放在方法上,默认为get请求路径,Request.GET,也可以放在类上,作为请求路径的默认前缀。

    ② @GetMapping(“/xxx”)

    ③ @PostMapping(“/xxx”)

    1. 接收客户端请求数据

    ① Get请求。

    • 当请求路径中key名字与形参名字一致时:

    SpringMVC会自动解析请求路径,当请求参数key名字与形参名字一致时,会根据名字自动匹配,将对应vlaue注入给同名的形参中。

    • 当请求路径中key名字与形参名字不一致时:

    使用@RequestParam(“请求参数名”)注解,在形参类型前面使用,使得请求参数与形参对应

    • 当请求路径中的参数有多个同名key时(比如复选框发送给后端的数据)

    http://localhost/yu/param1?hobby=football&hobby=basketball&hobby=pingpong

    后端可以使用String[]数组接收,也可以使用List集合接收,当使用List集合接收时,必须加@RequestParam注解,表示只需要将内容加入结合即可,不需要创建对象。应为当形参为Bean时,SpringMVC会尝试创建对象,而List是个接口,不能创建对象,所以不加@RequestParam会报错。Map集合同理,也要加RequestParam

    所以,数组不需要加,集合需要加@RequestParam

       @RequestParam注解解析:
       @RequestParam(vlaue="请求路径中的名字",request=true,defaultVlaue:"hhh")
       vlaue:请求路径中的名字
       request:是否为必须使用的形参,
         默认为faluse,如果路径中未匹配到参数,如果为引用数据类型,则为null,如果为基本数据类型,则报错。因为基本数据类型不能赋值为null
         如果设为true,则必须再路径中声明参数,否则会报错
       defaultVlaue:默认值
    • 接收客户端请求数据如何自动封装为实体类对象

    只需要在形参中添加想要封装的对象,实体类需要是一个javaBean(无参构造和set方法)

    SpringMVC会解析请求路径,通过反射机制创建对象。

    使用注解@PathVariable(“请求路径参数名”)

    例如:

    如果GetMapping(“/user/{name}/{id}”)

    这里可以在形参前使用@PathVariable(“name”)、@PathVariable(“age”)

    实例:

     @Controller
     @RequestMapping("/yu")
     public class ParamController{
         //请求路径:http://localhost/yu/param1?username=zhangsan&age=18
         @GetMapping("/param1")
         public String param1(String username,int age){
             System.out.println(username);
             System.out.println(age);
             return "index.jsp";
         }
         
         
         //请求路径:http://localhost/yu/param2?username=zhangsan&age=18
         @GetMapping("/param2")
         public String param1(@RequsetParam("username")String name,int age){
             System.out.println(username);
             System.out.println(age);
             return "index.jsp";
         }
         
        //请求路径:http://localhost/yu/param3?hobby=lq&hobby=zq&hobby=pp
         @GetMapping("/param3")
         public String param1(String[] hobby){
             System.out.println(username);
             System.out.println(age);
             return "index.jsp";
         }
         
         //请求路径:http://localhost/yu/param3?hobby=lq&hobby=zq&hobby=pp
         @GetMapping("/param3")
         public String param1(@RequestParam List<String> hobby){
             System.out.println(username);
             System.out.println(age);
             return "index.jsp";
         }
         
         
         //------前端数据自动封装为实体(反射,调用set方法)
         //请求路径:http://localhost/yu/param4?username=zhangsan&age=18&address.province=bj&address.country=china
         @GetMapping("/param4")
         public String param1(User user){
             System.out.println(username);
             System.out.println(age);
             return "index.jsp";
         }
     }
     ​
     ​
     //其中User类的定义如下:
     public class User{
         private String username;
         private int age;
         private Address address;
         public void setUsername(String username){}
         //...
     }
     //Address类的定义如下
     public class Address{
         private String country;
         private String province;
         public void setCountry(String country){}
         public void setProvince(String province){}
         //...
         
         
         
         --------------------------------------------------------
             ---------Restful风格中请求参数的获取-------------
         --------------------------------------------------------
     //请求路径:http://localhost/yu/user/zhangsan/10
         @GetMapping("/user/{name}/{idxxx}")
         public String param1(@PathVariale("name") String username,@PathVariale("idxxx") int age){
             System.out.println(username);
             System.out.println(age);
             return "index.jsp";
         }
         
     }

    ② Post请求。

    Post请求客户端的请求内容存储在请求体中,想要将请求体中的数据传给形参,则需要在形参前使用@RequestBody注解。

    实例:

     @Controller
     @RequestMapping("/yu")
     public class ParamController{
         //请求路径:http://localhost/yu/requestbody
         @GetMapping("/requestbody")
         public String param1(@RequestBody String jsonStr){
             System.out.println(jsonStr);
             return "index.jsp";
         }
     }    
     //通常为前端在请求体中传入json格式字符串,发送给后端,后端通过@RequestBody注解接收请求体中的内容。
     ​
  2. SpringMVC响应处理

分类:同步方式(转发、重定向) 异步方式(ajax+Restful风格+json)

区别:

① 同步方式回写数据,是将数据响应给浏览器;而异步方式一般是回写给AJAX引擎(ajax通过监听的方式,得到服务器的数据)。即谁访问服务器端,服务器端就就回写给谁。

② 同步方式一般响应的是无特定格式的字符串,而异步方式通常为JSON格式字符串。

同步方式

  1. 同步方式:

    • 转发和重定向

  • 响应数据模型(ModelAndView)

  • 直接回写数据给客户端

@ResponseBody注解:由于SpringMVC默认返回的字符串为视图名,在方法上加上或者也可以直接在类上添加,如果在类上添加,则就等于给所有方法都添加了@ResponseBody注解。@RespinseBody注解,表示返回的字符串是响应体内容而非视图名。

如果方法返回值为引用数据类型,则改注解还会自动将返回的对象转为JSON格式字符串。发生给请求方

由于目前项目大都采用前后端分离的开放方式,所有方法返回的都是JSON格式字符串,所以可以将@ResponseBody提到类的头部声明,这样,类的头部就有了两个注解,@Controller和@ResponseBody注解,这两个注解可以合并为@RestController注解。

   //@Controller
   //@ResponseBody
   @RestController
   @RequestMapping("/yu")
   public class ParamController{
       //请求路径:http://localhost/yu/responsebody
       @GetMapping("/responsebody")
      // @ResponseBody //告诉SpringMVC返回的字符串不是视图名,而是响应体的数据
       public String param1(@RequestBody String jsonStr){
           System.out.println(jsonStr);
           return "hello world";
       }
   }  

  @Controller
  @RequestMapping("/yu")
  public class ParamController{
      //请求路径:http://localhost/yu/responsebody
      @GetMapping("/responsebody")
      @ResponseBody //告诉SpringMVC返回的字符串不是视图名,而是响应体的数据
      public String param1(@RequestBody String jsonStr){
          System.out.println(jsonStr);
          return "hello world";
      }
  }   

异步方式

异步方式:见直接回写数据给客户端

   //@Controller
   //@ResponseBody
   @RestController
   @RequestMapping("/yu")
   public class ParamController{
       //请求路径:http://localhost/yu/responsebody
       @GetMapping("/responsebody")
      // @ResponseBody //告诉SpringMVC返回的字符串不是视图名,而是响应体的数据
       public String param1(@RequestBody String jsonStr){
           System.out.println(jsonStr);
           return "hello world";
       }
   } 

7.Restful风格数据提交

  1. 什么是restful风格。

    • 每个模块都是一个名词,用UR地址表示模块资源。

    • 请求:用请求方式表示当前模块下业务的动作(GET表示查询、POST表示插入、PUT表示更新、Delete表示删除)

    • 响应:用HTTP响应状态码表示结果,通常封装在Result实体中(状态码,状态信息,响应数据)

  1. Restful风格的请求,常见的模块规则有以下三种

模块URI资源
用户模块 userhttp://localhost/user
商品模块 producthttp://localhost/product
账户模块 accounthttp://localhost/account
日志模块 loghttp://localhost/log
  1. 常见请求动作

8.SpringMVC访问静态资源的三种方法

  1. SpringMVC无法访问静态资源的原因:原有的javaWeb程序中,存在一个DefaultServlet(Tomcat的config的web.xml可以找到),按照访问的文件名查找相应的Servlet,如果没有匹配到Servlet,则会用资源名找DefaultServlet,DefaultServlet具备查找静态资源的能力。而Tomcat中,DefaultServlet的匹配路径是“/”,在SpringMVC中,前端控制器DispatherServlet的匹配路径也是“/”,也就是说DispatherServlet将Tomcat中的DefaultServet覆盖掉了。所以无法访问静态资源。

  2. 解决方法:

    1. 再次激活DefalutServlet url-pattern配置更加精确一点

    <servlet-mapping>
    	<servlet-name>default</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    
    或者:
    <servlet-mapping>
    	<servlet-name>default</servlet-name>
        <url-pattern>/img/*</url-pattern>
    </servlet-mapping>
    
    1. 配置资源的映射路径

    请求路径为"/image/*"到"/img"下寻找资源
    <mvc:resources mapping="/image/*" location="/img/"></mvc:resources>
    
    1. 在spring-mvc.xml中配置<mvc:default-servlet-handler>,

    原理:该方式是在SpringMVC容器中注册了DefaultServletHttpRequestHandler处理器,改处理器会自动寻找静态资源。

    1. 采用方法2和方法3导致的问题:127-SpringMVC框架-annotation-driven的使用机器原理_哔哩哔哩_bilibili

    SpringMVC加载组件时,如果容器中没有处理器映射器时会默认加载处理器映射器(HanderMapping)的组件其中重要的是RequestMappingHanderMapping,用于解析请求路径的组件,但如果手动加载了其他的HanderMapping,则默认加载策略会失效。而配置2或3标签时,涉及到自定义命名空间解析,底层会注入SimplyUrlHanderMapping组件,此时SpringMVC将不会加载默认组件,也就没有了RequestMappingHanderMapping(解析注解),需要在spring-mvc.xml中向容器中注入一个RequestMappingHanderMapping。这样就实现了既能加载静态资源,又能解析注解,程序能正常运行。

    1. 由于上述配置繁琐的原因,Spring提供了一个标签--->MVC注解驱动

9.mvc注解驱动

mvc注解驱动内部会

  • 注册RequestMappingHandlerMapping

  • 注册RequestMappingHandlerAdapter

  • 注入Json消息转换器等(默认为MappingJackson2HttpMessageConverter)

<!--mvc注解驱动-->
<mvc:annotation-driven/>
<!--配置DefalutServletHttpRequestHandler,用于扫描静态资源-->
<mvc:default-servlet-handler>

*10.Spring容器 SpringMVC容器 web容器的关系

出处声明:Spring容器 SpringMVC容器 web容器的关系 - 知乎 (zhihu.com)

首先: springmvc和spring它俩都是容器,容器就是管理对象的地方,例如Tomcat,就是管理servlet对象的,而springMVC容器和spring容器,就是管理bean对象的地方,再说的直白点,springmvc就是管理controller对象的容器,spring就是管理service和dao的容器,这下你明白了吧。所以我们在springmvc的配置文件里配置的扫描路径就是controller的路径,而spring的配置文件里自然配的就是service和dao的路径。

 至于他是怎么管理起来的,又是怎么注入属性的,这就涉及到他们底层的实现技术了

  其次, spring容器和springmvc容器的关系是父子容器的关系。spring容器是父容器,springmvc是子容器。在子容器里可以访问父容器里的对象,但是在父容器里不可以访问子容器的对象,说的通俗点就是,在controller里可以访问service对象,但是在service里不可以访问controller对象

  所以这么看的话,所有的bean,都是被spring或者springmvc容器管理的,他们可以直接注入。然后springMVC的拦截器也是springmvc容器管理的,所以在springmvc的拦截器里,可以直接注入bean对象。

 而web容器又是什么鬼,

  web容器是管理servlet,以及监听器(Listener)和过滤器(Filter)的。这些都是在web容器的掌控范围里。但他们不在spring和springmvc的掌控范围里。因此,我们无法在这些类中直接使用Spring注解的方式来注入我们需要的对象,是无效的,

web容器是无法识别的。

  但我们有时候又确实会有这样的需求,比如在容器启动的时候,做一些验证或者初始化操作,这时可能会在监听器里用到bean对象;又或者需要定义一个过滤器做一些拦截操作,也可能会用到bean对象。

web容器中有servlet容器,spring项目部署后存在spring容器和springmvc容器。其中spring控制service层和dao层的bean对象。springmvc容器控制controller层bean对象。servlet容器控制servlet对象。项目启动是,首先 servlet初始化,初始化过程中通过web.xml中spring的配置加载spring配置,初始化spring容器和springmvc容器。待容器加载完成。servlet初始化完成,则完成启动。 HTTP请求到达web容器后,会到达Servlet容器,容器通过分发器分发到具体的spring的Controller层。执行业务操作后返回结果。

总结:

Tomcat在启动时给每个Web应用创建一个全局的上下文环境,这个上下文就是ServletContext,其为后面的Spring容器提供宿主环境。

Tomcat在启动过程中触发容器初始化事件,Spring的ContextLoaderListener会监听到这个事件,它的contextInitialized方法会被调用,在这个方法中,Spring会初始化全局的Spring根容器,这个就是Spring的IoC容器,IoC容器初始化完毕后,Spring将其存储到ServletContext中,便于以后来获取。

Tomcat在启动过程中还会扫描Servlet,一个Web应用中的Servlet可以有多个,以SpringMVC中的DispatcherServlet为例,这个Servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个Servlet请求。

Servlet一般会延迟加载,当第一个请求达到时,Tomcat&Jetty发现DispatcherServlet还没有被实例化,就调用DispatcherServlet的init方法,DispatcherServlet在初始化的时候会建立自己的容器,叫做SpringMVC 容器,用来持有Spring MVC相关的Bean。同时,Spring MVC还会通过ServletContext拿到Spring根容器,并将Spring根容器设为SpringMVC容器的父容器,请注意,Spring MVC容器可以访问父容器中的Bean,但是父容器不能访问子容器的Bean, 也就是说Spring根容器不能访问SpringMVC容器里的Bean。说的通俗点就是,在Controller里可以访问Service对象,但是在Service里不可以访问Controller对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值