传统servlet在spring中的应用

一、Servlet
servlet用于生成动态内容。webflux不是基于servlet容器。
静态内容由nginx生成,通过io的方式去读取文件。
http(request,response):是一个单路的协议。一次请求完后就结束了。而且服务端是被动的一方。其实我们可以把http请求看作是一个消息,一个消息会有两部分东西,一个是消息的content(body/payload),一个是消息的header,包含(destination)。
什么是双路协议?
比如长连接,比如既有客户端向服务端发送请求,也有服务端向客户端推送响应。比如websocket协议,以及http2。

请求头或者请求体就是一个输入,servlet APi负责解析这些内容(一般由servlet容器完成)。

契约(API):一种编程模式
编程模型:按照一定规范去操作API
大家都实现javax.servlet api,至于你是什么容器,不care
servlet容器有:tomcat,jetty,undertow。
servlet容器就是提供servlet的运行时环境。
什么是运行时环境?
请求从我们的客户端到服务端,服务端会解析一些客户端的输入,如请求url,请求体,请求头等,正是有了运行时环境,我不需要自己去解析这些请求头啊请求体啊等等,servlet容器会帮我解析这些,我只要直接取出来用就好了。

MIME类型就是字描述消息。
http是文本协议,文本协议就意味着有格式,格式就会有很多种方式,
比如,html是一种格式,xml是一种格式,json是一种格式,
无论是哪种格式,我需要有对应的视图去进行渲染。

servlet规范是属于J2ee的一部分,
Wikipedia对servlet:
(1)Pluggability :表示servlet可以通过java API以编程的方式进行配置,组装,而不是仅仅是原来的web.xml配置方式。
(2)Ease of development :更加容易部署,为什么?可以通过jar包的形式进行部署,而不需要利用web.xml的方式。
(3)Async Servlet: 异步
(4)Security:java security + servlet security(认证+授权)
(5)NIO:注意non blocking不代表异步,同步异步和阻塞非阻塞是两个维度的概念。
(6)Http protocol upgrade mechanism(websocket): http协议升级,比如客户端发出请求,想把http协议升级,升级后从http编程socket,需要浏览器支持。

Reactive streams APi规范,通过一套统一的API实现非阻塞编程。
Reactive streams是流式编程:process1->process2->process3
前面的计算和后面的计算必须有强关联,process1的结果result1,会作为process2的输入,以此类推。它不关心你是同步还是异步。

二、Servlet组件


image.png

servlet组件:servlet,filter,listener
filter的执行顺序:部署描述符里面定义的order
The order the container uses in building the chain of filters to be applied for a
particular request URI is as follows:

  1. First, the matching filter mappings in the same order that these
    elements appear in the deployment descriptor.
  2. Next, the matching filter mappings in the same order that these
    elements appear in the deployment descriptor

jsp九大对象:
ApplicationContext,sessionContext,requestContext,pageContext,

JSP是WEB渲染视图框架中性能最好的。为什么?
因为JSP会编译成java class,
运行时解释还是编译时解释?
静态语言是编译时解释,动态语言是运行时解释。

三、Listener ->Application LifeCycle Event

四、spring运用


image.png

1, RootWebApplicationContext
2,ServletWebApplicationContext
RootWebApplicationContext is the parent of ServletWebApplicationContext.
RootWebApplicationContext是读不到ServletWebApplicationContext的bean的。子类是可以获取得到父类所管理的bean。
所以ServletWebApplicationContext中的bean是可以读到RootWebApplicationContext中的bean,但是,RootWebApplicationContext中的bean是读不到ServletWebApplicationContext中的bean的。原因是双亲委派模型。
所以我们从来不会在Service中引用被标记为Controller的对象,而只在Controller中引用被标记为Service的对象。

可以试试看在Service中引用Controller.



    
        org.springframework.web.context.ContextLoaderListener
    

    
        contextConfigLocation
        /WEB-INF/app-context.xml
    

    
        app
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            
        
        1
    

    
        app
        /app/*
    


web.xml中
1,首先会定义一个ContextLoaderListener
ContextLoaderListener是ServletContextListener的实现类,负责初始化ServletContext(也就是ApplicationContext)。
ServletContext是servlet规范中的概念,
ApplicationContext是Spring中的概念。

    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }

在ServletContext(container)启动的时候,会调用父类ContextLoader的initWebApplicationContext().
实例化初始化一个RootWebApplicationContext,然后在instantiate a ServletWebApplicationContext,注意这个时候DispatcherServlet还没有被初始化。servlet是在container启动之后才会进行加载和初始化的。

2,然后再定义一个DispatcherServlet
为什么要这样?

3,servletContext,HttpSession,ServletRequest的那几个Listener,其实关系到他们的生命周期。ServletContextListener的任务就是初始化ServletContext,所以它的实现必须在ServletContext初始化之前。
**contextConfigLocation:config location for the root context

4,打包。嵌入式的包,其实就是把war包打成Jar包,然后以java jar的形式运行。
打包命令:mvc clean package
pom.xml中配置通过maven打一个嵌入式的jar包:


        
            
                org.apache.tomcat.maven
                tomcat7-maven-plugin
                2.1
                
                    
                        tomcat-run
                        
                            exec-war-only
                        
                        package
                        
                            /
                        
                    
                
            
        
    

打完后,在target目录下会出现一个servlet-in-spring-1.0-SNAPSHOT-war-exec.jar和
servlet-in-spring-1.0-SNAPSHOT.war,你会发现jar比war大很多,为什么?因为jar包中包含了嵌入式的tomcat。
查看target目录:

27608 -rw-r--r--   1 xixi  staff  14128471 Apr 24 15:47 servlet-in-spring-1.0-SNAPSHOT-war-exec.jar
10304 -rw-r--r--   1 xixi  staff   5275316 Apr 24 15:46 servlet-in-spring-1.0-SNAPSHOT.war

5,增加app-context.xml,注意在/webapp/META-INF/app-context.xml





6,运行java -jar servlet-in-spring-1.0-SNAPSHOT-war-exec.jar

四月 24, 2019 5:18:40 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-bio-8080"]
四月 24, 2019 5:18:40 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service Tomcat
四月 24, 2019 5:18:40 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/7.0.37
四月 24, 2019 5:18:42 下午 org.apache.catalina.core.ApplicationContext log
信息: No Spring WebApplicationInitializer types detected on classpath
四月 24, 2019 5:18:42 下午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring root WebApplicationContext
四月 24, 2019 5:18:42 下午 org.springframework.web.context.ContextLoader initWebApplicationContext
信息: Root WebApplicationContext: initialization started
四月 24, 2019 5:18:42 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing Root WebApplicationContext: startup date [Wed Apr 24 17:18:42 CST 2019]; root of context hierarchy
四月 24, 2019 5:18:42 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from ServletContext resource [/WEB-INF/app-context.xml]
四月 24, 2019 5:18:45 下午 org.springframework.web.context.ContextLoader initWebApplicationContext
信息: Root WebApplicationContext: initialization completed in 2865 ms
四月 24, 2019 5:18:45 下午 org.apache.catalina.core.ApplicationContext log
信息: Initializing Spring FrameworkServlet 'app'
四月 24, 2019 5:18:45 下午 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'app': initialization started
四月 24, 2019 5:18:45 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing WebApplicationContext for namespace 'app-servlet': startup date [Wed Apr 24 17:18:45 CST 2019]; parent: Root WebApplicationContext
四月 24, 2019 5:18:45 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache
信息: Looking for @ControllerAdvice: WebApplicationContext for namespace 'app-servlet': startup date [Wed Apr 24 17:18:45 CST 2019]; parent: Root WebApplicationContext
四月 24, 2019 5:18:46 下午 org.springframework.web.servlet.FrameworkServlet initServletBean
信息: FrameworkServlet 'app': initialization completed in 257 ms
四月 24, 2019 5:18:46 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-bio-8080"]

从日志中,我们可以发现启动顺序:
1,Starting Servlet Engine: Apache Tomcat/7.0.37
2,No Spring WebApplicationInitializer types detected on classpath
3, Initializing Spring root WebApplicationContext:
contextConfigLocation:

contextConfigLocation
/WEB-INF/app-context.xml

先创建一个RootServletContext,然后configure这个RootServletContext,配置的过程中会通过getInitParameter("contextConfigLocation")就可以获取到WEB-INF/app-context.xml,然后在loadBeanDefinitions的时候就可以读取app-context.xml中的内容。

4, Root WebApplicationContext: initialization started
5, Refreshing Root WebApplicationContext
6,Loading XML bean definitions from ServletContext resource [/WEB-INF/app-context.xml]
7, Root WebApplicationContext: initialization completed in 2865 ms
8, Initializing Spring FrameworkServlet 'app',其实就是DispatcherServlet

 
        app
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            
        
        1
    

'app'就是DispatcherServlet的别名。
在RootWebApplicationContext启动之后,才会去初始化DispatcherServlet。
DispatcherServlet extends FrameworkServlet extends HttpServletBean
那么什么时候去加载和实例化DispatcherServlet?
一般是容器启动后的时候,
而初始化DispatcherServlet是去调用Servlet的init(ServletConfig config).
最后会去调用HttpServletBean的init()。
我们配置的init-param会放在ServletConfig中。怎么实现的?
也就是说配置文件中的contextConfigLocation怎么和FrameworkServlet类中的字段contextConfigLocation进行绑定?
HttpServletBean#init()中的这段代码实现了这个功能。如下:

BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);

采用了java bean的内省机制。
你也可以配置多个,只要是对应的Servlet的字段,都可以通过内省机制让你的配置项的值和相应的字段绑定上。其实就是将我的Servlet配置信息和我的Servlet Bean进行绑定上。
contextLoader必然在DispatcherServlet之前执行?为什么?
ContextLoaderListener负责加载,实例化,配置RootWebApplicationContext,会作为我的DispatcherServelet所在的ServletWebApplicationContext的父类?
ServletWebApplicationContext其实是由DispatcherServelet去初始化的。
看代码:
HttpServletBean#init()->
FrameworkServlet#initServletBean()->
首先initWebApplicationContext()

WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());

在初始化ServletWebApplicationContext的过程中会去读取RootWebApplicationContext,
我们之前把RootWebApplicationContext保存在ServletContext中(见ContextLoader#initWebApplicationContext(ServletContext servletContext))。
所以现在可以从ServletContext中取出来。
然后initFrameworkServlet()

9,FrameworkServlet 'app': initialization started

10,Refreshing WebApplicationContext for namespace 'app-servlet': startup date [Wed Apr 24 17:18:45 CST 2019]; parent: Root WebApplicationContext

11,Looking for @ControllerAdvice: WebApplicationContext for namespace 'app-servlet': startup date [Wed Apr 24 17:18:45 CST 2019]; parent: Root WebApplicationContext

12,FrameworkServlet 'app': initialization completed in 257 ms

7,filter或Servlet的类的字段,好像都可以使用web.xml中的进行配置。

8,web.xml中关于servlet的配置信息会放在ServletConfig中,关于FIlter的配置信息会放在FilterConfig中。注意,Filter的配置无论是在前面还是后面,filter都会比它拦截的Servlet要先执行,这事关生命周期,于配置的顺序无关。

9,1可以指定Servlet的initialize顺序,按1,2,3...初始化。

五、FIlter
1,在web.xml中配置,映射到相应的Servlet-name上面。

    
        CharacterEncodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            utf-8
        
    
    
        CharacterEncodingFilter
        app
    

CharacterEncodingFilter#init()->
GenericFilterBean#init()
在GenericFilterBean#init()中也会有一段代码,将你在web.xml中配置的值与CharacterEncodingFilter的对应字段相绑定。和HttpServletBean#init()套路差不多。

BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
                Environment env = this.environment;
                if (env == null) {
                    env = new StandardServletEnvironment();
                }

                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, (PropertyResolver)env));
                this.initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);

2,如果有多个Filter1,Filter2都拦截同一个Servlet,那么这两个Filter的执行顺序如何安排?
Filters that match a request are chained in the order in which they are declared in the web.xml.
也就是说Filter会按照它们在web.xml中配置的顺序进行执行。

六、Listener的调用顺序
1,实现了javax.servlet.ServletContextListener的监听器的()将会以声明时的顺序调用,而contextDestroyed()将会以相反的顺序调用。
2,实现了javax.servlet.ServletRequestListener的监听器的requestInitialized()将会以声明时的顺序调用,而requestDestroyed()将会以相反的顺序调用。
3,。。。

七、
1,当在 web.xml、web-fragment.xml 和 注解之间解析发生冲突时 web 应用的 web.xml具有最高优先级。
2,如果web.xml的metadata-complete属性被设置为True,则将会忽略所有的注解配置和web-fragment.xml的配置。如果是web-fragment.xml的metadata-complete=true,则忽略web-fragment.xml的配置不会忽略注解的配置。
3,

你可能感兴趣的:(传统servlet在spring中的应用)