最近抽些时间研究了一下struts2源码,总结了一下前辈的研究成果,形成自己的一点心得吧。
框架整合WEB的入口位于web.xml文件,只有配置在web.xml文件中Servlet才会被应用加载。Struts2推荐的入口方法是StrutsPrepareAndExecuteFilter其在工程中作为一个Filter配置在web.xml中,配置如下:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
/**
*
* Struts 2框架的处理过程:
* 1. 客户端初始化一个指向Servlet容器(如Tomcat)的请求。
* 2. 这个请求经过一系列过滤器(如ActionContextCleanUp、SiteMesh等)。
* 3. StrutsPrepareAndExecuteFilter/FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action。
* 4. 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求交给ActionProxy。
* 这儿已经转到它的Delegate--Dispatcher来执行
* 5. ActionProxy通过Configuration Manager询问Struts配置文件,找到需要调用的Action类。
* 6. ActionProxy创建一个ActionInvocation实例。
* 7. ActionInvocation实例使用命名的模式来调用,回调Action的execute方法。 当然这涉及到相关拦截器的调用
* 8. 一旦Action执行完毕,ActionInvocation负责根据Struts.xml的配置返回结果
* 当然如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现
*
* @author Josn
*
*/
StrutsPrepareAndExecuteFilter中主要组成有:
属性:
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected List<Pattern> excludedPatterns=null;
方法:
init(FilterConfig filterConfig)
继承自Filter,初始化参数
doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
继承自Filter,执行方法
postInit(Dispatcher dispatcher, FilterConfig filterConfig)
一个空的方法,用于方法回调初始化
destroy()
继承自Filter,用于资源释放
具体实现及其代码描述如下:
/* 继承自Filter,初始化参数
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init=new InitOperations();//初始化辅助对象,封装了初始化的一些操作
try {
System.out.println(this.getClass().getName()+":"+filterConfig.getFilterName());
Enumeration e=filterConfig.getInitParameterNames();
while(e.hasMoreElements()){
System.out.println(e.nextElement().toString());
}
//对filterConfig进行封装 其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
FilterHostConfig config=new FilterHostConfig(filterConfig);
//通过config, 初始化struts内部日志
Iterator<String> cs=config.getInitParameterNames();
init.initLogging(config);
Dispatcher dispatcher=init.initDispatcher(config); //通过config,创建并初始化dispatcher
init.initStaticContentLoader(config, dispatcher);//通过config和dispatcher,初始化与过滤器相关的静态内容加载器
prepare=new PrepareOperations(filterConfig.getServletContext(),dispatcher); //通过config和dispatcher,创建request被处理前的系列操作对象
execute=new ExecuteOperations(filterConfig.getServletContext(),dispatcher);//通过config和dispatcher,创建处理request的系列操作对象
this.excludedPatterns=init.buildExcludedPatternsList(dispatcher);//构建Struts2不处理的URL列表
//回调空的postInit方法
postInit(dispatcher,filterConfig);
} finally {
init.clearup();//清空ActionContext
}
}
/*
* 继承自Filter,用于资源释放
*/
public void destroy() {
prepare.cleanupDispatcher();
}
/* 继承自Filter,执行方法
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
//父类向子类转:强转为http请求、响应
HttpServletRequest request=(HttpServletRequest)req;
HttpServletResponse response=(HttpServletResponse) resp;
try{
//setEncodingAndLocale调用了dispatcher方法的prepare方法:
prepare.setEncodingAndLocale(request, response); //设置request的编码和LOCAL
//创建Action上下文
prepare.createActionContext(request, response);//创建ACTIONCONTEXT,并初始化Theadlocal
prepare.assignDispatcherToThread(); //指派dispatcher给Theadlocal把dispatcher绑定到当前线程上,这样就可以通过 Dispatcher.getInstance()来获得当前线程的dispatcher,这样dispatcher就可以被所有请求共享
// 以上操作完成后就根据excludedPatterns来判断这个请求是否应该交给当前这个filter进行处理,如果不需要就调
//用chain.doFilter(request, response);交给其他filter进行处理,如果需要,那么PrepareOperations就执行
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);//对request进行包装,包装成StrutsRequestWrapper或者 MultiPartRequestWrapper(请求类型是multipart/form-data的情况)
ActionMapping mapping = prepare.findActionMapping(request, response, true);//查找并选择创建ActionMapping根据request的请求找
//到对应的ActionMapping,如果返回的是null,那就当做静态资源进行处理boolean handled =
//execute.executeStaticResourceRequest(request, response);处理失败就交给其他的filter,如果返回的不是
//null,那么就调用execute.executeAction(request, response, mapping);处理完成之后
//prepare.cleanupRequest(request);会把当前线程的ActionContext和Dispatcher设置为null
if (mapping == null) {//如果映射不存在
boolean handled=execute.executeStaticResourceRequest(request, response);
if(!handled){
chain.doFilter(request, response);
}
}else{ //如果存在映射
execute.executeAction(request, response, mapping);//执行action
}
}
}finally{
prepare.cleanupRequest(request);//清除request的Threadlocal
}
}
/**
* Callback for post initialization(一个空的方法,用于方法回调初始化)
* @param dispatcher
* @param filterConfig
*/
protected void postInit(Dispatcher dispatcher,FilterConfig filterConfig){
}
ActionContext保存了当前请求的上下文信息,当接到一个request以后,经过一些准备工作,都是再去调用Dispatcher.serviceAction(HttpServletRequest, HttpServletResponse, ServletContext, ActionMapping)方法处理请求,也就是这一步,让后续的Action能够和Servlet体系解耦而单独存在。StrutsPrepareAndExecuteFilter里面,先准备好上下文需要的东西,接着就会 构造出一个ActionMapping对象,这对象很简单(跟xwork.xml/struts.xml里的一个节点相对应的)。构造完,就扔给Dispatcher的serviceAction方法了。Dispatcher根据拿到的actionMapping对 象,创建ActionProxy对象(内部包含了目标Action),然后开始执行。ActionProxy的默认实现DefaultActionProxy里有一个叫ActionInvocation的对象,采用Command模式实现调用,命令: Action 调用者:ActionInvocation。
init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:
在init中带哦用了方法init.initDispatcher(config);这个方法里面有两个动作:
createDispatcher(filterConfig);
dispatcher.init();
来看这两个方法体的实现:
/**
* 创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,
* 然后根绝servlet上下文和参数Map构造Dispatcher
* @param filterConfig
* @return
*/
private Dispatcher createDispatcher(HostConfig filterConfig){
Map<String,String> params=new HashMap<String, String>();
Iterator e=filterConfig.getInitParameterNames();
while(e.hasNext()){
String key=(String)e.next();
String value=filterConfig.getInitParameter(key);
params.put(key, value);
}
return new Dispatcher(filterConfig.getServletContext(),params);
}
public void init(){
if(configurationManager == null){
configurationManager=new ConfigurationManager();
}
try {
init_DefaultProperties();// [1]读取properties信息,默认的default.properties struts.properties/xwork.properties
init_TraditionalXmlConfigurations();//[2]读取xml配置文件,默认的struts-default.xml,struts-plugin.xml,struts.xml ,xwork.xml文件(我们配置的具体的action的文件
init_LegacyStrutsProperties(); // [3]读取用户自定义的struts.properties国际化相关的配置
init_CustomConfigurationProviders(); // [5]自定义的configProviders
init_FilterInitParameters() ; // [6]载入FilterDispatcher传进来的initParams 整了一些Servlet相关的对象
init_AliasStandardObjects() ; // [7]将配置文件中的bean与具体的类映射 结合第1步读到的东西,注册StrutsConstants里提到的种种配置
Container container=init_PreloadConfiguration();//构建一个用于依赖注射的Container对象
//在这里面会循环调用上面七个ConfigurationProvider的register方法
//其中的重点就是DefaultConfiguration的#reload()方法
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l:dispatcherListeners){
l.dispatcherInitialized(this);
}
}
} catch (Exception ex) {
if (LOG.isErrorEnabled()) LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
/**
* 这个方法中是将一个DefaultPropertiesProvider对象追加到ConfigurationManager对象内部的ConfigurationProvider队列中。
* DefaultPropertiesProvider的#register()方法可以载入org/apache/struts2/default.properties中定义的属性。
*/
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
/**
* 这里负责载入的是FilterDispatcher的配置中所定义的config属性。
* 如果用户没有定义config属性,struts默认会载入DEFAULT_CONFIGURATION_PATHS这个值所代表的xml文件。
* 它的值为"struts-default.xml,struts-plugin.xml,struts.xml"。也就是说框架默认会载入这三个项目xml文件。
* 下一步框架会逐个判断每个config属性中定义的文件。
* 如果文件名为"xwork.xml",框架会用XmlConfigurationProvider类去处理,
* 反之则用StrutsXmlConfigurationProvider类去处理。
*/
private void init_TraditionalXmlConfigurations(){
String configPaths=initParams.get("config");
if(configPaths==null){
configPaths=DEFAULT_CONFIGURATION_PATHS;
}
String[] files=configPaths.split("\\s*[,]\\s*");
for(String file:files){
if(file.endsWith(".xml")){
if("xwork.xml".equals(file)){
configurationManager.addConfigurationProvider(createXmlConfigurationProvider(file,false));
}else{
configurationManager.addConfigurationProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
}else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}
protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
return new XmlConfigurationProvider(filename, errorIfMissing);
}
protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
}
/**
* 向ConfigurationManager加入了一个LegacyPropertiesConfigurationProvider。
*/
private void init_LegacyStrutsProperties(){
configurationManager.addConfigurationProvider(new LegacyPropertiesConfigurationProvider());
}
/**
* 此方法处理的是FilterDispatcher的配置中所定义的configProviders属性。
* 负责载入用户自定义的ConfigurationProvider。
*/
private void init_CustomConfigurationProviders(){
String configProvs = initParams.get("configProviders");
if (configProvs != null) {
String[] classes = configProvs.split("\\s*[,]\\s*");
for (String cname : classes) {
try {
Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
} catch (InstantiationException e) {
throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Unable to access provider: "+cname, e);
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Unable to locate provider class: "+cname, e);
}
}
}
}
/**
* 此方法用来处理FilterDispatcher的配置中所定义的所有属性。
*/
@SuppressWarnings("deprecation")
private void init_FilterInitParameters(){
configurationManager.addConfigurationProvider(new ConfigurationProvider() {
public void destroy() {}
public void init(Configuration configuration) throws ConfigurationException {}
public void loadPackages() throws ConfigurationException {}
public boolean needsReload() { return false; }
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
props.putAll(initParams);
}
});
}
private void init_AliasStandardObjects(){
configurationManager.addConfigurationProvider(new BeanSelectionProvider());
}
/**
* 执行完七个init_*方法后,Dispatcher的#init()会接着调用#init_PreloadConfiguration(),
* 构建一个用于依赖注射的Container对象。
* 此方法首先获取到ConfigurationManager中的Configuration对象,
* 在#getConfiguration()内部,调用上边6步添加到ConfigurationManager的ConfigurationProviders的#register()方法。
*/
private Container init_PreloadConfiguration(){
Configuration config=configurationManager.getConfiguration();
Container container=config.getContainer();
boolean reloadi18n=Boolean.valueOf(container.getInstance(String.class,StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
return container;
}
分享到:
相关推荐
这篇博文“Struts2源码解读”深入剖析了Struts2的核心机制,帮助开发者更好地理解和利用这个框架。源码分析是提升编程技能和解决问题的关键,特别是对于复杂的框架如Struts2,理解其内部工作原理能够帮助我们优化...
《Struts2技术内幕:深入解析Struts2架构设计与实现原理》由国内极为资深的...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象
《Struts2技术内幕:深入解析Struts2架构设计与实现原理》由国内极为资深的...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象。
内容简介 出版日期: 2012年1月10日 《Struts2技术内幕:深入解析Struts2架构...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象。
### 徒手配置Struts2的关键知识点 #### 一、徒手配置Struts2环境时需注意的关键事项 在没有使用任何集成开发环境的情况下配置Struts2,可能会遇到各种问题,尤其是在配置过程中对于细节的把握至关重要。以下是配置...
总之,《Struts2技术内幕——深入解析Struts2架构设计与实现原理》配合《struts2基础.chm》,将帮助读者全面掌握Struts2的架构设计、核心组件、配置方式、插件使用以及源码解读,对于想要在Java Web领域深入发展的...
本书由国内极为资深的Struts2技术专家(网名:downpour)亲自执笔,iteye兼...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象。
根据提供的文件信息“struts2-2.2.3-all.zip”,我们可以推断出这份资料包含了Struts2框架的相关资源,包括样例代码、API文档、源码以及JAR包等。下面将针对这些资源进行详细的解读与分析。 ### Struts2简介 ...
通过对Struts2中的关键参数配置进行详细解读,我们可以更深入地理解其工作原理。这些配置不仅对于Struts2的正常运行至关重要,也是开发者优化应用性能、提升用户体验的重要手段。希望本文能帮助读者更好地掌握Struts...
使用Struts2漏洞检查工具时,用户需要了解其基本操作,例如设置扫描范围、自定义扫描规则以及如何解读扫描结果。扫描完成后,工具会列出所有潜在的脆弱点,并提供相应的修复建议。在修复漏洞时,可能需要更新Struts2...
struts2教程 关于struts2的基本文件配置及实例解读
Struts2是一款非常流行的Java Web框架,它在开发企业级应用程序时提供了强大的MVC(Model-View-Controller)架构支持。然而,就像任何其他复杂的软件系统一样,Struts2也存在安全漏洞,其中一些漏洞可能导致严重的...
根据提供的文件信息,我们可以推断出这份材料是关于Struts2框架的学习资料,主要针对的是达内培训集团内部的学生。接下来,我们将详细解读并展开这些信息中的知识点。 ### Struts2框架简介 Struts2是一个基于Java...
Struts 1.2是该框架的一个版本,它的源码解读对于深入理解Struts的工作机制和原理至关重要。北大青鸟的这份文档是为了帮助学习者入门和精通Struts所编写的,包含了对Struts源码的详细解析。 首先,了解Struts的核心...
总的来说,"struts2 apps part2"涵盖了Struts2框架的深度使用和高级特性,包括Action、拦截器、视图技术、配置文件的详细解读,以及如何利用这些元素构建复杂的web应用。通过学习这部分内容,开发者可以提升在Struts...
《基于JSP+Struts2的诚欣企业免费电子商城网站系统详解》 在现代互联网商业环境中,电子商城网站已经成为企业的重要营销平台。本篇将深入解析一款名为“诚欣企业免费电子商城”的网站系统,该系统是利用JSP和Struts...
Struts2是一个强大的Java web应用程序框架,用于构建和维护可扩展、高效且易于维护的Web应用。这个"Struts2文本教程"包含了丰富的资源,帮助开发者从基础到深入理解Struts2框架。以下是对这些教程内容的详细解读: ...
下面将对Struts2 API进行详细的解读。 1. **Action接口和ActionSupport类**: Struts2的核心是Action类,它实现了业务逻辑。Action接口定义了execute()方法,它是处理请求的核心。ActionSupport是Action接口的一个...
Struts2是Java Web开发领域中一款非常重要的MVC框架,它基于Apache软件基金会的Jakarta Struts项目,为开发者提供了构建动态、数据驱动的Web应用程序的高效工具。《Struts2权威指南》这本书深入浅出地介绍了Struts2...