最简单的rose配置:
rose启动到底做了那些工作:
2大核心功能:
功能1:
首先通过new直接创建WebApplicationContext,然后将其作为参数继续构建modules。可以看到参数contextConfigLocation的处理:
而对于main/resources/applicationContext *.xml 或者jar包里面的applicationContext*.xml文件的读取,是通过复写XmlWebApplicationContext的方法实现的:
rose在init时会读取controllers里面的以Controller结尾的类,通过prepareModules功能实现的。
这块功能较复杂,涉及到*Controller,*Interceptor,*ErrorHandler,rose.properties,messages等的读取配置。
<
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 >
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 >
< 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";
}
}
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);
}
}
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;
}
* 创建最根级别的 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"
"contextConfigLocation");
if (StringUtils.isBlank(webxmlContextConfigLocation)) {
contextConfigLocation = RoseWebAppContext.DEFAULT_CONFIG_LOCATION;
} else {
contextConfigLocation = webxmlContextConfigLocation;
}
而对于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:
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]);
}
}
}
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;
}
// 自动扫描识别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中。
本文转自博客园沉睡森林@漂在北京的博客,原文链接:rose分析,如需转载请自行联系原博主。