zoukankan      html  css  js  c++  java
  • spring mvc及父子容器问题

    在web.xml文件中配置DispatcherServlet前端控制器,默认会读取/WEB-INF/${servlet-name}-servlet.xml文件,我们也可以通过contextConfigLocation属性(DispatcherServlet父类FrameworkServlet的一个属性)来显式指定配置文件的路径,如配置成classpath:webmvc.xml,这将会读取根路径下的webmvc.xml文件,我们需要在resources目录中创建一个webmvc文件。

    也可以配置此Servlet的<load-on-startup />,从而让tomcat一启动就创建DispatcherServlet实例(非必须,可选),配置如下:

    <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:webmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    我们在spring mvc配置文件需要配置三大组件:HandlerMapping处理器映射器、HandlerAdapter处理器适配器、ViewResolver视图解析器,配置示例如下:

    <context:component-scan base-package="com.kou.controller"></context:component-scan>
        
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
    
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
            
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    在上面的配置文件中,配置了RequestMappingHandlerMapping处理器映射器、RequestMappingHandlerAdapter处理器适配器、InternalResourceViewResolver视图解析器,还用<context:component-scan />元素指定了自动扫描的包。

    如果要处理静态资源的请求,则需要配置

    <mvc:default-servlet-handler />

    这将启用spring-mvc-xxx.jar包中的DefaultServletHttpRequestHandler处理器,它会根据当前服务器环境去找对应的默认的servlet来处理静态资源的请求。

    <mvc:annotation-driven />用法:

    这个元素可以自动注册RequestMappingHandlerMappingRequestMappingHandlerAdapter,也就是说,如果在spring mvc配置文件中配置了<mvc:annotation-driven />,就无需在<bean />元素中配置这两个组件了。

    此外,在spring mvc配置文件中配置了<mvc:annotation-driven />的话,就可以支持以下注解的使用了:

    1、@controller、@RestController

    标注某个类,表示该类是Handler处理器类。其中,@RestController=@Controller + @ResponseBody。

    2、@ResponseBody

    用来修饰处理器类或是处理器方法,需要HttpMessageConverter接口来对处理器方法的返回值进行转换,处理器方法返回值类型不同,使用到的具体实现类也不一样,这也是策略模式了。

    如果处理器方法返回值是String类型,则要用StringHttpMessageConverter实现类,该类默认的charSet是ISO-8859-1,需要配置其supportedMediaTypes属性值为text/html;charset=UTF-8,这样才能转为UTF-8编码。

    如果处理器方法返回的是集合类型或者实体类类型,则具体使用哪个实现类要看应用中引用的处理json的jar包是什么。如果没有引用任何处理json的jar包,这个时候会报错的No converter found for return value of type: class java.util.ArrayList。如果引用了Jackson包,则会用MappingJackson2HttpMessageConverter实现类,该类默认的charSet是UTF-8,最终将向前端返回json。

    3、@RequestMapping

    既可以标注Handler处理器类中的方法,也可以标注Handler处理器类。标注Handler处理器类的话,该类中的所有方法都会用到在@RequestMapping中配置的属性。@RequestMapping有很多属性很常用:

    method = {RequestMethod.POST}表示该方法只能匹配POST请求,默认可以匹配任意方式的请求。

    value = {"/test1"}表示该方法匹配请求路径是/test1的请求,value属性必配。如果此时同时用@RequestMapping来标注Handler处理器类的话,假如配置value属性为value = "/delete",则配置的方法就匹配路为/delete/test1的请求。

    value属性支持Ant风格的url配置,?匹配任意一个字符,*匹配任意个字符,**匹配任意层路径,如/delete/**/test1可以匹配/delete/aaa/test1、/delete/aaa/bbb/test1。

    params = {"userName=kou", "age"}表示该方法匹配的请求的请求参数必须包含userName和age,且userName参数的值必须为kou。

    4、@RequestBody

    用于修饰处理器方法形参,解析请求体参数并赋给方法形参,用于contentType值为application/json且请求参数是json字符串的post请求,需要配合HttpMessageConverter使用。

    5、@RequestParam

    修饰处理器方法的形参,会先得到前端请求参数的值然后再赋给指定的形参:

    @Controller
    @RequestMapping(value = "/hello")
    public class HelloController {
        @Resource
        private HelloService helloService;
    
        @RequestMapping(value = { "/dd" })
        public void getAll(HttpServletRequest request, HttpServletResponse response,
                @RequestParam(value = "username") String userName) {
            System.out.println(userName);
        }
    }

    如果请求url是/hello/dd,则会匹配到getAll()方法,@RequestParam会先执行request.getParameter("username")得到username参数的值,然后赋值给形参userName,总的来说就相当于执行userName=request.getParameter("username"),此时我们就可以在方法体中获得userName的值。如果username对应的参数值为null,则userName也会为null。如果设置了@RequestParam的话,默认要求请求必须有username参数,如果我们要求请求url不带这个参数的话,只需设置@RequestParam的required属性值为false。

    值得注意的是,@RequestParam不仅可以从get请求、一般post请求中获取请求参数的值,也可以从multipart/form-data的post请求中得到请求参数的值。

    6、@PathVariable

    修饰处理器方法的形参,将请求url中占位符的值绑定到形参中,用例:

    @Controller
    @RequestMapping(value = "/hello")
    public class HelloController {
        @Resource
        private HelloService helloService;
    
        @RequestMapping(value = { "/dd/{Id}" })
        public void getAll(HttpServletRequest request, HttpServletResponse response,
                @PathVariable(value = "Id") String id) {
            System.out.println(id);
        }
    }

    如果请求的url是/hello/dd/10,则此时将匹配到getAll()方法,@PathVariable从url中截取出ID对应的字符串,并赋值给参数id。@PathVariable的value属性的值必须要和RequestMapping的value属性的花括号的值保持一致。

    7、@RequestHeader

    获取请求报头中的指定的属性值并绑定到指定形参中。

    8、@CookieValue

    获取请求中指定的Cookie值并绑定到指定形参中。

    spring mvc中请求的处理流程是什么?这个在面试中遇见过不止一次,有必要整清楚。

    1、用户发送请求到前端控制器DispatcherServlet

    2、DispatcherServlet根据处理器映射器HandlerMapping找到处理器

    3、DispatcherServlet根据处理器找到处理器适配器HandlerAdapter

    4、通过处理器适配器执行处理器方法得到ModelAndView。在执行处理器方法之前先按拦截器的顺序执行其preHandle方法,在执行处理器方法之后按照拦截器的逆序,执行其postHandle方法。

    5、视图解析器解析ModelAndView返回具体View

    6、DispatcherServlet渲染View并返回给用户

    父子容器问题

    首先需要指明的是,父容器是spring容器,Root WebApplicationContext,又称为根容器,子容器是DispatcherServlet初始化时创建的容器,WebApplicationContext for namespace ${servlet-name}-servlet。子容器可以访问父容器中的bean,父容器不能访问子容器中的bean。例如,Controller实例能访问Service实例,但是Service实例却不能访问Controller实例。

    父容器创建:

    在web.xml中配置了ContextLoaderListener,ContextLoaderListener实现了ServletContext接口,在Servlet容器启动后,调用监听器的初始化方法,方法内部调用了ContextLoader的initWebApplicationContext()方法。initWebApplicationContext()方法先是调用createWebApplicationContext()方法创建一个容器,设置容器的父容器为null,然后调用configureAndRefreshWebApplicationContext()方法,configureAndRefreshWebApplicationContext()方法会给容器设置id,设置配置文件位置,然后调用AbstractApplicationContext的refresh()方法,实例化各bean。

        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
    
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                }
            }
        }

    子容器创建:

    DispatcherServlet在实例化之后,紧接着就初始化。DispatcherServlet继承了FrameworkServlet,FrameworkServlet又继承了HttpServletBean,HttpServletBean又继承了HttpServlet,HttpServlet又继承了GenericServlet,GenericServlet实现了Servlet接口。HttpServletBean重写了源自Servlet接口的init()方法,内部调用initServletBean()方法。FrameworkServlet重写了initServletBean()方法,内部调用initWebApplicationContext()方法,initWebApplicationContext()方法内部调用createWebApplicationContext()方法创建一个新的容器,并设置父容器为根容器,再调用configureAndRefreshWebApplicationContext()方法设置id、配置文件位置和实例化各bean。

    获取bean的操作最终会调用AbstractBeanFactory的doGetBean()方法,从中可以看到如果从当前容器中获取不到bean,就会去父容器中获取。

    在代码中有以下获取容器的方法:可以在上面的initWebApplicationContext()中找到

    WebApplicationContextUtils工具类的getWebApplicationContext(ServletContext sc)静态方法,可获取根容器。

    RequestUtils工具类的findWebApplicationContext(HttpServletRequest request)静态方法,可获取子容器。

    ContextLoader的getCurrentWebApplicationContext()静态方法,可获取根容器。

  • 相关阅读:
    2019-1-7 水晶报表
    2018-12-25工作记录 空白行===水晶报表
    2018-7-26-随笔-泛型
    2018-7-20-随笔-转换
    2018-7-18-随笔-接口
    2018-7-17-随笔-params和ref、out用法、事件访问器
    VPS安装metasploit-framework
    Mimiktaz抓取本机密码
    msfvenom生成各类Payload命令
    docker容器开启ssh远程登录
  • 原文地址:https://www.cnblogs.com/koushr/p/5873369.html
Copyright © 2011-2022 走看看