最简单的rose配置:<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jiexi.demos</groupId>
<artifactId>rose-demos</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>net.paoding</groupId>
<artifactId>paoding-rose</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<artifactId>mail</artifactId>
<groupId>javax.mail</groupId>
</exclusion>
<exclusion>
<artifactId>jms</artifactId>
<groupId>javax.jms</groupId>
</exclusion>
<exclusion>
<artifactId>jmxtools</artifactId>
<groupId>com.sun.jdmk</groupId>
</exclusion>
<exclusion>
<artifactId>jmxri</artifactId>
<groupId>com.sun.jmx</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>jeasyweb-framework</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.26</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>10</scanIntervalSeconds>
<stopKey>foo</stopKey>
<stopPort>9998</stopPort>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<scanIntervalSeconds>0</scanIntervalSeconds>
<daemon>true</daemon>
</configuration>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
version="2.4">
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<filter>
<filter-name>roseFilter</filter-name>
<filter-class>net.paoding.rose.RoseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>roseFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
</web-app>
package com.jiexi.demos.rose.controllers;
import net.paoding.rose.web.annotation.Path;
import net.paoding.rose.web.annotation.rest.Get;
@Path("")
public class IndexController {
@Get("index")
public String index() {
return "index";
}
}
rose启动到底做了那些工作: @Override
protected final void initFilterBean() throws ServletException {
try {
long startTime = System.currentTimeMillis();
if (logger.isInfoEnabled()) {
logger.info("[init] call 'init/rootContext'");
}
if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder();
@SuppressWarnings("unchecked")
Enumeration<String> iter = getFilterConfig().getInitParameterNames();
while (iter.hasMoreElements()) {
String name = (String) iter.nextElement();
sb.append(name).append("='").append(getFilterConfig().getInitParameter(name))
.append("'\n");
}
logger.debug("[init] parameters: " + sb);
}
WebApplicationContext rootContext = prepareRootApplicationContext();
if (logger.isInfoEnabled()) {
logger.info("[init] exits from 'init/rootContext'");
logger.info("[init] call 'init/module'");
}
// 识别 Rose 程序模块
this.modules = prepareModules(rootContext);
if (logger.isInfoEnabled()) {
logger.info("[init] exits from 'init/module'");
logger.info("[init] call 'init/mappingTree'");
}
// 创建匹配树以及各个结点的上的执行逻辑(Engine)
this.mappingTree = prepareMappingTree(modules);
if (logger.isInfoEnabled()) {
logger.info("[init] exits from 'init/mappingTree'");
logger.info("[init] exits from 'init'");
}
long endTime = System.currentTimeMillis();
// 打印启动信息
printRoseInfos(endTime - startTime);
//
} catch (final Throwable e) {
StringBuilder sb = new StringBuilder(1024);
sb.append("[Rose-").append(RoseVersion.getVersion());
sb.append("@Spring-").append(SpringVersion.getVersion()).append("]:");
sb.append(e.getMessage());
logger.error(sb.toString(), e);
throw new NestedServletException(sb.toString(), e);
}
}
2大核心功能:功能1:
/**
* 创建最根级别的 ApplicationContext 对象,比如WEB-INF、WEB-INF/classes、
* jar中的spring配置文件所组成代表的、整合为一个 ApplicationContext 对象
*
* @return
* @throws IOException
*/ private WebApplicationContext prepareRootApplicationContext()
throws IOException {
if (logger.isInfoEnabled()) {
logger.info("[init/rootContext] starting
");
}
ApplicationContext oldRootContext = (ApplicationContext) getServletContext().getAttribute(
ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
// 如果web.xml配置使用了spring装载root应用context 不可以
// roseFilter可能因为启动失败,在请求的时候容器还会尝试重新启动,此时rootContext可能已经存在,不要简单地抛出异常
// 同时这样留出了使用Listener作为init rose context的扩展机会
if (oldRootContext !=
null) {
if (oldRootContext.getClass() != RoseWebAppContext.
class) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - "
+ "check whether you have multiple ContextLoader* definitions in your web.xml!");
}
if (logger.isInfoEnabled()) {
logger.info("[init/rootContext] the root context exists:" + oldRootContext);
}
return (RoseWebAppContext) oldRootContext;
}
RoseWebAppContext rootContext =
new RoseWebAppContext(getServletContext(), load,
false);
String contextConfigLocation =
this.contextConfigLocation;
// 确认所使用的applicationContext配置
if (StringUtils.isBlank(contextConfigLocation)) {
String webxmlContextConfigLocation = getServletContext().getInitParameter(
"contextConfigLocation");
if (StringUtils.isBlank(webxmlContextConfigLocation)) {
contextConfigLocation = RoseWebAppContext.DEFAULT_CONFIG_LOCATION;
}
else {
contextConfigLocation = webxmlContextConfigLocation;
}
}
rootContext.setConfigLocation(contextConfigLocation);
rootContext.setId("rose.root");
rootContext.refresh();
if (logger.isInfoEnabled()) {
logger.info("[init/rootContext] exits");
}
/* enable: WebApplicationContextUtils.getWebApplicationContext() */ getServletContext().setAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootContext);
if (logger.isInfoEnabled()) {
logger.info("[init/rootContext] Published rose.root WebApplicationContext ["
+ rootContext + "] as ServletContext attribute with name ["
+ ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
return rootContext;
}
首先通过new直接创建WebApplicationContext,然后将其作为参数继续构建modules。可以看到参数contextConfigLocation的处理:
String webxmlContextConfigLocation = getServletContext().getInitParameter(
"contextConfigLocation");
if (StringUtils.isBlank(webxmlContextConfigLocation)) {
contextConfigLocation = RoseWebAppContext.DEFAULT_CONFIG_LOCATION;
} else {
contextConfigLocation = webxmlContextConfigLocation;
}
这里可以再web.xml里面定义参数,或者使用系统默认的参数:"/WEB-INF/applicationContext*.xml"
而对于main/resources/applicationContext *.xml 或者jar包里面的applicationContext*.xml文件的读取,是通过复写XmlWebApplicationContext的方法实现的:
@Override
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,
IOException {
Resource[] configResources = getConfigResourcesThrows();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (int i = 0; i < configLocations.length; i++) {
reader.loadBeanDefinitions(configLocations[i]);
}
}
}
功能2:
private List<Module> prepareModules(WebApplicationContext rootContext)
throws Exception {
// 自动扫描识别web层资源,纳入Rose管理
if (logger.isInfoEnabled()) {
logger.info("[init/mudule] starting
");
}
ModuleResourceProvider provider = moduleResourceProviderClass.newInstance();
if (logger.isInfoEnabled()) {
logger.info("[init/module] using provider: " + provider);
logger.info("[init/module] call 'moduleResource': to find all module resources.");
logger.info("[init/module] load " + load);
}
List<ModuleResource> moduleResources = provider.findModuleResources(load);
if (logger.isInfoEnabled()) {
logger.info("[init/mudule] exits 'moduleResource'");
}
ModulesBuilder modulesBuilder = modulesBuilderClass.newInstance();
if (logger.isInfoEnabled()) {
logger.info("[init/module] using modulesBuilder: " + modulesBuilder);
logger.info("[init/module] call 'moduleBuild': to build modules.");
}
List<Module> modules = modulesBuilder.build(moduleResources, rootContext);
if (logger.isInfoEnabled()) {
logger.info("[init/module] exits from 'moduleBuild'");
logger.info("[init/mudule] found " + modules.size() + " modules.");
}
return modules;
}
rose在init时会读取controllers里面的以Controller结尾的类,通过prepareModules功能实现的。
这块功能较复杂,涉及到*Controller,*Interceptor,*ErrorHandler,rose.properties,messages等的读取配置。
这块功能具体实现了rose的约定大约配置的思想,默认读取package的配置对应到不同的module中。