SpringMVC-web项目和HttpInvoker

部分摘抄自

https://blog.csdn.net/caomiao2006/article/details/51290494

SpringMVC的基本使用

<context:component-scan base-package="com.gdl.service" />

从Spring提供的参考手册中我们得知该配置的功能是扫描配置的base-package包下的所有使用了@Component注解的类,并且将它们自动注册到容器中,同时也扫描@Controller,@Service,@Respository这三个注解,因为他们是继承自@Component。

<mvc:annotation-driven />

声明了@RequestMapping、@RequestBody、@ResponseBody

<context:annotation-config/>

声明了@Required、@Autowired、 @PostConstruct、@PersistenceContext、@Resource、@PreDestroy等注解。

Web容器中SpringMVC项目的启动流程

一个典型的web.xml的内容如下

<?xml version="1.0" encoding="UTF-8"?>  
  
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
    
    <!—创建Root WebApplicationContext-->
    <context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>/WEB-INF/spring/applicationContext.xml</param-value>  
    </context-param>  
  
    <listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  
    
    <!—创建Servlet WebApplicationContext-->
    <servlet>  
        <servlet-name>dispatcher</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>/WEB-INF/spring/spring-servlet.xml</param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>dispatcher</servlet-name>  
        <url-pattern>/*</url-pattern>  
    </servlet-mapping>  
	 <filter>
		<filter-name>encodingfilter</filter-name>
		<filter-class>com.my.app.EncodingFilter</filter-class>
		<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
	<filter-name>encodingfilter</filter-name>
	<url-pattern>/*</url-pattern>
	</filter-mapping>







  
</web-app>

首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;

其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;

再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是XmlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。
在这里插入图片描述

注意,在默认的情况下,DispatcherServlet只会在MVC的spring context中寻找controller的定义(包括哪些没有配置为controller但是实现了HttpRequestHandler的bean)。如果,你不小心把controller定义到了WebApplicationContext,那么将无法找到。你可以通过如下配置改变默认行为,使DispatcherServlet在初始化时去寻找WebApplicationContext中controller的定义。

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
   <property name="detectHandlerMethodsInAncestorContexts">
       <value>true</value>
   </property>
</bean>

但是,spring官方建议你各司其职,不要把controller定义在WebApplicationContext中。

SpringMVC的执行流程

首先是filter的执行。fitler的执行顺序有如下约定

1.以url-pattern方式配置的filter运行时肯定先于以servlet-name方式配置的filter
2.以url-partern方式配置的filter中,如果有多个与当前请求匹配,则按web.xml中filter-mapping出现的顺序来运行
3.对于以servlet-name方式配置的filter,如果有多个与当前请求匹配,也是按web.xml中filter-mapping出现的顺序来运行

ApplicationFilterChain执行完后,会进入对用servlet的处理程序。

在这里插入图片描述

在DispathServelet执行流程一般为

在这里插入图片描述

HttpInvoker

HttpInvoker的竞争者是RMI。RMI使用java标准的序列化机制(Spring也支持RMI)。RMI的主要缺点是如何穿越防火墙。 RMI穿越防火墙不方便主要是因为除了RMI服务注册的端口(默认1099)外,与RMI的通讯还需要另外的端口来传送数据,而另外的端口是随机分配的,所以,防火墙只设置允许1099是不可以的(这种坑爹的设计究竟是谁想出来的呢)。HttpInvoker基于HTTP进行远程调用,是使用Spring提供web服务的基础。
首先来看服务端代码
首先,我们要在spring配置文件中配置这样一个handler

    <bean name="/queryFlightWayPointSVC"
          class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <property name="service" ref="queryFlightWayPointSVC" />
        <property name="serviceInterface"
                  value="com.gdl.service.flight.interfaces.IQuery" />
    </bean>

在这里,最终要的是HttpInvokerServiceExporter这个类。该类会接收到客户端的请求。
这里写图片描述

    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            //从网络请求中恢复调用对象RemoteInvocation
            RemoteInvocation invocation = this.readRemoteInvocation(request);
            //并不是本对象亲自处理该请求,呼叫本对象的代理
            RemoteInvocationResult result = this.invokeAndCreateResult(invocation, this.getProxy());
            //将结果返回客户端
            this.writeRemoteInvocationResult(request, response, result);
        } catch (ClassNotFoundException var5) {
            throw new NestedServletException("Class not found during deserialization", var5);
        }
    }

该对象的代理构造过程如下

    protected Object getProxyForService() {
        ProxyFactory proxyFactory;
        label25: {
            this.checkService();
            this.checkServiceInterface();
            proxyFactory = new ProxyFactory();
            proxyFactory.addInterface(this.getServiceInterface());
            if (this.registerTraceInterceptor != null) {
                if (!this.registerTraceInterceptor.booleanValue()) {
                    break label25;
                }
            } else if (this.interceptors != null) {
                break label25;
            }
			//主要添加了一些日志的打印
            proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(this.getExporterName()));
        }
        //注册关于该服务的一些interceptors
        if (this.interceptors != null) {
            AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();

            for(int i = 0; i < this.interceptors.length; ++i) {
                proxyFactory.addAdvisor(adapterRegistry.wrap(this.interceptors[i]));
            }
        }

        proxyFactory.setTarget(this.getService());
        proxyFactory.setOpaque(true);
        return proxyFactory.getProxy(this.getBeanClassLoader());
    }

再来看客户端代码
spring默认的Executor是CommonsHttpInvokerRequestExecutor,但是实际中因为服务注册的各个使用场景一般都写自己的Executor(继承AbstractHttpInvokerRequestExecutor即可)。CommonsHttpInvokerRequestExecutor使用HttpClient进行网络通信。使用POST请求。

	protected RemoteInvocationResult doExecuteRequest(
			HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
			throws IOException, ClassNotFoundException {

		PostMethod postMethod = createPostMethod(config);
		try {
			setRequestBody(config, postMethod, baos);
			executePostMethod(config, getHttpClient(), postMethod);
			validateResponse(config, postMethod);
			InputStream responseBody = getResponseBody(config, postMethod);
			return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
		}
		finally {
			// Need to explicitly release because it might be pooled.
			postMethod.releaseConnection();
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值