`
ajax
  • 浏览: 251953 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Restlet实战(二十九)(完结篇)运行流程之源代码分析

    博客分类:
  • REST
阅读更多

终于到了完结篇,也体会到写书人的痛苦,虽然我仅仅是写了几十篇博客。完结篇结合源代码分析Restlet的运行流程,也算是完美的结束。当然了,也不是说从此不再关注Restlet,后续如果有新的东西出来,比如现在2.0的版本就快release了,新功能,包括API都变动了不少,如果后续有时间的话,会继续写,但是会作为博客随笔,而不是作为实战系列了。

 

在分析之前,首先一个环境,因为毕竟不同的环境涉及到的源代码也不一样,比如,如果集成了Spring的话,那么客户端发起一个请求必然会涉及到SpringServlet这个类,而如果我们配置Component到restlet.xml里,则代码执行流程就不一样。所以,有必要给出这样的测试环境描述:

 

首先看一下我的环境里web.xml里面的配置(没有其它配置):

 

 

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext*.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
<servlet-name>restlet</servlet-name>
<servlet-class>com.noelios.restlet.ext.spring.SpringServerServlet</servlet-class>
<init-param>
	<param-name>org.restlet.component</param-name>
	<param-value>component</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>restlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>

 

为什么偏偏要集成Spring来做运行流程的源代码分析呢?当然了,之前的文章提到,配置ServerServlet来分析也是可以,但是集成Spring会复杂一点,涉及到的源代码的调用会多一点,基于此,对于以后无论使用Spring或者不使用Spring的读者都可以作为参考。

 

 然后是spring配置文件的部分配置:

 

<?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:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" 
	default-autowire="byName" >
	
	<bean id="component" class="org.restlet.ext.spring.SpringComponent">
		<property name="defaultTarget" ref="defaultRouter" />
	</bean>
	
	<bean id="defaultRouter" class="org.restlet.ext.spring.SpringRouter">
		<property name="attachments">
			<map>
				<entry key="/users">
					<bean class="com.infosys.restlet.application.UserApplication"/>
				</entry>
			</map>
		</property>
	</bean>
</beans>

 

UserApplication包含的代码:

 

@Override
public synchronized Restlet createRoot() {
	Router router = new Router(getContext());
	getDecoderService().setEnabled(false);
	
	Route route = router.attach("/{userId}", UserResource.class);
	
	route.extractQuery("query", "test", true);
	
	router.attach("/{userId}/orders", UserOrdersResource.class);
	return router;
}	

 

 

客户端发起的请求代码:

 

public void test(){
	Reference reference = new Reference("http://localhost:8080/restlet/resources/users/1");
	Client client = new Client(Protocol.HTTP);
	
	Representation representation =new StringRepresentation(getUserXml(), MediaType.TEXT_PLAIN);
	
	Response response = client.post(reference, representation);
	
	assertTrue(response.getStatus().isSuccess());
}

  

 

服务器端资源应答的代码:

 

 

@Override
public void acceptRepresentation(Representation entity) throws ResourceException {
	try {
		String text = null;
		text = getRequest().getEntity().getText();
		
		System.out.println(text);
	} catch (IOException e1) {
		e1.printStackTrace();
	}
}

 

当客户端发起请求,服务器接收请求后,会根据请求的URI把这个请求分发给我们配置在Web.xml中的SpringServlet来处理。做过Java Web开发的都知道Servlet的生命周期,首先从访问Servlet的init方法开始,ok,现在就让我们进入SpringServlet的init方法,如果你随后打开Springservlet这个类的源码,你会发现里面没有init,没关系,如果本类没有,会去调用父类的同名方法,而SpringServlet的父类就是ServerServlet,看一下ServerServlet里面的init方法:

 

@Override
public void init() throws ServletException {
	if ((getComponent() != null)) {
		if ((getApplication() != null) && (getApplication().isStopped())) {
			try {
				getApplication().start();
			} catch (Exception e) {
				log("Error during the starting of the Restlet Application",
						e);
			}
		}
	}
}

 

代码比较简单,让我们跟随运行的流程深入到每一个方法,看看发生了什么,接下来是getComponet():

 

public Component getComponent() {
	Component result = this.component;

	if (result == null) {
		synchronized (ServerServlet.class) {
			if (result == null) {
				// Find the attribute name to use to store the component
				final String componentAttributeName = getInitParameter(
						NAME_COMPONENT_ATTRIBUTE,
						NAME_COMPONENT_ATTRIBUTE_DEFAULT);

				// Look up the attribute for a target
				result = (Component) getServletContext().getAttribute(
						componentAttributeName);

				if (result == null) {
					result = createComponent();
					getServletContext().setAttribute(
							componentAttributeName, result);
				}
			}

			this.component = result;
		}
	}

	return result;
}

 

为了更加清晰的理解这段代码,贴出里面调用的方法的代码:

 

public String getInitParameter(String name, String defaultValue) {
	String result = getServletConfig().getInitParameter(name);

	if (result == null) {
		result = getServletConfig().getServletContext().getInitParameter(
				name);
	}

	if (result == null) {
		result = defaultValue;
	}

	return result;
}

private static final String NAME_COMPONENT_ATTRIBUTE = "org.restlet.attribute.component";

private static final String NAME_COMPONENT_ATTRIBUTE_DEFAULT = "com.noelios.restlet.ext.servlet.ServerServlet.component";

 

 

 

结合上面两段代码,能看出,系统首先想获取组件的属性名字,按照我上面给出的配置,显而易见,我没有在web.xml中没有为servlet配置这个属性值,也没有配置一个context值,所以,代码运行到这里,必然会得到一个默认的组件属性名字,也就是上面代码的"com.noelios.restlet.ext.servlet.ServerServlet.component", 得到这个值以后,系统会根据这个值,来获取放到Servlet Context里的Component,当然了,如果是第一次请求的话,这个值肯定是空的,因为只有在创建完Component后,才会放到Servlet Context里。如果没有Component返回的话,则系统会开始创建一个新的Component,然后把创建好的Component放到Servlet Context里,以便下次再次请求的时候不需要重新创建。

 

下面看一下创建Component的源码,由于SpringServlet重写了父类的这个方法,所以将会读取本类的方法:

 

@Override
public Component createComponent() {
	Component component = null;
	final String componentBeanName = getInitParameter(
			Component_BEAN_PARAM_NAME, null);

	// Not mentionned in the Spring javadocs, but getBean surely fails if
	// the argument is null.
	if (componentBeanName != null) {
		try {
			component = (Component) getWebApplicationContext().getBean(
					componentBeanName);
		} catch (BeansException be) {
			// The bean has not been found, let the parent create it.
		}
	}

	if (component == null) {
		component = super.createComponent();
	}

	return component;
}

 

 

上面这段代码首先还是得到Component Bean name,注意跟之前的Componet attribute name是不一样的。回到我刚开始给的web.xml中的配置,就会发现,这个属性名字我们在web.xml中已经配置了,对应的值是:component。得到这个值后,系统会到spring中寻找id是component的已经实例化的bean。同样,在上面给出的spring配置文件中,我们定义了一个id是component的Bean。如果一旦没找到,则会调用父类的createComponent方法创建一个新的Bean。按照我们上面的配置,肯定能从Spring配置中获得一个bean,而不会去调用父类的方法去创建一个新的component,但是下面我还是要解释一下父类的的这个方法,因为如果不使用Spring的话,这个方法就会被调用。

 

 由于createComponent代码太长,这里就不全部贴出来,只对一些关键的代码做一个说明:

 

// Look for the Component XML configuration file.
Client client = createWarClient(new Context(), getServletConfig());
Response response = client.get("war:///WEB-INF/restlet.xml");
if (response.getStatus().isSuccess() && response.isEntityAvailable()) {
	component = new Component(response.getEntity());
}

 

如果看过之前这个系列的文章,应该能记得有一篇文章专门讲如何在restlet.xml中配置相关component。这里是首先查找一个开发者是否在WEB-INF下放置了restlet.xml这个文件,并且分析文件里面是否已经配置了实体,如Application等。如果都满足情况,则使用配置的内容来构造一个Component。

 

如果没有发现使用restlet.xml来构造component,则会查找web.xml中是否配置了component的名字,查找的参数名字是org.restlet.component. 如果记得之前系列文章的例子,在web.xml中应该是这样:

 

	<context-param>
		<param-name>org.restlet.component</param-name>
		<param-value>com.mycompany.MyComponent</param-value>
	</context-param>

 

 如果你的web.xml有如上的配置,说明你想使用自己创建的Component,当然,系统就不会为你创建默认的了。接下去就会把自己创建的Component实例化,看如下代码:

 

if (component == null) {
	final String componentClassName = getInitParameter(COMPONENT_KEY,
			null);

	// Load the component class using the given class name
	if (componentClassName != null) {
		try {
			final Class<?> targetClass = loadClass(componentClassName);

			// Create a new instance of the component class by
			// invoking the constructor with the Context parameter.
			component = (Component) targetClass.newInstance();
			......

 

ok,我们继续分析,如果没有在web.xml中配置Component,那会怎么样呢?先看代码:

 

// Create the default Component
if (component == null) {
	component = new Component();

	// The status service is disabled by default.
	component.getStatusService().setEnabled(false);

.....

 

是的,如果componet继续为空,则实例化一个新的Component,这个if判断的后面也会定义支持的客户端协议列表。

 

至此,我们知道,不管你自己是否配置了Component,这里都会有一个Component了(没有,系统创建一个默认的)。

 

创建Component方法后面的代码就相对简单了,就是把servlet的一些参数拷贝到Component的上下文中,包含了3种拷贝:

 

1. 拷贝所有的servlet容器的初始化参数

2. 拷贝所有的servlet应用的初始化参数

3. 拷贝所有的servlet上下文的属性

 

最后返回创建的Component,得到Component后,我们重新回到getComponent,因为createComponent的调用点在这个方法里。下面代码说明把得到的Component放到Servlet Context的attribute里。

 

result = createComponent();
getServletContext().setAttribute(componentAttributeName, result);

 至此,getComponent方法我们已经分析完成,让我们回到init方法。既然getComponent返回的不是null,那么会调用运行如下代码:

 

if ((getApplication() != null) && (getApplication().isStopped())) {
	try {
		getApplication().start();
	} catch (Exception e) {

 

注意,上面这段代码仍旧是init里面的代码,这里贴出来,只是为了更加清晰的描述代码运行的流程。显而易见,我们接下去要分析getApplication方法,下面就让我们进入这个方法,看看有什么东东在里面?

 

摘取这个方法部分代码:

 

if (isDefaultComponent()) {
	// Find the attribute name to use to store the
	// application
	final String applicationAttributeName = getInitParameter(
			NAME_APPLICATION_ATTRIBUTE,
			NAME_APPLICATION_ATTRIBUTE_DEFAULT);

	// Look up the attribute for a target
	result = (Application) getServletContext()
			.getAttribute(applicationAttributeName);

	if (result == null) {
		result = createApplication(getComponent()
				.getContext());
		getServletContext().setAttribute(
				applicationAttributeName, result);
	}

	this.application = result;
}

 

 

在这个方法里,首先判断当前的Component是否是default component,何谓default component?反过来理解一下,满足什么条件就不是default component呢?有两种情况,一个是通过restlet.xml配置component,一个是在web.xml配置了自己的component,实际上这两种情况在上面的代码已经分析过。根据上下文,我们知道,由于我们是结合spring,在spring配置文件配置了compoent,所以,显而易见不是default component。判断代码如下:

 

private boolean isDefaultComponent() {
	// The Component is provided via an XML configuration file.
	Client client = createWarClient(new Context(), getServletConfig());
	Response response = client.get("war:///WEB-INF/restlet.xml");
	if (response.getStatus().isSuccess() && response.isEntityAvailable()) {
		return false;
	}

	// The Component is provided via a context parameter in the "web.xml"
	// file.
	final String componentAttributeName = getInitParameter(COMPONENT_KEY,
			null);
	if (componentAttributeName != null) {
		return false;
	}

	return true;
}

 

基于我的测试环境,既然不是default component,那么if判断体里面的代码就不会执行。这里还是做一下分析,毕竟将来我们的应用有可能会达到这个分支。那就假设现在是default component。系统会首先application的属性名称,然后根据这个属性名字到servlet context里面找是否已经有值,第一次肯定是空值,但是第一次访问之后会把application的值放到servlet context里,以便下次请求可以直接取值,优化性能。如果这个值为空,就会去创建一个application,然后放到servlet context里。代码见上面。

 

好了,就不多罗嗦了,按照我们测试的流程继续走,既然不是default component,那么通过getApplication就得不到application了,相应的也不需要调用application的start方法。

 

至此servlet的init方法分析完成。

 

 

分享到:
评论
1 楼 tmw622 2010-03-27  
花了三天时间,终于看完了
虽然有些步骤没有实现起来,不过已经对Restful Web服务有了更深入的了解
非常感谢楼主的奉献

相关推荐

Global site tag (gtag.js) - Google Analytics