SpringMVC入门详解

SpringMVC概述

开始之前了解一下三层架构

在JavaEE中我们现在几乎全用的是B/S架构,也就是浏览器 / 服务器架构,在B/S架构中,系统包括标准的三层架构模式:

1.web层(表现层)

它负责接收客户端请求,向客户端响应结果;通常客户端使⽤http 协议请求web 层,web 需要接收 http 请求,完成 http 响应

而我们的表现层 用的就是MVC设计模式 (和其他层没有关系)

MVC包括:Model(模型) View(视图) Controller(控制)

MVC提倡:每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码;分层是为了解耦:解耦是为了维护⽅便和分⼯协作

Model一般用的是数据模型:存储实体对象(封装数据)

View通常指的就是jsp 或者 html,负责结果的展示

Controller负责接收请求,用来处理用户的交互,处理程序逻辑

web层依赖业务层——当表现层里的**控制层接收到客户端请求后**——会**调⽤业务层进⾏业务处理**——并将**处理结果响应给客户端**
2.service层(业务层)

它负责业务逻辑处理,和我们开发项⽬的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层;

业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务⼀致性。(所以, 事务应该放到业务层来控制)

3.dao层(持久层)

负责数据持久化;通俗的讲,持久层就是负责和数据库交互,对数据库表进⾏增删改查的

如何理解SpringMVC

  • SpringMVC是基于Spring框架的,Spring是一个容器,而SpringMVC也可以看作是一个容器。其实从类名来看,Spring做javase项目其容器对象叫ApplicationContext,Spring做web项目就是SpringMVC,其容器对象叫做WebApplicationContext。ApplicationContext是WebApplicationContext的超类

  • SpringMVC就是Spring的一个升级,是专门用来做web开发的。web开发底层用的是Servlet,SpringMVC底层也是Servlet。

  • 综上,SpringMVC就是一个容器,能够创建对象,将对象保存到容器中,在把容器对象保存在application域中。SpringMVC是controller层的框架,可以在springmvc配置文件中用<bean>创建对象,也可以用注解,以后只用注解。Spring中创建对象的注解有四种,分别是:@Component、@Resportry、@Service、@Controller。而SpringMVC创建对象使用@Controller注解,因为SpringMVC是专门用来做web开发的,其创建的是Controller对象。

总结:

Spring MVC是为了解决表现层问题 的web框架,是基于 MVC 设计模式的。⽽这些表现层框架的主要职责就是处理前端HTTP请求。Spring MVC 本质可以认为是对servlet的封装,简化了我们serlvet的开发

Servlet对象与Controller对象的区别

​ web开发的底层对象就是Servlet,但是直接使用它很麻烦,而且功能很少。因此,我们使用SpringMVC框架,SpringMVC是一个容器,创建controller对象,之之前我们在controller层创建的都是servlet对象,用servlet来接收请求,发送响应。而在SpringMVC中,我们不创建Servlet,在controller层,我们就仅仅创建controller对象,该对象只是一个普通类,不是Servlet对象。

​ 综上,servlet一定是controller对象,因为它是在controller层完成职能;而controller对象不一定是servlet,在springmvc中controller对象就是普通类对象。

DispatherServlet(中央调度器)

​ SpringMVC是一个容器,保存的是controller对象,该对象是我们创建的,它不是一个Servlet。那么怎么接收请求和响应数据呢?

​ SpringMVC框架中有一个类DispatherServlet(中央调度器),DispatherServlet的超类是HttpServlet,因此它是Servlet。我们使用SpringMVC框架做web开发时,前端发送请求,首先被tomcat接收,tomcat把请求转交给DispatherServlet,它在根据资源具体名字把请求转交给Controller对象,最后Controller处理请求,Controller调service层,service调dao层将数据再返回给controller,controller中将数据再交给DispatherServlet,由DispatherServlet完成响应。

​ 结论:DispatherServlet根据uri转交请求给对应的controller对象,并将controller的数据响应给前端。controller对象就是接收请求和发响应,只不过它过了底层的DispatherServlet,由DispatherServlet来响应数据给前端。

​ DispatherServlet是一个Servlet,每个Servlet有init()方法,在该方法中创建了springmvc容器对象WebApplicationContext。因此,我们要求在tomcat服务器启动时,就要创建DispatherServlet,同时在它内部创建了容器对象,容器对象去扫描springmvc配置文件,将配置文件中用到的对象都创建出来保存在容器中,然后再将容器对象保存在application域中,以上过程都是在tomcat服务器启动时完成。DispatherServlet中央调度器在tomcat启动时创建,在web.xml文件中配置。

<!--声明,注册springmvc的核心对象DispatcherServlet
        需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。
        为什么要创建DispatcherServlet对象的实例呢?
        因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,
        读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起
        请求时就可以直接使用对象了。

        servlet的初始化会执行init()方法。 DispatcherServlet在init()中{
           //创建容器,读取配置文件
           WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
           //把容器对象放入到ServletContext中
           getServletContext().setAttribute(key, ctx);	
        }

        启动tomcat报错,读取这个文件 /WEB-INF/springmvc-servlet.xml(/WEB-INF/myweb-servlet.xml)
        springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml .
-->
<!--声明中央调度器-->
<servlet>
    <servlet-name>myWeb</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--自定义springmvc读取的配置文件的位置-->
    <init-param>
        <!--springmvc的配置文件的位置的属性-->
        <param-name>contextConfigLocation</param-name>
        <!--指定自定义文件的位置-->
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--在tomcat启动后,创建Servlet对象
            load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
                            tomcat创建对象的时间越早。 大于等于0的整数。
        -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>myWeb</servlet-name>
    <!--
            使用框架的时候, url-pattern可以使用两种值
            1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
               不能使用 *.jsp
               http://localhost:8080/myweb/some.do
               http://localhost:8080/myweb/other.do

            2.使用斜杠 "/"
        -->
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MVC组件

​ MVC是一种设计模式,modal模型表示数据、View视图表示页面、Controller控制器分为前端控制器和后端控制器。DispatherServlet是前端控制器,用来接收请求中数据,以及发送响应;Controller类是后端控制器用来调service完成业务处理,把service层处理好的数据返回给DispatherServlet

image-20220107205111810

第一个SpringMVC项目

基于SpringMVC完成一个简单的web项目。复习web项目数据怎么传输的,DispatherServlet的作用,Controller对象的作用。DispatherServlet是前端控制器(front-controller),Controller类是后端控制器(back controller)

  1. 在web.xml中声明中央调度器DispatherServlet,在tomcat启动时创建该对象,并在内部创建容器对象,以及controller对象

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
             version="5.0">
    
        <!--声明中央调度器-->
        <servlet>
            <servlet-name>myWeb</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>myWeb</servlet-name>
            <url-pattern>*.do</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    注意:以上中央调度器的声明式模板,以后用直接复制,只用改中的就可以

  2. 创建springmvc配置文件,在DispatherServlet创建时,由容器对象加载

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--声明组件扫描器-->
        <context:component-scan base-package="com.xd.controller"/>
        
        <!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!--前缀:视图文件的路径-->
            <property name="prefix" value="/WEB-INF/view/"/>
            <!--后缀:视图文件的扩展名-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    
  3. 创建controller类,添加@Controller注解,表示该对象由springmvc容器创建

    /**
     *  @Controller:创建处理器对象,对象放在springmvc容器中。
     *  位置:在类的上面
     *  和Spring中讲的@Service ,@Component
     *
     *  能处理请求的都是控制器(处理器): MyController能处理请求,
     *                         叫做后端控制器(back controller)
     *
     *  没有注解之前,需要实现各种不同的接口才能做控制器使用
     */
    @Controller
    public class MyController {
        /*
           处理用户提交的请求,springmvc中是使用方法来处理的。
           方法是自定义的, 可以有多种返回值, 多种参数,方法名称自定义
         */
    
        /**
         * 准备使用doSome方法处理some.do请求。
         * @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。
         *                  一个请求指定一个方法处理。
         *       属性: 1. value 是一个String[],表示请求的uri地址的(some.do)。
         *                value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”
         *       位置:1.在方法的上面,常用的。
         *            2.在类的上面
         *  说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。
         *  使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost
         *
         *  返回值:ModelAndView 表示本次请求的处理结果
         *   Model: 数据,请求处理完成后,要显示给用户的数据
         *   View: 视图, 比如jsp等等。
         */
        @RequestMapping(value = {"/doSome.do","/doOther.do"})
        public ModelAndView doSome(){
    
            //数据可以从service层获取
            String data = "访问doSome.do获取到的数据";
            
            ModelAndView mv = new ModelAndView();
            
            //添加数据, 框架在请求的最后把数据放入到request作用域。
            //request.setAttribute("msg","欢迎使用springmvc做web开发");
            mv.addObject("data", data);
            
            //指定视图, 指定视图的完整路径
            //框架对视图执行的forward操作, request.getRequestDispather("/doSome.jsp).forward(...)
            //mv.setViewName("doSome.jsp");
            //为了防翻墙,将资源放到WEB-INF目录下,表示受保护
            //mv.setViewName("/WEB-INF/view/doSome.jsp");
    
            //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
            //框架会使用视图解析器的前缀 + 逻辑名称 + 后缀 组成完成路径, 这里就是字符连接操作
            ///WEB-INF/view/ + show + .jsp
            mv.setViewName("doSome");
    
            //mv.setView( new RedirectView("/a.jsp"));
            
            return mv;
        }
    }
    

注意:

  • 后端路径要以/开头,表示从根路径下开始。如/show.jsp表示webapp下的show.jsp文件

  • 防用户翻墙,一般把.jsp文件保存在WEB-INF目录下,该目录下文件受保护,用户无法在浏览器直接访问,只能在后端访问。但是这样会导致资源路径过长如/WEB-INF/view/doSome.jsp,可以在springmvc配置文件中使用视图解析器,来指定视图文件路径以及扩展名。

  • 不同请求可以执行Controller类中同一个方法,同一个Controller类中也可以写多个方法用来处理不同请求。如在UserController中写四个方法CURD,根据路径走不同方法。注意:@RequestMapping注解value值唯一

    @Controller
    public class UserController{
        @RequestMapping(value = {"/insertUser.do","/addUser.do"})
        public ModelAndView insertUser(){
        }
    
        @RequestMapping(value = {"/deleteUser.do","/removeUser.do"})
        public ModelAndView deleteUser(){
        }
    
        @RequestMapping(value = {"/updateUser.do","/modifyUser.do"})
        public ModelAndView updateUser(){
        }
    
        @RequestMapping(value = {"/selectUser.do","/queryUser.do"})
        public ModelAndView selectUser(){
        }
    }
    

springmvc请求的处理流程

  1. 发起some.do
  2. tomcat(web.xml–url-pattern知道 *.do的请求给DispatcherServlet)
  3. DispatcherServlet(根据springmvc.xml配置知道 some.do—doSome())
  4. DispatcherServlet把some.do转发个MyController.doSome()方法
  5. 框架执行doSome()把得到ModelAndView进行处理, 转发到show.jsp

以上过程简化为:some.do—DispatcherServlet—MyController

springmvc执行过程源代码分析

  1. tomcat启动,创建容器的过程

    通过load-on-start标签指定的1,创建DisaptcherServlet对象,DisaptcherServlet它的父类是继承HttpServlet的, 它是一个serlvet, 在被创建时,会执行init()方法。在init()方法中:创建springmvc容器对象,容器对象扫描springmvc配置文件,创建对象放入容器内,然后将容器保存到application域中

    //创建容器,读取配置文件
    WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
    //把容器对象放入到ServletContext中
    getServletContext().setAttribute(key, ctx);
    

    注意:上面创建容器作用: 创建@controller注解所在的类的对象, 创建MyController对象,这个对象放入到 springmvc的容器中, 容器是map , 类似 map.put(“myController”,MyController对象)

  2. 请求的处理过程

    1. 执行servlet的service()

      //方法调用关系
      protected void service(HttpServletRequest request, HttpServletResponse response)
      protected void doService(HttpServletRequest request, HttpServletResponse response)
      DispatcherServlet.doDispatch(request, response){
      	//调用MyController的.doSome()方法
      }
      

      doDispatch:DispatcherServlet的核心方法, 所有的请求都在这个方法中完成的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SpringMVC注解式开发(重点)

主要研究处理器方法,它的@RequestMapping注解的使用、形参的含义、返回值类型不同代表的含义

处理器方法能匹配请求路径,能接参,能调service处理,能返回结果

@RequestMapping注解的使用

  • @RequestMapping使用位置:

    • 处理器方法上:作用是将前端请求与该方法绑定起来。即前端发请求,DispatherServlet根据uri找到对应的处理器方法执行请求。
    • Controller类上:一般做模块模块名称,是该controller类中所有方法请求地址的公共部分。
  • @RequestMapping注解有两个属性,value和method。value表示该方法绑定的uri,是一个String[ ];method表示请求方式,如RequestMethod.POST,RequestMethod是一个枚举类,里面是7种请求方式。

    @Controller
    @RequestMapping("/user") //表示该controller类所在模块是/user
    public class MyController {
    	@RequestMapping(value = "/doSome.do", method = RequestMethod.GET) //实际前端发送的请求是:/user/doSome.do
        public ModelAndView doSome(HttpServletRequest request){
            //方法块
        }
    
        @RequestMapping(value = "/doOther.do", method = RequestMethod.POST)
        public ModelAndView doOther(HttpServletRequest request){
            //方法块
        }
    }
    

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

处理器方法的形参

  • 处理器方法的形参其实就是请求参数。它是由DispatherServlet从request中取值,然后按照某些对应关系赋值给处理器方法的形参。程序员直接用。

  • 处理器方法的形参可以是:HttpServletRequest、HttpServletResponse、HttpSession、请求中所携带的请求参数。

    • HttpServletRequest、HttpServletResponse、HttpSession表示DispatherServlet直接将域对象传给处理器方法,处理器方法从域对象中取参数。

      @RequestMapping(value = "/doSome.do", method = RequestMethod.GET)
      public ModelAndView doSome(HttpServletRequest request,
                                HttpServletResponse response,
                                HttpSession session){ //相当于DispatherServlet将请求request转发给了Controller
          //从request中取值,与Servlet相同
          String name = request.getParameter("name");
          String age = request.getParameter("age");
          ModelAndView mv = new ModelAndView();
          mv.addObject("name", name);
          mv.addObject("age", age);
          mv.setViewName("doSome");
          return mv;
      }
      
    • 请求中所携带的请求参数表示DispatherServlet直接把请求参数传给处理器方法。方式有两种:逐个接收以java对象接收

      • 逐个接收:按照处理器方法形参名与请求参数名进行匹配,与参数的位置无关,比名字。

        /**
             * 逐个接收请求参数:
             *   要求: 处理器(控制器)方法的形参名和请求中参数名必须一致。同名的请求参数赋值给同名的形参,与位置无关
             
             * 框架接收请求参数
             *   1. 使用request对象接收请求参数,springmvc框架底层还是servlet
             *      String strName = request.getParameter("name");
             *      String strAge = request.getParameter("age");
             *   2. springmvc框架通过 DispatcherServlet 调用 MyController的doSome()方法
             *      调用方法时,按名称对应,把接收的参数赋值给形参
             *      doSome(strName,Integer.valueOf(strAge))
             *      框架会提供类型转换的功能,能把String转为 int ,long , float, double等类型。
             */
        @RequestMapping(value = "/doSome.do", method = RequestMethod.GET)
        public ModelAndView doSome(String name, int age){
            ModelAndView mv = new ModelAndView();
            mv.addObject("name", name);
            mv.addObject("age", age);
            mv.setViewName("doSome");
            return mv;
        }
        
        /*
        问题1:请求中的数据无法转换为处理器方法参数的类型导致400错误
        	DispatherServlet调Controller类的doSome(name, Integer.valueOf(age))
        	400状态码是客户端错误, 表示提交请求参数过程中,发生了问题。
        解决办法:参数的类型都用String,保证不会出错,而且String类型很方便使用,即使数据库中也是char和varchar
        */
        public ModelAndView doSome(String name, Integer age)
            
        /*
        问题2:请求参数名与处理器方法参数名不一致,会导致无法赋值,原理是根据参数名匹配赋值,不匹配当然无法赋值。
        解决办法:在处理器方法参数名前使用@RequestParam注解,如@RequestParam("rname") String name,表示将请求参数中名为rname的参数赋值		  给处理器方法的name参数。与MyBatis的@Param注解相同。
        */
        @RequestMapping(value = "/doOther.do", method = RequestMethod.POST)
        public ModelAndView doOther(@RequestParam("rname") String name, @RequestParam("rage") Integer age){
            //name:null, age:null 请求参数名与处理器方法名不一致
            System.out.println("name:" + name + ", age:" + age);
            //数据可以从service层获取
            ModelAndView mv = new ModelAndView();
            mv.addObject("name", name);
            mv.addObject("age", age);
            mv.setViewName("doOther");
            return mv;
        }
        

        注意:

        • 处理器方法参数的类型,DispatherServlet从request中取出的数据类型都是String,若处理器方法参数类型不是String,可能会存在类型转换的错误,前端报400错误(如年龄,处理器参数若是int,前端页面输入整形不会报错,但是若输入abc或不输入,那么就出错,因为DispatherServlet接收到的字符串“abc”无法转型为int)。因此,一般参数类型使用String,因为String方便处理,数据库中类型也是char与varchar
        • 400错误码:在请求把数据发给controller过程中发生的错误。发生这种错误大部分情况是,前端发的数据与后端Controller中方法的形参类型不匹配,导致框架做类型转换时失败
        • 请求参数名与处理器方法参数名不一致时,在处理器方法的参数前加@RequestParam注解用来指定请求参数名与处理器方法参数名的对应关系。
      • java对象接收:由框架创建Student对象,实际应用最多。可以直接从对象里取值,也可以把对象传给service和dao。处理器方法的形参可以有多个对象,互相干扰,框架都会创建他们的实例完成赋值

        /**
             * Student的无参构造方法执行了
             * setage:22
             * setname:张三
             * name:张三, age:22
             */
        @RequestMapping(value = "/doParamObject.do", method = RequestMethod.POST)
        public ModelAndView doOther(Student student){
            System.out.println("name:" + student.getName() + ", age:" + student.getAge());
            //数据可以从service层获取
            ModelAndView mv = new ModelAndView();
            mv.addObject("name", student.getName());
            mv.addObject("age", student.getAge());
            mv.setViewName("doOther");
            return mv;
        }
        

        执行原理:创建一个vo类或者直接使用现成domain类作为处理器方法的参数,注意vo类或domain类的属性名必须和请求参数名一致。框架执行到该方法时,看到参数是一个java对象,会由容器创建一个java对象,并将DispatherServlet从request中取出参数作为该java对象的属性值。如请求参数是name,框架会调用setName(),set注入。

  • 总结:

    • 接收参数用的是处理器方法的形参。可以逐个接收;也可以用java对象接收参数,java对象接收参数用最多

    • 逐个接收要求请求参数必须和处理器参数名一直,按属性名赋值。如果不一致,需要用@RequestParam注解来指定对应关系(与MyBatis的@Param相同),@RequestParam在java对象接收中无效。

    • java对象接收参数,要求该对象的属性名与请求参数名一直,原理是set注入。该java对象在处理器方法中为形参,执行处理器方法是,框架会自动创建该java对象,完成赋值。

post请求乱码问题

  • 参数乱码:get请求不会有乱码,post请求数据是中文会有乱码。使用过滤器解决post请求乱码,过滤器可以自定义,也可使用框架中提供的过滤器 CharacterEncodingFilter。这里使用框架的过滤器,与Spring框架自带的监听器一样。

    <!--使用SpringMVC框架自带的过滤器-->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!--CharacterEncodingFilter类有三个属性,需要赋值-->
        <!--第一个属性encoding:设置为utf-8,表示编码方式-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <!--第二个属性forceRequestEncoding:设置为true,表示强制请求编码方式-->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!--第三个属性forceResponseEncoding:设置为true,表示强制响应编码方式-->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!--设置过滤器过滤的路径-->
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <!--一般只对动态资源进行过滤,静态资源不需要过滤,jsp有@page指令-->
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
    
  • CharacterEncodingFilter的执行源码:

    //继承结构
    public class CharacterEncodingFilter extends OncePerRequestFilter
    public abstract class OncePerRequestFilter extends GenericFilterBean
    public abstract class GenericFilterBean implements Filter
    //过滤器的核心方法是doFilter方法
    //CharacterEncodingFilter类的方法,重写了父类的doFilterInternal
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain){
        //获取web.xml文件中CharacterEncodingFilter的参数encoding
        String encoding = getEncoding();
        if (encoding != null) {
            //isForceRequestEncoding()获取的是forceRequestEncoding参数,在配置过滤器时赋值为true
            if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                //设置请求的编码方式为encoding,即utf-8
                request.setCharacterEncoding(encoding);
            }
            if (isForceResponseEncoding()) {
                //设置响应的编码方式为encoding,即utf-8
                response.setCharacterEncoding(encoding);
            }
        } 
        //放行
        filterChain.doFilter(request, response);
    }
    

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

处理器方法的返回值类型

​ 处理器方法的返回值有4种,分别是:ModelAndView、String、void、Object。每一种返回值类型都有其适用场景。要根据不同场景选用不同返回值。

返回ModelAndView

​ 若处理器方法执行完成后,即要返回数据,又要返回视图,即跳转一个页面。就用ModelAndView类型作为返回值。其中Model表示数据,View表示视图。框架对视图进行的是forward操作。若只有数据或只有视图就不用它,大材小用。

/**
     * Student的无参构造方法执行了,该对象由容器创建
     * setage:22
     * setname:张三
     */
@RequestMapping(value = "/doSome.do", method = RequestMethod.POST)
public ModelAndView doSome(Student student){
    ModelAndView mv = new ModelAndView();
    //返回的数据
    mv.addObject("name", student.getName());
    mv.addObject("age", student.getAge());
    mv.addObject("student", student);
    //返回的视图,采用视图的逻辑名称,要求配置视图解析器
    mv.setViewName("doSome");
    return mv;
}

返回String

​ 若处理器方法执行完成后,只返回页面,不反回数据,处理器方法的返回值就用String类型。返回的字符串可以是页面的完整路径,也可以是逻辑视图名称。

@RequestMapping(value = "/returnString.do", method = RequestMethod.POST)
public String doFirst(HttpServletRequest request, Student student){
    //使用Sting返回的是页面,但是也可以将request作为参数传进来,给request域中保存数据,前端用EL表达式从request中取值。
    request.setAttribute("name", student.getName());
    request.setAttribute("age", student.getAge());
    //返回的视图,采用视图的逻辑名称,要求配置视图解析器
    //return "returnString";
    //消息 文.件[/WEB-INF/view/WEB-INF/view/returnString.jsp.jsp] 未找到
    //注意用完整路径就不能有视图解析器;用逻辑视图名称就必须有视图解析器。框架是视图进行的是forword操作
    return "/WEB-INF/view/returnString.jsp";
}

注意:用完整路径就不能有视图解析器;用逻辑视图名称就必须有视图解析器。框架是视图进行的是forword操作

返回void(了解)

​ 若处理器方法执行完成后,无需跳转任何页面,可以用void。一般用于ajax请求的响应。但是用的不多,对ajax请求返回值用Object更多。

//处理器方法返回void, 响应ajax请求
//手工实现ajax,json数据: 代码有重复的 1. java对象转为json; 2. 通过HttpServletResponse输出json数据
@RequestMapping(value = "/returnvoid.do", method = RequestMethod.POST)
public void doSecond(String name, String age, HttpServletResponse response) throws IOException {

    //调service层处理数据,假设数据已经处理好
    Student student = new Student();
    student.setName(name);
    student.setAge(age);

    //将结果的对象使用jackson工具库转化为json字符串
    ObjectMapper om = new ObjectMapper();
    String jsonStudent = om.writeValueAsString(student);

    //使用response将json字符串输出至浏览器,响应ajax请求,response是应答对象
    response.setContentType("application/json;charset=utf-8");
    PrintWriter out = response.getWriter();
    out.print(jsonStudent);
    out.flush();
    out.close();
    return;
}

注意:

  • response.setContentType(“application/json;charset=utf-8”);是告诉浏览器响应的是json数据,在响应头的content-type可以看到;jsp的jq代码中的dataType:"json"是告诉服务器想要json格式数据,从请求头中可以看出。如果不加dataType,但是应答对象还是以json格式响应数据,jQuery会尝试将该json字符串转化为json对象使用。
  • 浏览器将请求包以ISO-8859-1发给服务器,服务器要制定解码方式,比如在过滤器中以utf-8解码,否则会出现post请求乱码问题。而服务器把处理完数据发给浏览器以json字符串的形式,jQuery会自动将json字符串转换为json对象使用。
  • 以上代码有重复的 1. java对象转为json; 2. 通过HttpServletResponse输出json数据。可以交给框架去做。

返回Object(重点)

  • 若处理器方法执行完成后,只返回数据,可以返回Object,例如Student、User等都是Object,对象的属性就是数据,和视图无关,响应ajax请求

  • 处理器方法返回Object类型主要响应ajax请求,ajax请求只要数据,不要视图。也可以使用void,但一般用Object,由框架将java对象转换为json字符串,由框架将数据响应给前端,而不需要我们自己用jackson转换,用response响应。

  • 实现步骤:

    1. 加入处理json的工具库的依赖, springmvc默认使用的jackson。

    2. 在sprigmvc配置文件之间加入 <mvc:annotation-driven> 注解驱动。

      json  = om.writeValueAsString(student);
      
    3. 在处理器方法的上面加入@ResponseBody注解。

      //通过HttpServletResponse输出数据,响应ajax请求的。
      response.setContentType("application/json;charset=utf-8");
      PrintWriter pw  = response.getWriter();
      pw.println(json);
      
分析原理(源码)

​ springmvc处理器方法返回Object, 可以转为json输出到浏览器,响应ajax的内部原理

  • <mvc:annotation-driven>注解驱动

    • 注解驱动实现的功能是 完成java对象到json,xml, text,二进制等数据格式的转换。<mvc:annotation-driven>在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口的8个实现类对象。分别是:

      org.springframework.http.converter.ByteArrayHttpMessageConverter
      org.springframework.http.converter.StringHttpMessageConverter
      org.springframework.http.converter.ResourceHttpMessageConverter
      org.springframework.http.converter.ResourceRegionHttpMessageConverter
      org.springframework.http.converter.xml.SourceHttpMessageConverter
      org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
      org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
      org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
      

      其中只需要知道==StringHttpMessageConverterMappingJackson2HttpMessageConverter==这两对象。StringHttpMessageConverter是处理字符串数据的读写;MappingJackson2HttpMessageConverter是负责将java对象转化为json字符串,底层用的是jackson工具库的ObjectMapper类。

    • HttpMessageConverter接口:消息转换器。定义了java转为json,xml等数据格式的方法。 这个接口有很多的实现类。这些实现类完成 java对象到json, java对象到xml,java对象到二进制数据的转换。HttpMessageConverter接口有5个方法:

      public interface HttpMessageConverter<T> {
          //以下两个方法是控制器类读取浏览器输入数据时使用
          boolean canRead(Class<?> var1, @Nullable MediaType var2); 
          T read(Class<? extends T> var1, HttpInputMessage var2);
      	
          //下面的两个方法是控制器类把结果输出给浏览器时使用的:
          //canWrite作用检查处理器方法的返回值,能不能转为var2表示的数据格式。能转返回true。MediaType类型里面定义了许多数据格式,如图片、md
          boolean canWrite(Class<?> var1, @Nullable MediaType var2);  
          //write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串。
          void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3);
          
          List<MediaType> getSupportedMediaTypes(); //不关心
      }
      

      注意:canWrite方法只判断控制器方法返回的数据能否转化为var2,如果能才会用write方法将其转化为json数据。

      json=om.writeValueAsString(student);
      
    • 根据对DispatcherServlet进行debug调试看出来的,得到以下结论:

      • 不加注解驱动,DispatherServlet中生成的HttpMessageConverter对象的实现类只有四个,不含MappingJackson2HttpMessageConverter对象。

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      • 加了注解驱动,DispatherServlet中生成的HttpMessageConverter对象的实现类有8个,会生成MappingJackson2HttpMessageConverter对象,才能将java对象转化为json数据

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 调试步骤:

      1. 在DispatcherServlet的核心方法doDispatcher()打断点,以及里面的request.getMethod()打断点
      2. 观察debug窗口中this->handlerAdapters->elementDate->0->messageConverters中的实现类
实例
返回Studnet
  • 返回对象框架的处理流程:
    1. 框架会把返回Student类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法,检查那个HttpMessageConverter接口的实现类能处理Student类型的数据–MappingJackson2HttpMessageConverter。
    2. 框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法把李四同学的student对象转为json, 调用Jackson的ObjectMapper实现转为json,默认响应数据格式:contentType: application/json;charset=utf-8
    3. 框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
/**
     * 处理器方法返回一个Student,通过框架转为json,响应ajax请求
     * @ResponseBody:
     *    作用:把处理器方法返回对象转为json后,通过HttpServletResponse输出给浏览器。
     *    位置:方法的定义上面。 和其它注解没有顺序的关系。
     */
@RequestMapping(value = "/returnStudent.do", method = RequestMethod.POST)
@ResponseBody
public Student doReturnStudent(String name, String age){
    //调service层处理数据,假设数据已经处理好
    Student student = new Student();
    student.setName("张三同学");
    student.setAge("20");
    return student;
}
返回List<Studnet>
  • 返回对象框架的处理流程:
    1. 框架会把返回List<Student>类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法,检查那个HttpMessageConverter接口的实现类能处理Student类型的数据–MappingJackson2HttpMessageConverter。
    2. 框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法把李四同学的student对象转为json, 调用Jackson的ObjectMapper实现转为json array,默认响应数据格式:contentType: application/json;charset=utf-8
    3. 框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
//处理器方法返回List<Student>
@RequestMapping(value = "/returnStudentList.do", method = RequestMethod.POST)
@ResponseBody
public List<Student> doReturnStudentList(String name, String age){

    List<Student> studentList = new ArrayList<>();
    //调service层处理数据,假设数据已经处理好
    Student student = new Student();
    student.setName("张三同学");
    student.setAge("20");
    studentList.add(student);

    student = new Student();
    student.setName("李四同学");
    student.setAge("22");
    studentList.add(student);

    return studentList;
}
返回String
  • 处理器方法返回的是String ,String可以表示数据,也可以表示视图。具体区分看有没有@ResponseBody注解,如果有String表示文本数据,反之,为视图。
  • 若返回值为String表示数据,则后端默认使用“text/plain;charset=ISO-8859-1”作为contentType,导致中文有乱码,因为前端jsp采用utf-8。解决方案:给RequestMapping增加一个属性 produces, 使用这个属性指定新的contentType.
  • 返回对象框架的处理流程:
    1. 框架会把返回String类型,调用框架的中ArrayList中每个类的canWrite()方法检查那个HttpMessageConverter接口的实现类能处理String类型的数据–StringHttpMessageConverter
    2. 框架会调用实现类的write(), StringHttpMessageConverter的write()方法把字符按照指定的编码处理 text/plain;charset=ISO-8859-1
    3. 框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
//返回值为String,String表示数据
@RequestMapping(value = "/returnStringText.do", method = RequestMethod.POST, produces = "text/plain;charset=utf-8")
@ResponseBody
public String doReturnString(String name, String age){
    return "返回数据为String类型,是一个文本";
}

总计:SpringMVC注解式开发主要学的就是处理器方法

  • @RequestMapping是将请求路径与处理器方法对应起来,即根据不同的uri去执行不同的方法;
  • 处理器方法的形参,该形参其实就是请求的参数,可以逐个接收或者java对象接收
  • 接收到了请求的参数,可以把参数传给service层进行业务处理,最终将处理好的数据保存返回给Dispatcher,有中央调度器响应。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解读<url-pattern>

<url-pattern>在SpringMVC框架中是DispatcherServlet的<url-pattern>

分析请求由谁处理

  • 若DispatcherServlet的<url-pattern>不为”/“,那么静态资源(jsp、css、jpg)由tomcat处理,动态资源(*.do)由DispatcherServlet中央调度器处理。

    http://localhost:8080/ch05_url_pattern/index.jsp :tomcat(jsp会转为servlet)
    http://localhost:8080/ch05_url_pattern/js/jquery-3.4.1.js : tomcat
    http://localhost:8080/ch05_url_pattern/images/p1.jpg : tomcat
    http://localhost:8080/ch05_url_pattern/html/test.html: tomcat
    http://localhost:8080/ch05_url_pattern/some.do :  DispatcherServlet(springmvc框架处理的)
    
    • tomcat处理静态资源的原理:tomcat的web.xml文件有一个servlet 名称是 default , 在服务器启动时创建的。

      • <servlet>
            <servlet-name>default</servlet-name>
            <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
            <init-param>
                <param-name>debug</param-name>
                <param-value>0</param-value>
            </init-param>
            <init-param>
                <param-name>listings</param-name>
                <param-value>false</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>/</url-pattern>  <!--  <url-pattern>为“/”表示静态资源和未映射的请求都这个default处理  -->
        </servlet-mapping>
        
      • default这个servlet作用:处理静态资源、处理未映射到其它servlet的请求。The default servlet for all web applications, that serves static resources. It processes all requests that are not mapped to other servlets with servlet mappings (defined either here or in your own web.xml file).

  • 若DispatcherServlet的<url-pattern>为”/“,此时静态资源与动态资源都会由DispatcherServlet处理,而DispatcherServlet没有处理静态资源的能力。会导致静态资源无法访问,问题解决办法有两种,如下:

    • 第一种处理静态资源的方式:在springmvc配置文件加入 <mvc:default-servlet-handler>

      • 原理是: 加入这个标签后,框架会创建控制器对象DefaultServletHttpRequestHandler(类似我们自己创建的MyController)。DefaultServletHttpRequestHandler这个对象可以把接收的请求转发给 tomcat的default这个servlet。

      • 但是要注意:<mvc:default-servlet-handler> 和 @RequestMapping注解 有冲突,冲突原因是:DispatcherServlet会把所有请求都转发给tomcat的default这个servlet,所以访问失败,解决办法加入注解驱动 <mvc:annotation-driven /> `

        <!--  在springmvc配置文件中加入  -->
        <mvc:annotation-driven />
        <mvc:default-servlet-handler />
        
    • 第二种处理静态资源的方式(常用):在springmvc配置文件加入 <mvc:resources />注解

      • 原理是:加入后框架会创建 ResourceHttpRequestHandler这个处理器对象。让这个对象处理静态资源的访问,不依赖tomcat服务器。

      • 但是要注意: <mvc:resources />和 @RequestMapping注解 有冲突, 需要加入<mvc:annotation-driven /> 解决问题

        <!--
        	mapping:访问静态资源的uri地址, 使用通配符 **
        	location:静态资源在你的项目中的目录位置。
        
        	images/**:表示 images/p1.jpg  , images/user/logo.gif , images/order/history/list.png
        -->
        <mvc:resources mapping="/images/**" location="/images/" />
        <mvc:resources mapping="/html/**" location="/html/" />
        <mvc:resources mapping="/js/**" location="/js/" />
        
        <!--mvc:resources和@RequestMapping有一定的冲突-->
        <mvc:annotation-driven />
        
        <!--
        	使用一个配置语句,指定多种静态资源的访问,这种方式常用,将所有静态资源放到一个目录下,只指定该目录位置
        -->
        <mvc:resources mapping="/static/**" location="/static/" />
        

        注意:只有中央调度器的url-pattern是/才要配置以上标签来解决静态资源无法访问的问题

**<url-pattern>**的取值

​ 使用框架的时候, DispatcherServlet的url-pattern可以使用两种值:

  1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等,不能使用 *.jsp

    http://localhost:8080/myweb/some.do
    http://localhost:8080/myweb/other.do
    
  2. 使用斜杠 “/”:当你的项目中使用了/,它会替代 tomcat中的default。导致所有的静态资源都给DispatcherServlet处理, 默认情况下DispatcherServlet没有处理静态资源的能力。没有控制器对象能处理静态资源的访问。所以静态资源(html,js,图片,css)都是404。动态资源some.do是可以访问,的因为我们程序中有MyController控制器对象,能处理some.do请求。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

路径问题

这里探讨静态资源与动态资源前加与不加“/”的影响

通常前端资源不以”/“开头,后端资源以”/“开头,表示webapp的根

​ 后端资源与配置文件中的路径都要以/开头,表示webapp的根。

​ 这里主要探讨前端资源html与jsp中用 “/” 还是不用。在jsp 、html中使用的地址,都是在前端页面中的地址,都是相对地址

(前端)地址分类

  • 绝对地址:带有协议名称的是绝对地址, http://www.baidu.com , ftp://202.122.23.1

  • 相对地址:没有协议开头的,例如 user/some.do , /user/some.do。相对地址不能独立使用,必须有一个参考地址。 通过参考地址+相对地址本身才能指定资源。

  • 参考地址

    • 在你的页面中的,访问地址不加 “/”

      访问的是: http://localhost:8080/ch06_path/index.jsp;
      路径: http://localhost:8080/ch06_path/
      资源: index.jsp
      
      在index.jsp发起 user/some.do请求,访问地址变为 http://localhost:8080/ch06_path/user/some.do
      当你的地址 没有斜杠开头,例如 user/some.do , 当你点击链接时,访问地址是当前页面的地址加上链接的地址。
      http://localhost:8080/ch06_path/ + user/some.do
      
      
      index.jsp访问 user/some.do(在该资源中转发到index.jsp),返回后现在的地址: http://localhost:8080/ch06_path/user/some.do
      http://localhost:8080/ch06_path/user/some.do
      路径:	  http://localhost:8080/ch06_path/user/
      资源:   some.do
      在index.jsp在 user/some.do ,就变为 http://localhost:8080/ch06_path/user/user/some.do
      
      解决方案:
      1.加入${pageContext.request.contextPath}
      2.加入一个base标签, 是html语言中的标签。 表示当前页面中访问地址的基地址。你的页面中所有 没有“/”开头的地址,都是以base标签中的地址为参考地址使用base中的地址 + user/some.do 组成访问地址
      
    • 在你的页面中的,访问地址加 “/”

      访问的是: http://localhost:8080/ch06_path/index.jsp
      路径: http://localhost:8080/ch06_path/
      资源: index.jsp
      
      点击 /user/some.do, 访问地址变为 http://localhost:8080/user/some.do
      参考地址是你的服务器地址:http://localhost:8080
      
      如果你的资源不能访问: 加入${pageContext.request.contextPath}
      <a href="${pageContext.request.contextPath}/user/some.do">发起user/some.do的get请求</a>
      
  • 总结:jsp中路径前有/,将服务器地址与资源地址拼接;路径前无/,有base去拼接,无base拼接当前访问路径(指除资源外)

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%
        String basePath = request.getScheme() + "://" +
                request.getServerName() + ":" + request.getServerPort() +
                request.getContextPath() + "/";
    %>
    <html>
    <head>
        <title>Title</title>
        <script type="text/javascript" src="static/js/jquery-1.11.1-min.js"></script>
        <base href="<%=basePath%>" />
    </head>
    

    注意:<base>标签只针对当前页面有效,使用base标签会让当前页面所有相对路径失效。

SSM整合开发

整合思路

  • SSM: SpringMVC + Spring + MyBatis.

    • SpringMVC:视图层,界面层,负责接收请求,显示处理结果的。
    • Spring:业务层,管理service,dao,工具类对象的。
    • MyBatis:持久层, 访问数据库的
  • 处理流程:用户发起请求–SpringMVC接收–Spring中的Service对象–MyBatis处理数据

  • SSM整合也叫做SSI (IBatis也就是mybatis的前身), 整合中有容器。

    1. 第一个容器SpringMVC容器, 管理Controller控制器对象的。

    2. 第二个容器Spring容器,管理Service,Dao,工具类对象的我们要做的把使用的对象交给合适的容器创建,管理。 把Controller还有web开发的相关对象交给springmvc容器, 这些web用的对象写在springmvc配置文件中;service,dao对象定义在spring的配置文件中,让spring管理这些对象。

SpringMVC容器与Spring容器关系

  • springmvc容器和spring容器是有关系的,关系已经确定好了
  • springmvc容器是spring容器的子容器, 类似java中的继承。 子可以访问父的内容在子容器中的Controller可以访问父容器中的Service对象, 就可以实现controller使用service对象。因此,在Controller类中直接将要用到的Service对象作为属性,根据@Autowired完成引用赋值。

实现步骤

  1. 使用springdb的mysql库, 表使用student(id auto_increment, name, age)

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 新建maven web项

  3. 加入依赖

    • springmvc,spring,mybatis三个框架的依赖,jackson依赖,mysql驱动,druid连接池,jsp,servlet依赖
  4. 写web.xml

    1. 注册DispatcherServlet。 目的:1.创建springmvc容器对象,才能创建Controller类对象。2.创建的是Servlet,才能接受用户的请求。
    2. 注册spring的监听器:ContextLoaderListener,目的: 创建spring的容器对象,才能创建service,dao等对象。
    3. 注册字符集过滤器,解决post请求乱码的问题
    <!--注册中央调度器DispatcherServlet
            目的:1、DispatcherServlet是Servlet用来接收用户请求,响应数据
                 2、创建SpringMVC容器对象(因为要创建容器对象,因为要给出springmvc配置文件的路径)
        -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:conf/dispatcherServlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
    <!--注册spring的监听器
            目的:在ServletContext对象创建时,创建Spring容器对象(因此,要给出spring配置文件的路径)
        -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:conf/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <!--注册字符集过滤器
            目的:设置请求字符集
        -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
    
  5. 创建包, Controller包, service ,dao,实体类包名创建好

  6. 写springmvc,spring,mybatis的配置文件

    1. springmvc配置文件

      <!--声明组件扫面器
                  原因:SpringMVC的IOC是基于注解的
              -->
      <context:component-scan base-package="某模块中controller层包名"/>
      
      <!--声明视图解析器
                  注意:声明了视图解析器,Controller层的处理器方法中就不能返回资源的完整路径,要是逻辑名称(文件名)
              -->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name="prefix" value="/WEB-INF/jsp/"/>
          <property name="suffix" value=".jsp"/>
      </bean>
      
      <!--声明注解驱动
                  目的:1、响应ajax,返回json
                       2、处理静态资源(只有在DispatcherServlet的url-pattern是“/”是有用,解决冲突)
              -->
      <mvc:annotation-driven/>
      
    2. spring配置文件

      <!--引入jdbc属性配置文件,存放数据库连接信息-->
      <context:property-placeholder location="classpath:conf/jdbc.properties"/>
      
      <!--声明数据源
              目的:连接数据库
          -->
      <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
          <property name="url" value="${jdbc.url}"/>
          <property name="username" value="${jdbc.username}"/>
          <property name="password" value="${jdbc.password}"/>
      </bean>
      
      <!--SqlSessionFactoryBean创建SqlSessionFactory-->
      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
          <property name="dataSource" ref="dataSource"/>
          <property name="configLocation" value="classpath:conf/mybatis.xml"/>
      </bean>
      
      <!--声明mybatis的扫描器,创建dao对象-->
      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
          <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
          <property name="basePackage" value="dao接口所在包名"/>
      </bean>
      
      <!--声明service的注解@Service所在的包名位置-->
      <context:component-scan base-package="service所在包名"/>
      
      <!--事务配置:注解的配置, aspectj的配置-->
      
    3. mybatis主配置文件

      <!--设置别名-->
      <typeAliases>
          <package name="domain类所在包名"/>
      </typeAliases>
      
      <!-- sql mapper(sql映射文件)的位置,是compile后target/classes下的是类路径-->
      <mappers>
          <!--采用包名的方法,指定mapper文件的位置
      		注意:使用package的方式,mapper文件名必须和dao接口名一致。
      	-->
          <package name="mapper文件所在包名"/>
      </mappers>
      
    4. 数据库的属性配置文件

      jdbc.url=jdbc:mysql://localhost:3306/bjpowernode
      jdbc.username=root
      jdbc.password=666
      

    注意:以上配置文件很多都是不变的,以后直接用模板生成,知道改哪里就可以;实际开发中,这些配置文件可能都是由专门的负责人写好的。能看懂就行。在大型项目中,一个模块一套配置文件。

  7. 写代码, dao接口和mapper文件, service和实现类,controller, 实体类。

    • 注意:dao接口代理类对象是service类的属性;service类的对象是controller类的属性。
  8. 写jsp页面

流程:index.jsp–addStudent.jsp—student/addStudent.do( service的方法,调用dao的方法)–result.jsp

总结:

  • 配置文件写一点让项目跑一点
  • ssm的四个配置文件要知道都是干什么的,不需要背,大部分都是模板,只要知道作用,以及改动哪里即可。
  • 实际开发中测试对外接口(处理器方法,即对外提供一个地址,别人可以访问),直接在浏览器访问该资源就可以。直接在浏览器里访问,这样的好处是,不需要从前端页面访问。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Spring核心技术

请求转发和重定向

  • forward:表示转发,相当于request.getRequestDispatcher("xx.jsp").forward(request, response);

    mv.setViewName("forward:/WEB-INF/doOther.jsp");
    
  • redirect:表示重定向,相当于response.sendRedirect("xxx.jsp");

    //http://localhost:8080/ch08_forward_redirect/WEB-INF/doOther.jsp?name=%E7%94%98%E9%9B%A8&age=22---无法在浏览器直接访问WEB-INF
    mv.setViewName("redirect:/WEB-INF/doOther.jsp"); //404,无法找到资源
    mv.setViewName("redirect:/static/images/p1.png"); //可以访问到
    mv.setViewName("redirect:/doRedirect.jsp"); 
    
    /*
    若使用重定向,就不能从request中取数据,要从param中取数据,param是参数集合。以下俩等价
    	age=${param.age}<br/>
        取参数数据age=<%=request.getParameter("age")%><br/>
    */
    
  • forward是一次请求,可以转发到服务器内部资源;redirect是两次请求,先响应到浏览器,浏览器再重发请求,因此redirect无法访问服务器内部资源

image-20220111202512835
  • forward和redirect与视图解析器无关,可以转发到视图解析器外面的资源。
  • forward和redirect他们都可以访问视图文件,比如某个jsp ,html。forward:/hello.jsp forward:/main.html
  • forward和redirect他们都可以访问其它的controller。forward:/some.do , redirect:/other.do
  • 处理器方法可以返回ModelAndView, String , void 都可以使用forward,redirect

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

异常

  • springmvc框架采用的是统一,全局的异常处理。把controller中的所有异常处理都集中到一个地方。 采用的是aop的思想。把业务逻辑和异常处理代码分开。解耦合。

    image-20220112123037317
  • 使用两个注解:@ExceptionHandler、@ControllerAdvice

  • 异常处理步骤:

    1. 新建maven web项目

    2. 加入依赖

    3. 新建一个自定义异常类 MyUserException , 再定义它的子类NameException ,AgeException

      • 异常类写在exception包下,定义自定义异常类MyUserException,集成Exception类,重写无参和带String的有参。子类继承MyUserException同上
    4. 在controller抛出NameException , AgeException

      • 向上抛父类异常MyUserException。
    5. 创建一个普通类,作用全局异常处理类GlobalExceptionHandler

      1. 在类的上面加入@ControllerAdvice
        • 表示控制器增强,SpringMVC处理异常使用aop的思想,将项目中所有异常抛给框架。
      2. 在类中定义方法,方法的上面加入@ExceptionHandler
        • @ExceptionHandler注解的value是发生异常的类,根据不同的异常执行不同的方法。
      /**
       * @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)
       *           位置:在类的上面。
       *  特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。
       *  指定@ControllerAdvice所在的包名
       */
      @ControllerAdvice
      public class GlobalExceptionHandler {
          //定义方法,处理发生的异常
          /*
              处理异常的方法和控制器方法的定义一样, 可以有多个参数,可以有ModelAndView,
              String, void,对象类型的返回值
      
              形参:Exception,表示Controller中抛出的异常对象。
              通过形参可以获取发生的异常信息。
      
              @ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,
              由当前方法处理
           */
      
          @ExceptionHandler(value = NameException.class)
          public ModelAndView doNameException(Exception exception){
              //处理NameException的异常。
              /*
                 异常发生处理逻辑:
                 1.需要把异常记录下来, 记录到数据库,日志文件。
                   记录日志发生的时间,哪个方法发生的,异常错误内容。
                 2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
                 3.给用户友好的提示。
               */
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","姓名必须是zs,其它用户不能访问");
              mv.addObject("ex",exception);
              mv.addObject("time", new Date());
              mv.setViewName("nameError");
              return mv;
          }
      
          //处理AgeException
          @ExceptionHandler(value = AgeException.class)
          public ModelAndView doAgeException(Exception exception){
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","你的年龄不能大于80");
              mv.addObject("ex",exception);
              mv.setViewName("ageError");
              return mv;
          }
      
          //@ExceptionHandler不写value表示处理其它异常, NameException, AgeException以外,不知类型的异常
          @ExceptionHandler
          public ModelAndView doOtherException(Exception exception){
              //处理其它异常
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","你的年龄不能大于80");
              mv.addObject("ex",exception);
              mv.setViewName("defaultError");
              return mv;
          }
      }
      
    6. 创建处理异常的视图页面

    7. 创建springmvc的配置文件

      1. 组件扫描器 ,扫描@Controller注解
      2. 组件扫描器,扫描@ControllerAdvice所在的包名
      3. 声明注解驱动
      <context:component-scan base-package="com.xd.controller"/>
      <context:component-scan base-package="com.xd.handler"/>
      <mvc:annotation-driven/>
      

总结:SpringMVC框架对项目中的异常以aop的思想处理。项目中发生了异常,将异常抛给框架,程序员自己定义一个全局异常处理类GlobalExceptionHandler,该类上用@ControllerAdvice注解。在该类中写异常处理方法,方法上用@ExceptionHandler注解其value是异常的类,用来将异常和方法绑定起来;value也可以没有表示发生的其它异常(相当于else)。这些异常处理方法与处理器方法相似,可以与指定异常绑定(@ExceptionHandler)、可以接参(Exception,接收异常信息)、可以有返回值(ModelAndView、String、Object、void)

拦截器

  • 拦截器是springmvc中的一种,需要实现HandlerInterceptor接口。

  • 拦截器和过滤器类似,功能方向侧重点不同。 过滤器是用来过滤器请求参数,设置编码字符集等工作;拦截器是拦截用户的请求,做请求做判断处理的。

  • 拦截器是全局的,可以对多个Controller做拦截。

  • 一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。拦截器常用在:用户登录处理,权限检查, 记录日志。

  • 拦截器使用步骤:

    1. 新建maven web项目

    2. 加入依赖

    3. 创建Controller类

    4. 创建一个普通类,作为拦截器使用

      1. 实现HandlerInterceptor接口
      2. 实现接口中的三个方法(注意:Spring5.2.5后,HandlerInterceptor接口中的三个方法改为了default方法,用到那个重写那个)
    5. 创建show.jsp

    6. 创建springmvc的配置文件

      1. 组件扫描器 ,扫描@Controller注解
      2. 声明拦截器,并指定拦截的请求uri地址
      <!--声明拦截器: 拦截器可以有0或多个-->
      <mvc:interceptors>
          <!--声明第一个拦截器-->
          <mvc:interceptor>
              <!--指定拦截的请求uri地址
                      path:就是uri地址,可以使用通配符 **
                            ** : 表示任意的字符,文件或者多级目录和目录中的文件
                      http://localhost:8080/myweb/user/listUser.do
                      http://localhost:8080/myweb/student/addStudent.do
                  -->
              <mvc:mapping path="/**"/>
              <!--声明拦截器对象-->
              <bean class="com.xd.handler.MyInterceptor"/>
          </mvc:interceptor>
      </mvc:interceptors>
      
  • 拦截器的执行时间:

    1. 在请求处理之前, 也就是controller类中的方法执行之前先被拦截。
    2. 在控制器方法执行之后也会执行拦截器。
    3. 在请求处理完成后也会执行拦截器。
  • 拦截器:看做是多个Controller中公用的功能,集中到拦截器统一处理。使用的aop的思想

image-20220111214330629

​ 解读:拦截器是整个项目的门户,拦截器的preHandler()方法如果返回true执行Controller类中的处理器方法;若返回false,请求到此结束。可以跳转到响应jsp页面,用于提示用户。

拦截器的三个方法

  • HandlerInterceptor接口中的三个方法:preHandler()、postHandler()、afterCompletion()

  • preHandler():预处理方法。是整个项目的门户,拦截器主要写该方法,用于身份验证、权限验证。

    /*
         * preHandle叫做预处理方法。
         *   重要:是整个项目的入口,门户。 当preHandle返回true 请求可以被处理。
         *        preHandle返回false,请求到此方法就截止。
         *
         * 参数:
         *  Object handler : 被拦截的控制器对象
         * 返回值boolean
         *   true:请求是通过了拦截器的验证,可以执行处理器方法。
             *   拦截器的MyInterceptor的preHandle()
                 =====执行MyController中的doSome方法=====
                 拦截器的MyInterceptor的postHandle()
                 拦截器的MyInterceptor的afterCompletion()
             *
         *   false:请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理
         *      拦截器的MyInterceptor的preHandle()
         *
         *  特点:
         *   1.方法在控制器方法(MyController的doSome)之前先执行的。用户的请求首先到达此方法
         *   2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
         *      如果验证失败,可以截断请求,请求不能被处理。
         *      如果验证成功,可以放行请求,此时控制器方法才能执行。
         */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        System.out.println("拦截器MyInterceptor的preHandle()方法执行");
    
        //计算的业务逻辑,根据计算结果,返回true或者false。例如身份验证成功返回true,否则为false
        return true;
    }
    

    注意:preHandler()方法返回true才会执行Controller类中的处理器方法,否则请求到此终止。该方法常用作身份验证,是整个项目的门户。

  • postHandler():后处理方法。在处理器方法执行后执行,并且能够接收处理器方法的返回值并对其修改

    /*
           postHandle:后处理方法。
           参数:
            Object handler:被拦截的处理器对象MyController
            ModelAndView mv:处理器方法的返回值
    
            特点:
             1.在处理器方法之后执行的(MyController.doSome())
             2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的数据和视图,可以影响到最后的执行结果。
             3.主要是对原来的执行结果做二次修正,
    
             ModelAndView mv = MyController.doSome();
             postHandle(request,response,handler,mv);
         */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mv)
        throws Exception {
        System.out.println("拦截器MyInterceptor的postHandle()方法执行");
        mv.setViewName("forward:/interceptor.jsp");
    }
    
  • afterCompletion:最后执行的方法。常在该方法中释放资源

    /*
          afterCompletion:最后执行的方法
          参数
            Object handler:被拦截器的处理器对象
            Exception ex:程序中发生的异常
          特点:
           1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
           2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
         */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){
        System.out.println("拦截器MyInterceptor的afterCompletion()方法执行");
    }
    

    注意:可以在该方法中计算一次请求所需时间。用System.currentTimeMillis();

有了拦截器后,一次请求的执行流程

​ 执行过程是:在容器创建时,扫描springmvc配置文件,扫面到interceptor标签,知道有拦截器,发现拦截器的mapping是/**,即拦截所有请求,然后容器创建拦截器对象。之后前端每发一次请求,先执行该拦截器对象的preHandler()方法若该方法返回true,由它将请求转发给Controller类,执行处理器方法,执行完成后,执行拦截器的postHandler()方法,在该方法中可以修改处理器方法的结果,下来再执行afterCompletion()方法,响应结果。若preHandler()方法返回false,请求到此结束。可以由preHandler()响应一个视图到失败页面

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多个拦截器

​ 项目中可以存在0个或多个拦截器。这里研究存在多个拦截器时一次请求执行的过程。执行结果如下:

//MyInterceptor与MyInterceptor2的preHandle()都返回true
11111拦截器MyInterceptor的preHandle()方法执行11111
22222拦截器MyInterceptor2的preHandle()方法执行22222
MyController类的doSome方法执行
22222拦截器MyInterceptor2的postHandle()方法执行22222
11111拦截器MyInterceptor的postHandle()方法执行1111
22222拦截器MyInterceptor2的afterCompletion()方法执行22222
11111拦截器MyInterceptor的afterCompletion()方法执行11111

//MyInterceptor的preHandle()返回ture、MyInterceptor2的preHandle()返回false
11111拦截器MyInterceptor的preHandle()方法执行11111
22222拦截器MyInterceptor2的preHandle()方法执行22222
11111拦截器MyInterceptor的afterCompletion()方法执行11111

//MyInterceptor的preHandle()返回false、MyInterceptor2的preHandle()返回false/true
11111拦截器MyInterceptor的preHandle()方法执行11111

结论:

  • 项目中存在多个拦截器时,拦截器的执行顺序与其在springmvc配置文件中声明的先后顺序一致。

  • 多个拦截器只要有一个preHandler为false就不会执行处理器方法。

  • 由此可以看出,一个项目由多个拦截器时,它是以回字处理请求的。

image-20220111215711820
  • 处理流程:
image-20220112171710817

注意:处理器执行链HandlerExecutionChain这个对象,如果拦截器的preHandler()返回false会断链。

为什么需要多级拦截器

​ 大型项目中,分别由不同的功能的拦截器。如用户身份验证的拦截器、删除记录的权限验证拦截器、访问某个资源的拦截器。这样方便管理项目

image-20220111220231448

总结:

  • 拦截器就是aop的思想,可以拦截多个Controller类,将其抽取出去。
  • 拦截器的使用就两步:
    1. 写拦截器类,实现HandlerInterceptor接口,重写其三个方法。注意:在spring4这三个方法必须重写,在spring5.2.5版本中,这三个方法时default的,需要用那个重写那个就可以。
    2. 在springmvc配置文件中声明拦截器对象,以及声明拦截那个资源。
  • 注意多拦截器时的处理顺序。有一个拦截器的preHandler返回false,Controller方法不会执行。
  • 拦截器和过滤器的区别:
    1. 过滤器是servlet中的对象, 拦截器是框架中的对象
    2. 过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
    3. 过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的。拦截器是用来验证请求的,能截断请求。
    4. 过滤器是在拦截器之前先执行的。
    5. 过滤器是tomcat服务器创建的对象 拦截器是springmvc容器中创建的对象
    6. 过滤器是一个执行时间点。拦截器有三个执行时间点
    7. 过滤器可以处理jsp,js,html等。拦截器是侧重拦截对Controller的对象。 如果你的请求不能被DispatcherServlet接收, 这个请求不会执行拦截器内容

拦截器实例:权限验证

  • 为方便,在登录页面login.jsp中直接给session域中保存username和password,表示登录成功。登出也是在logout.jsp将session中的username和password给remove掉

    login.jsp
    <%
        request.getSession().setAttribute("username", "甘雨");
        request.getSession().setAttribute("password", "123");
    %>
    <a href="user/doSome.do">进入用户系统</a>
    
    logout.jsp
    <%
        request.getSession().removeAttribute("username");
        request.getSession().removeAttribute("password");
    %>
    用户登出成功,点击此<a href="login.jsp">重新登录</a>
    
  • 详细看springmvc-study项目的ch12-interceptor-premission项目

总结:SpringMVC的应用:异常、拦截器都是用aop思想。即新建异常类GlobalExceptionHandler,拦截器类MyInterceptor。将异常处理方法,拦截方法(切面)写在该类中。在springmvc配置文件中声明异常类的组件扫描器以及拦截器。

SpringMVC的执行流程

流程分析

分析一次请求的执行过程。从源码分析,主要有DispatcherServlet的doDispatcher()方法、HandlerMapping、HandlerAdapter类

  1. 用户发送请求doSome.do

  2. 中央调度器DispatcherServlet接收请求some.do,把请求转交给处理器映射器

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    	HandlerExecutionChain mappedHandler = null;
    	mappedHandler = this.getHandler(processedRequest); //getHandler方法底层掉的是HandlerMapping实现类的方法,获取处理器执行链
    }
    
    • 处理器映射器:springmvc中的一种对象,框架把实现了HandlerMapping接口的类都叫做映射器(多个)。

      image-20220112200140121 image-20220112194654952
    • 处理器映射器的作用:根据请求,从springmvc容器对象中获取处理器对象(MyController controller = ac.getBean(“some”)),框架把找到的处理器对象放到一个叫做处理器执行链(HandlerExecutionChain)的类中保存。

      • HandlerExecutionChain:该类保存着 处理器对象MyController、项目中所有的拦截器List<HandlerInterceptor>

        image-20220112195255429
  3. DispatcherServlet把2中的HandlerExecutionChain中的处理器对象交给了处理器适配器对象(多个)

    ModelAndView mv = null;
    //mappedHandler.getHandler()表示从处理器执行链获取处理器对象
    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); 
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    • 处理器适配器:springmvc框架中的对象,需要实现HandlerAdapter接口。

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 处理器适配器的作用:执行处理器方法(调用MyController.doSome() 得到返回值ModelAndView)

  4. Dispatcherservlet把3中获取的ModelAndView交给了视图解析器对象。

    • 视图解析器:springmvc中的对象,需要实现viewResoler接口(可以有多个)

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 视图解析器作用:组成视图完整路径,使用前缀,后缀。并创建view对象。

      • view是一个接口,表示视图的,在框架中jsp,html不是string表示,而是使用view和他的实现类表示视图。
    • InternalResourceView:视图类,表示jsp文件,视图解析器会创建InternalResourceView类对象。

      • 这个对象的里面,有一个属性url赋值为:/WEB-INF/view/show.jsp
  5. DispatcherServlet把4步骤中创建的view对象获取到,调用view类自己的方法render(),把Model数据放入到request作用域。执行对象视图的forward。请求结束

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论:

  • 早期没有注解之前,需要实现各种不同的接口才能创建Controller类。因此,对应有不同的映射器(HandlerMapping)和适配器(HandlerAdapter)
  • 只要知道:适配器是执行处理器方法;映射器是找对象的(从处理器执行链中获取w),找Controller类对象。不用了解代码
  • 框架里面使用的是视图类View来表示jsp、html等页面
  • 框架内部以中央调度器为核心,这样的好处是方便框架的升级。例如,改动映射器,其它器不需要改动。
  • 中央调度器调这些器都是通过方法调用实现的。
  • 中央调度器核心方法:doDispatcher() 程序中所有请求都是在这里完成的。

流程图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/209620.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SIP协议在语音通信的应用方式及OKCC系统中的配置方法

在企业语音通信的过程中&#xff0c;SIP协议支持的网络通信技术通过网络为用户提供了无数的通信便利&#xff0c;已成为企业不可或缺的重要通信技术。由于SIP协议是语音通信帮助企业实现这些优势的原因&#xff0c;因此了解支持这些呼叫的SIP协议的上下文至关重要。 什么是SI…

服务器和Linux ,安装R rstudio ,常用软件

服务器的基本概念&#xff1a; 如服务器的基本结构&#xff0c;节点&#xff0c;端口的概念等。 服务器的基本设置和管理&#xff1a; 如何配置新服务器&#xff0c; 如何管理服务器&#xff0c;如何分配账户并确保他们互不访问&#xff0c; 如何全局安装软件让所有人都可以…

【数据结构】 堆排序与TopK问题详解

在学习完堆的创建后&#xff0c;就轮到了标题的两个问题 这两个问题在实际生活中会有比较强的实际问题解决能力 先分别解释一下 堆排序&#xff1a; 运用堆的思想进行排序&#xff0c;时间复杂度为O(NlogN)TopK&#xff1a; 从一大堆数据中选择K个最大或最小的数据&#xff0c…

如何本地搭建个人hMailServer邮件服务并实现远程发送邮件

文章目录 前言1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 前言 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpola…

了解c语言中的结构体

介绍&#xff1a; 在C语言中&#xff0c;结构体是一种用户自定义的数据类型&#xff0c;它允许我们将不同类型的数据组合在一起&#xff0c;形成一个更为复杂的数据结构。结构体可以用来表示现实世界中的实体&#xff0c;如人员、学生、图书等。本篇博客将介绍结构体的基本概念…

Postgresql分区表

PostgreSQL 提供了三种分区表实现方式&#xff1a; range &#xff1a;范围分区list &#xff1a;列表分区hash &#xff1a;哈希分区 一、范围分区 根据某个字段的值&#xff0c;将数据存入不同的分区表中。 创建父表 create table test_person_table (name varchar(64),ag…

SpringSecurity工作原理

实现功能就是继承这几个对应功能的类。 大概工作流程 Spring Security 的过滤器&#xff08;Filters&#xff09;和拦截器&#xff08;Interceptors&#xff09;是 Spring Security 框架中用于保护 web 应用安全的重要组件。它们在处理 HTTP 请求时扮演不同的角色&#xff0c…

【动态规划】LeetCode-62.不同路径

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

vue 中 mixin 和 mixins 区别

目录 前言 用法 全局Mixin 局部Mixin 代码 理解 高质量的Mixin使用 在Vue.js框架中&#xff0c;Mixin是一种非常重要和强大的功能&#xff0c;它允许开发者创建可复用的代码片段&#xff0c;并将其应用到一个或多个组件中。Vue提供了两种方式来使用Mixin&#xff0c;分别…

以太网PHY,MAC接口

本文主要介绍以太网的 MAC 和 PHY&#xff0c;以及之间的 MII&#xff08;Media Independent Interface &#xff0c;媒体独立接口&#xff09;和 MII 的各种衍生版本——GMII、SGMII、RMII、RGMII等。 简介 从硬件的角度看&#xff0c;以太网接口电路主要由MAC&#xff08;M…

OpenTelemetry系列 - 第4篇 OpenTelemetry K8S生态

目录 一、【Helm】添加OTel Helm repo二、【Helm Chart】OTel Collector2.1 daemonset2.2 deloyment 三、【K8S Operator】OTel Operator3.1 安装OTel Operator3.2 部署OpenTelemetryCollector3.2.1 Deloyment Mode3.2.2 DeamonSet Mode3.2.3 StatefulSetMode3.2.4 Sidecar Mod…

Matlab R2022b 安装成功小记

Matlab R2022b 安装成功小记 前言一、 下载链接二、 安装过程小记 叮嘟&#xff01;这里是小啊呜的学习课程资料整理。好记性不如烂笔头&#xff0c;今天也是努力进步的一天。一起加油进阶吧&#xff01; 前言 windows 10系统之前安装过Matlab R2010b做基础研究&#xff0c;最…

【高效开发工具系列】Hutool Http工具类

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

基于Spring Cloud智慧工地可视化管理平台源码

智慧工地是聚焦工程施工现场&#xff0c;紧紧围绕人、机、料、法、环等关键要素&#xff0c;综合运用物联网、云计算、大数据、移动计算和智能设备等软硬件信息技术&#xff0c;与施工生产过程相融合。 一、什么是智慧工地 智慧工地是指利用移动互联、物联网、智能算法、地理…

【Linux】awk 使用

awk 输出 // 打印所有列 $ awk {print $0} file // 打印第一列 $ awk {print $1} file // 打印第一和第三列 $ awk {print $1, $3} file // 打印第三列和第一列&#xff0c;注意先后顺序 $ cat file | awk {print $3, $1} …

DDPM代码详解

最近准备要学习一下AIGC&#xff0c;因此需要从一些基本网络开始了解&#xff0c;比如DDPM&#xff0c;本篇文章会从代码解析角度来供大家学习了解。DDPM(Denoising Diffusion Probabilistic Models) 是一种扩散模型。 扩散模型包含两个主要的过程&#xff1a;加噪过程和去噪过…

C语言--每日选择题--Day32

如果大家对读研究生和就业不知道如何抉择&#xff0c;我的建议是看大家的经济基础&#xff0c;如果家里不是很需要你们工作&#xff0c;就读研提升自己的学历&#xff0c;反之就就业&#xff1b;毕竟经济基础决定上层建筑&#xff1b; 第一题 1. 下面代码的结果是&#xff1a;…

牛客小白月赛82(A~C)

目录 A.谜题&#xff1a;质数 输入描述 输出描述 输入 输出 解析 B.Kevin逛超市 2 (简单版本) 输入描述 输出描述 输入 输出 思路 C.被遗忘的书籍 题目描述 输入描述 输出描述 输入 输出 输入 输出 思路 比赛链接 牛客小白月赛82_ACM/NOI/CSP/CCPC/ICPC算…

C#Backgroundworker与Thread的区别

前言 当谈到多线程编程时&#xff0c;C#中的BackgroundWorker和Thread是两个常见的选择。它们都可以用于实现并行处理和异步操作&#xff0c;但在某些方面有一些重要的区别。本文将详细解释BackgroundWorker和Thread之间的区别以及它们在不同场景中的使用。 目录 前言1. Backgr…

微软 Power Platform 零基础 Power Pages 网页搭建教程学习实践进阶以及常见问题解答(二)

微软 Power Platform 零基础 Power Pages 网页搭建教程学习实践进阶及常见问题解答&#xff08;二&#xff09; Power Pages 学习实践进阶 微软 Power Platform 零基础 Power Pages 网页搭建教程学习实践进阶及常见问题解答&#xff08;二&#xff09;Power Pages 核心工具和组…