第一篇:struts2代码分析
1.Struts2架构图和请求处理流程
请求首先通过Filter chain,Filter主要包括ActionContextCleanUp,它主要清理当前线程的ActionContext和Dispatcher;FilterDispatcher主要通过AcionMapper来决定需要调用哪个Action。
ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里创建ActionProxy,ActionProxy创建ActionInvocation,然后ActionInvocation调用Interceptors,执行Action本身,创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。
2.Struts2部分类介绍
这部分从Struts2参考文档中翻译就可以了。
ActionMapper
ActionMapper其实是HttpServletRequest和Action调用请求的一个映射,它屏蔽了Action对于Request等java Servlet类的依赖。Struts2中它的默认实现类是DefaultActionMapper,ActionMapper很大的用处可以根据自己的需要来设计url格式,它自己也有Restful的实现,具体可以参考文档的docs¥actionmapper.html。
ActionProxy&ActionInvocation
Action的一个代理,由ActionProxyFactory创建,它本身不包括Action实例,默认实现DefaultActionProxy是由ActionInvocation持有Action实例。ActionProxy作用是如何取得Action,无论是本地还是远程。而ActionInvocation的作用是如何执行Action,拦截器的功能就是在ActionInvocation中实现的。
ConfigurationProvider&Configuration
ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其实现类XmlConfigurationProvider及其子类StrutsXmlConfigurationProvider来解析。
3.Struts2请求流程
1、客户端发送请求
2、请求先通过ActionContextCleanUp-->FilterDispatcher
3、FilterDispatcher通过ActionMapper来决定这个Request需要调用哪个Action
4、如果ActionMapper决定调用某个Action,FilterDispatcher把请求的处理交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行
5、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类
6、ActionProxy创建一个ActionInvocation的实例
7、ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用
8、Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现。
首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,像Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干.接下来就从Dispatcher开始看起,先看其构造函数:
//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param参数
this.initParams = initParams;
}
//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param参数
this.initParams = initParams;
}
我们再看在FilterDispatcher创建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以从FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以从FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties()
创建Dispatcher之后,来看init()方法
init()方法是用来Load用户配置文件,资源文件以及默认的配置文件.主要分七步走,看下面注释
public void init(){
if(configurationManager == null){
//设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等
configurationManager
=
new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//读取properties信息,默认的default.properties,init_DefaultProperties();// [1]
//读取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//读取用户自定义的struts.properties
init_LegacyStrutsProperties();// [3]
//自定义的configProviders
init_CustomConfigurationProviders();// [5]
//载入FilterDispatcher传进来的initParams
init_FilterInitParameters();// [6]
//将配置文件中的bean与具体的类映射
init_AliasStandardObjects();// [7]
//构建一个用于依赖注射的Container对象
//在这里面会循环调用上面七个ConfigurationProvider的register方法
//其中的重点就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
}
public void init(){
if(configurationManager == null){
//设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等
configurationManager
=
new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//读取properties信息,默认的default.properties,init_DefaultProperties();// [1]
//读取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//读取用户自定义的struts.properties
init_LegacyStrutsProperties();// [3]
//自定义的configProviders
init_CustomConfigurationProviders();// [5]
//载入FilterDispatcher传进来的initParams
init_FilterInitParameters();// [6]
//将配置文件中的bean与具体的类映射
init_AliasStandardObjects();// [7]
//构建一个用于依赖注射的Container对象
//在这里面会循环调用上面七个ConfigurationProvider的register方法
//其中的重点就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
}
分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties()
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
throw
}
loadSettings(props, defaultSettings);
}
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
throw
}
new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e);
loadSettings(props, defaultSettings);
}
//PropertiesSettings构造方法
//读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings
// Load settings
InputStream in = null;
try {
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props
//这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
props.setProperty(name, settings.getLocationImpl(name));
}
}
//PropertiesSettings构造方法
//读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings =
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
settings.getImpl(name),// Load settings
InputStream in = null;
try {
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props
//这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
props.setProperty(name, settings.getLocationImpl(name));
}
}
再来看第二步:init_TraditionalXmlConfigurations()
private void init_TraditionalXmlConfigurations(){
settings.getImpl(name), //首先读取web.xml中的config初始参数值
//如果
没
有
配
置
就
使
用
默
认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了
//如果不想使用默认的名称,直接在web.xml中配置config初始参数即可
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)){
//XmlConfigurationProvider负责解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider来解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
private void init_TraditionalXmlConfigurations(){
//首先读取web.xml中的config初始参数值
//如果
没
有
配
置
就
使
用
默
认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了
//如果不想使用默认的名称,直接在web.xml中配置config初始参数即可
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)){
//XmlConfigurationProvider负责解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider来解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
对于其它配置文件只用接口。
类XmlConfigurationProvider负责配置文件的读取和解析,首先通过init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map
addAction()方法负责读取
loadInterceptorStack()方法负责将
StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProviderloadInterceptorStacks()方法负责将
而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。
protected PackageConfig
addPackage(Element
packageElement)
throws ConfigurationException {
PackageConfig.Builder newPackage = buildPackageContext(packageElement);
if(newPackage.isNeedsRefresh()){
return newPackage.build();
}
// add result types(and default result)to this package
addResultTypes(newPackage, packageElement);
// load the interceptors and interceptor stacks for this package
loadInterceptors(newPackage, packageElement);
// load the default interceptor reference for this package
loadDefaultInterceptorRef(newPackage, packageElement);
// load the default class ref for this package
loadDefaultClassRef(newPackage, packageElement);
// load the global result list for this package
loadGlobalResults(newPackage, packageElement);
// load the global exception handler list for this package
loadGobalExceptionMappings(newPackage, packageElement);
// get actions
NodeList actionList = packageElement.getElementsByTagName(“action”);
for(int i = 0;i < actionList.getLength();i++){
Element actionElement =(Element)actionList.item(i);
addAction(actionElement, newPackage);
}
// load the default action reference for this package
loadDefaultActionRef(newPackage, packageElement);
PackageConfig cfg = newPackage.build();
configuration.addPackageConfig(cfg.getName(), cfg);
return cfg;
}
loadConfigurationFiles解析读取xml中的内容
private List
loadConfigurationFiles(String
fileName,Element includeElement){
...//通过DomHelper调用SAX进行解析xml
doc = DomHelper.parse(in, dtdMappings);
...Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for(int i = 0;i < childSize;i++){
Node childNode = children.item(i);
if(childNode instanceof Element){
Element child =(Element)childNode;
final String nodeName = child.getNodeName();
if(“include”.equals(nodeName)){
String includeFileName = child.getAttribute(“file”);
//解析每个action配置是,对于include文件可以使用通配符*来进行配置
//如Struts.xml中可配置成
if(includeFileName.indexOf('*')!=-1){
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Vector
for(String match : wildcardMatches){
//递归Load子file中的
docs.addAll(loadConfigurationFiles(match, child));
}
} else {
docs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
docs.add(doc);
loadedFileUrls.add(url.toString());
...return docs;
}
首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,像Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干.接下来就从Dispatcher开始看起,先看其构造函数:
//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param参数
this.initParams = initParams;
}
//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param参数
this.initParams = initParams;
}
我们再看在FilterDispatcher创建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以从FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以从FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties()
创建Dispatcher之后,来看init()方法
init()方法是用来Load用户配置文件,资源文件以及默认的配置文件.主要分七步走,看下面注释
public void init(){
if(configurationManager == null){
//设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等
configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//读取properties信息,默认的default.properties,init_DefaultProperties();// [1]
//读取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//读取用户自定义的struts.properties
init_LegacyStrutsProperties();// [3]
//自定义的configProviders
init_CustomConfigurationProviders();// [5]
//载入FilterDispatcher传进来的initParams
init_FilterInitParameters();// [6]
//将配置文件中的bean与具体的类映射
init_AliasStandardObjects();// [7]
//构建一个用于依赖注射的Container对象
//在这里面会循环调用上面七个ConfigurationProvider的register方法
//其中的重点就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
new
}
public void init(){
if(configurationManager == null){
//设置ConfigurationManager的defaultFrameworkBeanName.//这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等
configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//读取properties信息,默认的default.properties,init_DefaultProperties();// [1]
//读取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//读取用户自定义的struts.properties
init_LegacyStrutsProperties();// [3]
//自定义的configProviders
init_CustomConfigurationProviders();// [5]
//载入FilterDispatcher传进来的initParams
init_FilterInitParameters();// [6]
//将配置文件中的bean与具体的类映射
init_AliasStandardObjects();// [7]
//构建一个用于依赖注射的Container对象
//在这里面会循环调用上面七个ConfigurationProvider的register方法
//其中的重点就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
new
}
分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.下面将分析这七层功夫是怎样一步步练成的.首先是init_DefaultProperties()
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
throw
}
loadSettings(props, defaultSettings);
}
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
throw
}
loadSettings(props, defaultSettings);
}
//PropertiesSettings构造方法
//读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings
// Load settings
InputStream in = null;
try {
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props
//这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
props.setProperty(name, settings.getLocationImpl(name));
}
}
//PropertiesSettings构造方法
//读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
settings.getImpl(name),if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings
// Load settings
InputStream in = null;
try {
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props
//这个props是register的一个入参.protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
props.setProperty(name, settings.getLocationImpl(name));
}
}
再来看第二步:init_TraditionalXmlConfigurations()
private void init_TraditionalXmlConfigurations(){
//首先读取web.xml中的config初始参数值
//如果
没
有
配
置
就
使
settings.getImpl(name),用默认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了
//如果不想使用默认的名称,直接在web.xml中配置config初始参数即可
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)){
//XmlConfigurationProvider负责解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider来解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
private void init_TraditionalXmlConfigurations(){
//首先读取web.xml中的config初始参数值
//如果
没
有
配
置
就
使
用
默
认的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//这儿就可以看出为什么默认的配置文件必须取名为这三个名称了
//如果不想使用默认的名称,直接在web.xml中配置config初始参数即可
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)){
//XmlConfigurationProvider负责解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider来解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
对于其它配置文件只用接口。
类XmlConfigurationProvider负责配置文件的读取和解析,首先通过init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map
addAction()方法负责读取
loadInterceptorStack()方法负责将
loadInterceptorStacks()方法负责将
而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。
protected PackageConfig
addPackage(Element
packageElement)
throws ConfigurationException {
PackageConfig.Builder newPackage = buildPackageContext(packageElement);
if(newPackage.isNeedsRefresh()){
return newPackage.build();
}
// add result types(and default result)to this package
addResultTypes(newPackage, packageElement);
// load the interceptors and interceptor stacks for this package
loadInterceptors(newPackage, packageElement);
// load the default interceptor reference for this package
loadDefaultInterceptorRef(newPackage, packageElement);
// load the default class ref for this package
loadDefaultClassRef(newPackage, packageElement);
// load the global result list for this package
loadGlobalResults(newPackage, packageElement);
// load the global exception handler list for this package
loadGobalExceptionMappings(newPackage, packageElement);
// get actions
NodeList actionList = packageElement.getElementsByTagName(“action”);
for(int i = 0;i < actionList.getLength();i++){
Element actionElement =(Element)actionList.item(i);
addAction(actionElement, newPackage);
}
// load the default action reference for this package
loadDefaultActionRef(newPackage, packageElement);
PackageConfig cfg = newPackage.build();
configuration.addPackageConfig(cfg.getName(), cfg);
return cfg;
}
loadConfigurationFiles解析读取xml中的内容
private List
loadConfigurationFiles(String
fileName, includeElement){
...//通过DomHelper调用SAX进行解析xml
doc = DomHelper.parse(in, dtdMappings);
...Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for(int i = 0;i < childSize;i++){
Node childNode = children.item(i);
if(childNode instanceof Element){
Element child =(Element)childNode;
final String nodeName = child.getNodeName();
if(“include”.equals(nodeName)){
String includeFileName = child.getAttribute(“file”);
//解析每个action配置是,对于include文件可以使用通配符*来进行配置
//如Struts.xml中可配置成
if(includeFileName.indexOf('*')!=-1){
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Element
Vector
for(String match : wildcardMatches){
//递归Load子file中的
docs.addAll(loadConfigurationFiles(match, child));
}
} else {
docs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
docs.add(doc);
loadedFileUrls.add(url.toString());
...return docs;
}
接下来第三步:init_LegacyStrutsProperties()调用的是调用的是LegacyPropertiesConfigurationProvider 通过比较前
面
DefaultPropertiesProvider
与
调
用的是LegacyPropertiesConfigurationProvider.发现DefaultPropertiesProvider继承自后者,但重写了register()方法,主要是生成PropertiesSetting的不同,前者是根据org/apache/struts2/default.properties 后者是根据struts.properties 我们展开register()中的Settings.getInstance(),最后是调用getDefaultInstance()
private static Settings getDefaultInstance(){
if(defaultImpl == null){
// Create bootstrap implementation
//不带参数的DefaultSettings(),区别与DefaultPropertiesProvider中直接带default.properties参数
//不带参数就是默认为struts.propertes,并且加载struts.custom.properties所定义的properties文件
defaultImpl = new DefaultSettings();
// Create default implementation
try {
//STRUTS_CONFIGURATION为:struts.configuration
//在struts.proterties中查找struts.configuration的值,这个值必须是org.apache.struts2.config.Configuration接口的实现类
//所以我有个困惑就是在下面的转换当中怎么将Configuration转换成Setting类型的...//这一点先放下了,有时间再研究
String className = get(StrutsConstants.STRUTS_CONFIGURATION);
if(!className.equals(defaultImpl.getClass().getName())){
try {
// singleton instances shouldn't be built accessing request or session-specific context data
defaultImpl oader().loadClass(className), null);
} catch(Exception e){
LOG.error(“Settings:
}
}
} catch(IllegalArgumentException ex){
// ignore
}
private static Settings getDefaultInstance(){
if(defaultImpl == null){
// Create bootstrap implementation
//不带参数的DefaultSettings(),区别与DefaultPropertiesProvider中直接带default.properties参数
//不带参数就是默认为struts.propertes,并且加载struts.custom.properties所定义的properties文件
defaultImpl = new DefaultSettings();
// Create default implementation
try {
//STRUTS_CONFIGURATION为:struts.configuration
//在struts.proterties中查找struts.configuration的值,这个值必须是
Could
not
instantiate
the struts.configuration object, substituting the default implementation.”, e);
=
(Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLorg.apache.struts2.config.Configuration接口的实现类
//所以我有个困惑就是在下面的转换当中怎么将Configuration转换成Setting类型的...//这一点先放下了,有时间再研究
String className = get(StrutsConstants.STRUTS_CONFIGURATION);
if(!className.equals(defaultImpl.getClass().getName())){
try {
// singleton instances shouldn't be built accessing request or session-specific context data
defaultImpl oader().loadClass(className), null);
} catch(Exception e){
LOG.error(“Settings:
}
}
} catch(IllegalArgumentException ex){
// ignore
}
在2.1.6中去掉了第四步:init_ZeroConfiguration();第五步是自定义的configProviders
private void init_CustomConfigurationProviders(){
//从这里可以看到可以将自定义的Provider定义在web.xml中FilterDispatcher的param中:configProviders
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(ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
prov
=
Could
not
instantiate
the struts.configuration object, substituting the default implementation.”, e);
=
(Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassL
}
...}
}
}
private void init_CustomConfigurationProviders(){
//从这里可以看到可以将自定义的Provider定义在web.xml中FilterDispatcher的param中:configProviders
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(ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
}
...}
}
}
第六步:init_FilterInitParameters
//从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcher的param中定义
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;}
prov
=
public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException {
props.putAll(initParams);//在这里实现滴~
}
});
}
//从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcher的param中定义
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);//在这里实现滴~
}
});
}
第七步:init_AliasStandardObjects,使用BeanSelectionProvider 这是将配置文件中定义的
接下来是看怎样调用这些ConfigurationProviders 展开init_PreloadConfiguration()
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;
}
//再看getConfiguration()
public synchronized Configuration getConfiguration(){
if(configuration == null){
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
//重点就是这个reloadContainer
configuration.reloadContainer(getContainerProviders());
} catch(ConfigurationException e){
setConfiguration(null);
throw new ConfigurationException(“Unable to load configuration.”, e);
}
} else {
conditionalReload();
}
return configuration;
}
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;
}
//再看getConfiguration()
public synchronized Configuration getConfiguration(){
if(configuration == null){
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
//重点就是这个reloadContainer
configuration.reloadContainer(getContainerProviders());
} catch(ConfigurationException e){
setConfiguration(null);
throw new ConfigurationException(“Unable to load configuration.”, e);
}
} else {
conditionalReload();
}
return configuration;
}
展开DefaultConfiguration中的reloadContainer
public synchronized List
reloadContainer(List
packageContexts.clear();
loadedFileNames.clear();
List
packageProviders = new ArrayList
();
//Struts2(xwork2)用Container来完成依赖注入的功能
//首先初始化一个ContainerBuilder,再由builder来保存接口与实现类或工厂类的对应关系
//然后通过builder.create(boolean)方法产生container
//由container.getInstance(Class);就可以得到接口的实现实例了
//这一部分比较复杂,后面研究完成了,会单独拿出来讲,这里先弄清楚Xwork依赖注入的实现步骤就可以了
ContainerProperties props = new ContainerProperties();
ContainerBuilder builder = new ContainerBuilder();
for(final ContainerProvider containerProvider : providers)
{
//循环调用ConfigurationProvider的init和register方法,明白了吧,在这里统一循环调用
containerProvider.init(this);
containerProvider.register(builder, props);
}
props.setConstants(builder);
//注入依赖关系,在这里并不产生实例
builder.factory(Configuration.class, new Factory
public Configuration create(Context context)throws Exception {
return DefaultConfiguration.this;
}
});
ActionContext oldContext = ActionContext.getContext();
try {
// Set the bootstrap container for the purposes of factory creation
Container bootstrap = createBootstrapContainer();
setContext(bootstrap);
//create已经注入依赖关系的Container
container = builder.create(false);
setContext(container);
objectFactory = container.getInstance(ObjectFactory.class);
// Process the configuration providers first
for(final ContainerProvider containerProvider : providers)
{
if(containerProvider instanceof PackageProvider){
container.inject(containerProvider);
//调用PackageProvider的loadPackages()方法,这里主要是针对XmlConfigurationProvider和StrutsXmlConfigurationProvider
((PackageProvider)containerProvider).loadPackages();
packageProviders.add((PackageProvider)containerProvider);
}
}
// Then process any package providers from the plugins
Set
packageProviderNames
= container.getInstanceNames(PackageProvider.class);
if(packageProviderNames!= null){
for(String name : packageProviderNames){
PackageProvider
provider.init(this);
provider.loadPackages();
packageProviders.add(provider);
}
}
rebuildRuntimeConfiguration();
} finally {
if(oldContext == null){
ActionContext.setContext(null);
}
}
return packageProviders;
}
Dispatcher已经在之前讲过,这就好办了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。
public void init(FilterConfig filterConfig)throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging();
//创建dispatcher,前面都已经讲过啰
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
//注入将FilterDispatcher中的变量通过container注入,如下面的staticResourceLoader
dispatcher.getContainer().inject(this);
//StaticContentLoader在BeanSelectionProvider中已经被注入了依赖关系:DefaultStaticContentLoader
//可以在struts-default.xml中的
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
provider
= container.getInstance(PackageProvider.class, name);
ActionContext.setContext(null);
}
}
public void init(FilterConfig filterConfig)throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging();
//创建dispatcher,前面都已经讲过啰
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
//注入将FilterDispatcher中的变量通过container注入,如下面的staticResourceLoader
dispatcher.getContainer().inject(this);
//StaticContentLoader在BeanSelectionProvider中已经被注入了依赖关系:DefaultStaticContentLoader
//可以在struts-default.xml中的
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
ActionContext.setContext(null);
}
}
//下面来看DefaultStaticContentLoader的setHostConfig
public void setHostConfig(HostConfig filterConfig){
//读取初始参数
pakages,调用
parse(),解析成类似/org/apache/struts2/static,/template的数组
String param = filterConfig.getInitParameter(“packages”);
//“org.apache.struts2.static org.apache.struts2.interceptor.debugging static”
String packages = getAdditionalPackages();
if(param!= null){
packages = param + “ ” + packages;
}
this.pathPrefixes = parse(packages);
initLogging(filterConfig);
}
template //下面来看DefaultStaticContentLoader的setHostConfig
public void setHostConfig(HostConfig filterConfig){
//读取初始参数
pakages,调用
parse(),解析成类似/org/apache/struts2/static,/template的数组
String param = filterConfig.getInitParameter(“packages”);
//“org.apache.struts2.static org.apache.struts2.interceptor.debugging static”
String packages = getAdditionalPackages();
if(param!= null){
packages = param + “ ” + packages;
}
this.pathPrefixes = parse(packages);
initLogging(filterConfig);
}
现在回去doFilter的方法,每当有一个Request,都会调用这些Filters的doFilter方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
HttpServletResponse response =(HttpServletResponse)res;
ServletContext servletContext = getServletContext();
String timerKey = “FilterDispatcher_doFilter: ”;
try {
// FIXME: this should be refactored better to not duplicate work with the action invocation
//先看看ValueStackFactory所注入的实现类OgnlValueStackFactory
//new OgnlValueStack
ValueStack
stack
= dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
ActionContext ctx = new ActionContext(stack.getContext());
ActionContext.setContext(ctx);
template
UtilTimerStack.push(timerKey);
//如果是multipart/form-data就用MultiPartRequestWrapper进行包装
//MultiPartRequestWrapper
是
StrutsRequestWrapper的子类,两者都是HttpServletRequest实现
//此时在MultiPartRequestWrapper中就会把Files给解析出来,用于文件上传
//所有request都会StrutsRequestWrapper进行包装,StrutsRequestWrapper是可以访问ValueStack
//下面是参见Dispatcher的wrapRequest
// String content_type = request.getContentType();
//if(content_type!= null&&content_type.indexOf(“multipart/form-data”)!=-1){
//MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);
//request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));
//} else {
//
request = new StrutsRequestWrapper(request);
// }
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
//根据url取得对应的Action的配置信息
//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存储在 ActionMapping对象中
mapping
=
actionMapper.getMapping(request, dispatcher.getConfigurationManager());
} catch(Exception ex){
log.error(“error getting ActionMapping”, ex);
dispatcher.sendError(request,return;
}
//如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等
//这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到
response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
=new 404
if(mapping == null){
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if(“".equals(resourcePath)&& null!= request.getPathInfo()){
resourcePath = request.getPathInfo();
}
if(staticResourceLoader.canHandle(resourcePath)){
// 在DefaultStaticContentLoader
中
:return
serveStatic
&&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“));
staticResourceLoader.findStaticResource(resourcePath, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
//正式开始Action的方法
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
request,HttpServletResponse response =(HttpServletResponse)res;
ServletContext servletContext = getServletContext();
String timerKey = ”FilterDispatcher_doFilter: “;
try {
// FIXME: this should be refactored better to not duplicate work with the action invocation
//先看看ValueStackFactory所注入的实现类OgnlValueStackFactory
//new OgnlValueStack
ValueStack
stack
= dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
ActionContext ctx = new ActionContext(stack.getContext());
ActionContext.setContext(ctx);
UtilTimerStack.push(timerKey);
//如果是multipart/form-data就用MultiPartRequestWrapper进行包装
//MultiPartRequestWrapperHttpServletRequest实现
//此时在MultiPartRequestWrapper中就会把Files给解析出来,用于文件上传
//所有request都会StrutsRequestWrapper进行包装,StrutsRequestWrapper是可以访问ValueStack
//下面是参见Dispatcher的wrapRequest
// String content_type = request.getContentType();
//if(content_type!= null&&content_type.indexOf(”multipart/form-data“)!=-1){
//MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);
//request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));
//} else {
//
request = new StrutsRequestWrapper(request);
// }
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
=new
是
StrutsRequestWrapper的子类,两者都是
//根据url取得对应的Action的配置信息
//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存储在 ActionMapping对象中
mapping
} catch(Exception ex){
log.error(”error getting ActionMapping“, ex);
dispatcher.sendError(request,return;
}
//如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等
//这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到404
if(mapping == null){
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if(”“.equals(resourcePath)&& null!= request.getPathInfo()){
resourcePath = request.getPathInfo();
}
if(staticResourceLoader.canHandle(resourcePath)){
// 在DefaultStaticContentLoader
中
:return
serveStatic
&&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“));
staticResourceLoader.findStaticResource(resourcePath, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
request,response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
=
actionMapper.getMapping(request, dispatcher.getConfigurationManager());
//正式开始Action的方法
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
//下面是ActionMapper接口的实现类 DefaultActionMapper的getMapping()方法的源代码:
public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){
ActionMapping mapping = new ActionMapping();
String uri = getUri(request);//得到请求路径的URI,如:testAtcion.action或testAction.do
int indexOfSemicolon = uri.indexOf(”;“);//修正url的带;jsessionid 时找不到而且的bug
uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;
uri = dropExtension(uri, mapping);//删除扩展名,默认扩展名为action
if(uri == null){
return null;
}
parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace
handleSpecialParameters(request, mapping);//去掉重复参数
//如果Action的name没有解析出来,直接返回
if(mapping.getName()== null){
returnnull;
}
//下面处理形如testAction!method格式的请求路径
if(allowDynamicMethodCalls){
// handle ”name!method“ convention.String name = mapping.getName();
int exclamation = name.lastIndexOf(”!“);//!是Action名称和方法名的分隔符
if(exclamation!=-1){
mapping.setName(name.substring(0, exclamation));//提取左边为name
mapping.setMethod(name.substring(exclamation + 1));//提取右边的method
}
}
return mapping;
}
//下面是ActionMapper接口的实现类 DefaultActionMapper的getMapping()方法的源代码:
public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){
ActionMapping mapping = new ActionMapping();
String uri = getUri(request);//得到请求路径的URI,如:testAtcion.action或testAction.do
int indexOfSemicolon = uri.indexOf(”;“);//修正url的带;jsessionid 时找不到而且的bug
uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;
uri = dropExtension(uri, mapping);//删除扩展名,默认扩展名为action
if(uri == null){
return null;
}
parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace
handleSpecialParameters(request, mapping);//去掉重复参数
//如果Action的name没有解析出来,直接返回
if(mapping.getName()== null){
returnnull;
}
//下面处理形如testAction!method格式的请求路径
if(allowDynamicMethodCalls){
// handle ”name!method“ convention.String name = mapping.getName();
int exclamation = name.lastIndexOf(”!“);//!是Action名称和方法名的分隔符
if(exclamation!=-1){
mapping.setName(name.substring(0, exclamation));//提取左边为name
mapping.setMethod(name.substring(exclamation + 1));//提取右边的method
}
}
return mapping;
}
从代码中看出,getMapping()方法返回ActionMapping类型的对象,该对象包含三个参数:Action的name、namespace和要调用的方法method。
如果getMapping()方法返回ActionMapping对象为null,则FilterDispatcher认为用户请求不是Action,自然另当别论,FilterDispatcher会做一件非常有意思的事:如果请求以/struts开头,会自动查找在web.xml文件中配置的 packages初始化参数,就像下面这样:
org.apache.struts2.dispatcher.FilterDispatcher
packages
com.lizanhong.action
org.apache.struts2.dispatcher.FilterDispatcher
packages
com.lizanhong.action
FilterDispatcher会将com.lizanhong.action包下的文件当作静态资源处理,即直接在页面上显示文件内容,不过会忽略扩展名为class的文件。比如在com.lizanhong.action包下有一个aaa.txt的文本文件,其内容为“中华人民共和国”,访问
http://localhost:8081/Struts2Demo/struts/aaa.txt时会输出txt中的内容
FilterDispatcher.findStaticResource()方法
protectedvoid findStaticResource(String
name,HttpServletRequest
request, HttpServletResponse response)throws IOException {
if(!name.endsWith(”.class“)){//忽略class文件
//遍历packages参数
for(String pathPrefix : pathPrefixes){
InputStream is = findInputStream(name, pathPrefix);//读取请求文件流
if(is!= null){
...// set the content-type header
String contentType = getContentType(name);//读取内容类型
if(contentType!= null){
response.setContentType(contentType);//重新设置内容类型
}
...try {
//将读取到的文件流以每次复制4096个字节的方式循环输出
copy(is, response.getOutputStream());
} finally {
is.close();
}
return;
}
}
}
}
protectedvoid findStaticResource(String
name,HttpServletRequest
request, HttpServletResponse response)throws IOException {
if(!name.endsWith(”.class“)){//忽略class文件
//遍历packages参数
for(String pathPrefix : pathPrefixes){
InputStream is = findInputStream(name, pathPrefix);//读取请求文件流
if(is!= null){
...// set the content-type header
String contentType = getContentType(name);//读取内容类型
if(contentType!= null){
response.setContentType(contentType);//重新设置内容类型
}
...try {
//将读取到的文件流以每次复制4096个字节的方式循环输出
copy(is, response.getOutputStream());
} finally {
is.close();
}
return;
}
}
}
}
如果用户请求的资源不是以/struts开头——可能是.jsp文件,也可能是.html文件,则通过过滤器链继续往下传送,直到到达请求的资源为止。
如果getMapping()方法返回有效的ActionMapping对象,则被认为正在请求某个Action,将调用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,该方法是处理Action的关键所在。
下面就来看serviceAction,这又回到全局变量dispatcher中了
//Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping)throws ServletException {
//createContextMap方法主要把Application、Session、Request的key value值拷贝到Map中
Map
// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
ValueStack
stack
=
(ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if(nullStack){
ActionContext ctx = ActionContext.getContext();
if(ctx!= null){
stack = ctx.getValueStack();
}
}
if(stack!= null){
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
}
String timerKey = ”Handling request from Dispatcher“;
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
Configuration config = configurationManager.getConfiguration();
//创建一个Action的代理对象,ActionProxyFactory是创建ActionProxy的工厂
//参考实现类:DefaultActionProxy和DefaultActionProxyFactory
ActionProxy
proxy
= config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
// if the ActionMapping says to go straight to a result, do it!
//如果是Result,则直接转向,关于Result,ActionProxy,ActionInvocation下一讲中再分析
if(mapping.getResult()!= null){
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
//执行Action
proxy.execute();
}
// If there was a previous value stack then set it back onto the request
if(!nullStack){
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch(ConfigurationException e){
// WW-2874 Only log error if in devMode
if(devMode){
LOG.error(”Could not find action or result“, e);
}
else {
LOG.warn(”Could not find action or result“, e);
}
sendError(request, HttpServletResponse.SC_NOT_FOUND, e);
} catch(Exception e){
sendError(request,} finally {
UtilTimerStack.pop(timerKey);
}
} 下面开始讲一下主菜ActionProxy了.在这之前最好先去了解一下动态Proxy的基本知识.ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法。归根到底,最后调用的是DefaultActionInvocation.invokeAction()方法。DefaultActionInvocation()->init()->createAction()。
最后
通
过
调
用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()这里的步骤是先由ActionProxyFactory创建ActionInvocation和ActionProxy.public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
response,context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
response,context,ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
下面先看DefaultActionInvocation的init方法
public void init(ActionProxy proxy){
this.proxy = proxy;
Map
// Setting this so that other classes, like object factories, can use the ActionProxy and other
// contextual information to operate
ActionContext actionContext = ActionContext.getContext();
if(actionContext!= null){
actionContext.setActionInvocation(this);
}
//创建Action,struts2中每一个Request都会创建一个新的Action
createAction(contextMap);
if(pushAction){
stack.push(action);
contextMap.put(”action“, action);
}
invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName());
// get a new List so we don't get problems with the iterator if someone changes the list
List
interceptorList
=
new ArrayList
interceptors = interceptorList.iterator();
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
}
protected void createAction(Map
// load action
String timerKey = ”actionCreate: “ + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
//默认为SpringObjectFactory:struts.objectFactory=spring.这里非常巧妙,在struts.properties中可以重写这个属性
//在前面BeanSelectionProvider中通过配置文件为ObjectFactory设置实现类
//这里以Spring为例,这里会调到SpringObjectFactory的buildBean方法,可以通过ApplicationContext的getBean()方法得到Spring的Bean
action
=
objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
} catch(InstantiationException e){
throw new
XWorkException(”Unable
to
intantiate
Action!“,e, proxy.getConfig());
} catch(IllegalAccessException e){
throw new XWorkException(”Illegal access to constructor, is it public?", e, proxy.getConfig());
} catch(Exception e){
...} finally {
UtilTimerStack.pop(timerKey);
}
if(actionEventListener!= null){
action = actionEventListener.prepare(action, stack);
}
}
//SpringObjectFactory
public Object buildBean(String beanName, Map
Object o = null;
try {
//SpringObjectFactory
会
通
过
web.xml
中的
第二篇:Struts2介绍
Struts2集成指南
关于Struts2 Struts是Apache软件基金会(ASF)赞助的一个开源项目。它最初是Jakarta项目中的一个子项目,并在2004年3月成为ASF的顶级项目。它通过采用Java Servlet/JSP技术,实现了基于Java EE Web应用的Model-View-Controller〔MVC〕设计模式的应用框架〔Web Framework〕,是MVC经典设计模式中的一个经典产品。
Struts,a history 在Java EE的Web应用发展的初期,除了使用Servlet技术以外,普遍是在JavaServer Pages(JSP)的源代码中,采用HTML与Java代码混合的方式进行开发。因为这两种方式不可避免的要把表现与业务逻辑代码混合在一起,都给前期开发与后期维护带来巨大的复杂度。为了摆脱上述的约束与局限,把业务逻辑代码从表现层中清晰的分离出来,2000年,Craig McClanahan采用了MVC的设计模式开发Struts。后来该框架产品一度被认为是最广泛、最流行JAVA的WEB应用框架。
Craig McClanahan 2006年,WebWork与Struts这两个优秀的Java EE Web框架(Web Framework〕的团体,决定合作共同开发一个新的,整合了WebWork与Struts优点,并且更加优雅、扩展性更强的框架,命名为“Struts 2”,原Struts的1.x版本产品称为“Struts 1”。
至此,Struts项目并行提供与维护两个主要版本的框架产品——Struts 1与Struts 2。Struts1 vs.Struts2 侵入性
Struts 1 在编程方面是面向抽象类编程,而不是面向接口编程。Struts 1要求自定义Action 类继承一个特定的抽象基类Action。另一方面,Struts 1的 Action 依赖于 Servlet API,因为Struts 1 Action 的execute 方法中有HttpServletRequest 和HttpServletResponse 方法。例如 e.g.public class LogonAction extends Action {
public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response){
} }
Struts 2 Action 类可以实现一个Action接口,也可以实现其他接口,甚至不实现任何接口。这使得可选的和定制的服务成为可能。e.g.public class ExampleAction {
public String doSomething(){
return “success”;} }
线程模式
Struts 1 Action类 是单例模式并且必须是线程安全的,因为在web容器中,仅有Action类 的一个实例来处理所有的请求。
Struts2 Web容器为每一个请求产生一个Action类实例,因此没有线程安全问题。可测试性
Struts1 由于对Servlet API的依赖,使得针对于自定义Action类的测试变得复杂。
Struts2 由于自定义Action可以为POJO,所以可以向测试一个POJO一样来测试Action类。
请求参数封装
Struts1 使用ActionForm 对象封装用户的请求参数,所有的 ActionForm 必须继承一个基类:ActionForm。普通的JavaBean 不能用作ActionForm,并且需要在配置文件中定义ActionForm。e.g.public class LogonForm extends ActionForm {
private String userpassword;
private String username;}
Struts2 直接使用Action属性来封装用户请求属性,避免了开发者需要大量开发ActionForm类的烦琐,实际上,这些属性还可以是包含子属性的 Rich对象类型。e.g.public class ExampleAction {
private String responseMessage;private String requestMessage;
public String getResponseMessage(){
return responseMessage;}
public void setResponseMessage(String responseMessage){
this.responseMessage = responseMessage;}
public String getRequestMessage(){
return requestMessage;
} } public void setRequestMessage(String requestMessage){ } this.requestMessage = requestMessage;public String doSomething(){
} setMessage(“Hi, ” + getRequestMessage());return “success”;EL Struts1 整合了 JSTL,因此可以使用JSTL 表达式语言。JSTL有基本对象图遍历,但在对集合和索引属性的支持上则功能不强。在向视图绑定值时,Struts1 使用标准JSP 机制把对象绑定到视图页面。
Struts2 Struts 2 可以使用JSTL,但它整合了一种更强大和灵活的表达式语言:OGNL(Object Graph Notation Language),因此,Struts 2 下的表达式语言功能更加强大。在向视图绑定值时,Struts2 使用“ValueStack ”技术,使标签库能够访问值,而不需要把对象和视图页面绑定在一起。
校验框架
Struts1 Struts1 支持在 ActionForm 重写validate 方法中手动校验,或者通过整合 Commons-validator 框架来完成数据校验。
Struts2 Struts 2 支持通过重写validate方法进行校验,也支持整合XWork 校验框架进行校验。Struts2 architacture
名词约定
凡是代码、配置文件、IDE中出现的名词,均采用英文原称。
Roadmap 本文档的目标是,帮助读者在Eclipse中将Struts2集成至一个崭新的Dynamic Web Project。集成步骤大致如下:配置Struts2的代码环境-> 在web.xml中加入Struts2功能-> 测试Struts2。文档目标达成的标志是:页面请求能够通过Struts2的Action Mapping成功转发,并且基于Java的Struts2验证框架能够生效。
集成步骤
引入Struts2相关的jar文件
Struts2 jars commons-fileupload-1.2.2.jar commons-io-2.1.jar commons-lang-2.4.jar commons-logging-1.1.1.jar freemarker-2.3.16.jar javassist.jar jstl-1.2.jar ognl-3.0.1.jar struts2-core-2.2.3.jar xwork-core-2.2.3.jar 加入build path 将以上10个jar文件,拷贝至WebContent/WEB-INF/lib下:
对于Web Dynamic Project,一般情况下,当你向lib目录下copy了jar文件,eclipse会自动将jar文件加入build path下的名为Web App Libraries的Library。请确认在工程下出现了名为Web App Libraries的Library。如果有,说明这些jar文件已经被添加至build path了:
如果在工程目录下找不到名为Web App Libraries的Library,说明jar文件没有被添加至build path,需要进行手动添加。首先进入build path设置界面,选中Libraries 页,并点击Add JARs:
在JAR Selection窗口中,选中lib下所有的jar文件。选中后点击OK:
你将看到被选中的jar文件已经被添加至build path:
在工程中会出现一个名为Referenced Libraries的Libraries。这说明jar文件已经被添加至build path:
以上两种方法都可以将jar文件添加至build path,它们的效果是一样的。
配置web.xml 添加filter 在web.xml中添加一个filter:
filter-name表示filter的名字,你可以任意决定这个名字。filter-class表示使用哪个类作为filter,从这个类的全称来判断,可以发现FilterDispatcher是Struts2提供的一个类。它是Struts2转发请求的起点。在web.xml中添加一个filter-mapping:
filter-mapping用来映射url和filter的映射关系。filter-name表示filter的名字,这个名字必须和之前filter声明中的filter-name一致。url-pattern表示哪些格式的url会被此filter滤中。/*表示在此web应用域名下,所有的地址都会被filter滤中,换言之,所有的http请求都会通过Struts2进行转发。
filter的作用
通过以上的配置,FilterDispatcher和url与filter-name联系在了一起。
由于在web容器中注册了FilterDispatcher这个filter,Struts2可以收到所有http://localhost:8080/tyland-b2b 的http请求。随后,FilterDispatcher会根据我们定义的action-mapping规则,将请求分发到指定的action类以及它的拦截器栈。最后,Struts2按照action-mapping规则,将后台计算的结果返回给指定页面。笼统地来说,Struts2就是这样工作的,所以说,FilterDispatcher是Struts2工作的入口。
编写代码,测试Struts2 Struts2的环境已经配置好了,基于action-mapping的转发机制已经可以运行了。为了证明这一点,请编写一些测试jsp页面和java代码。
在编写代码的过程中,请确保代码文件的位置如下图所示:
代码清单如下:
Java代码
UserVO.java package com.tyland.b2b.vo;
// 一个Value Object(Data Model),用来存放用户名、密码 public class UserVO {
private String username;private String password;
// 成员变量password的getter方法。
// 在Strtus2中,用来在页面和服务器间传值的Value Object必须有getter方法
public String getPassword(){
return password;
} } // 成员变量password的setter方法。
// 在Strtus2中,用来在页面和服务器间传值的Value Object必须有setter方法 public void setPassword(String password){ } this.password = password;// 同password
public String getUsername(){ } return username;// 同password
public void setUsername(String username){ } this.username = username;BaseAction.java package com.tyland.b2b.web.base;
import com.opensymphony.xwork2.ActionSupport;
// 为了代码的灵活性和可扩展性,请声明一个BaseAction基类
// BaseAction继承Struts2的ActionSupport,因为我们想使用Struts2的一些额外帮助。// 对于ActionSupport的继承不是必须的
public class BaseAction extends ActionSupport {
private static final long serialVersionUID = ***74952195L;} UserAction.java package com.tyland.b2b.web;
import com.tyland.b2b.vo.UserVO;import com.tyland.b2b.web.base.BaseAction;
// 自定义的Action类,继承BaseAction // 由于继承了ActionSupport,我们可以使用Struts2默认的action方法execute()// 由于继承了ActionSupport,我们可以使用Struts2默认的校验方法validate()public class UserAction extends BaseAction {
private static final long serialVersionUID =-7***3684190L;
// 用来在页面和服务器之间传递用户名、密码的Value Object。变量名任意。
private UserVO userVO;
执行。// 用来在页面和服务器之间传递message变量。名称任意。private String message;//用来在页面和服务器之间传递sayHiTo变量。名称任意。private String sayHiTo;// 用来传值的变量必须有getter方法 public UserVO getUserVO(){ return userVO;}
//用来传值的变量必须有setter方法
public void setUserVO(UserVO userVO){ } this.userVO = userVO;public String getMessage(){ } return message;public void setMessage(String message){ } this.message = message;public String getSayHiTo(){ } return sayHiTo;public void setSayHiTo(String sayHiTo){ } this.sayHiTo = sayHiTo;// Override声明说明这个方法复写或实现了父类或接口方法。
// 如action-mapping中不显示指定别的方法,struts2会将execute()作为默认的action方法// 返回的SUCCESS常量,来自ActionSupport,值为“success”。
// action-mapping会根据不同的返回值采取不同的转发或页面跳转动作。@Override
public String execute()throws Exception {
} System.out.println(“******execute******”);System.out.println(userVO.getUsername()+ “ logins”);return SUCCESS;
// 在Struts2执行execute()之前,会先执行validateExecute()进行用户输入验证 // 这个方法名必须符合Struts2验证框架所规定的命名规范 public void validateExecute(){
}
} System.out.println(“******validateExecute******” + userVO.getUsername());if(null == userVO.getUsername()|| userVO.getUsername().length()< 5){ this.addFieldError(“username”, “USERNAME ERROR”);} if(null == userVO.getPassword()|| userVO.getPassword().length()< 5){ this.addFieldError(“password”, “PASSWORD ERROR”);} // 一个自定义方法。通过在action-mapping中的设置,可以实现使用POJO的自定义服务配置 public String sayHi()throws Exception {
} System.out.println(“say hi to ” + getSayHiTo());return SUCCESS;// 符合验证框架命名规范的、真对于sayHi()的验证方法 public void validateSayHi(){
} System.out.println(“******validateSayHi******” + getSayHiTo());if(null == getSayHiTo()|| getSayHiTo().length()< 5){ this.addFieldError(“sayHiTo”, “SAYHITO ERROR”);} ExampleAction.java package com.tyland.b2b.web;
import com.tyland.b2b.web.base.BaseAction;
public class ExampleAction extends BaseAction {
private static final long serialVersionUID =-***7281L;
private String message;private String sayHiTo;
public String getMessage(){
return message;}
public void setMessage(String message){
this.message = message;}
public String getSayHiTo(){
return sayHiTo;}
} public void setSayHiTo(String sayHiTo){ } this.sayHiTo = sayHiTo;public String finish(){
} System.out.println(“example finished”);setMessage(getSayHiTo());return SUCCESS;JSP代码
index.jsp <%@ page language=“java” contentType=“text/html;charset=UTF-8”
pageEncoding=“UTF-8”%>
<%@ taglib uri = “http://java.sun.com/jsp/jstl/core” prefix = “c” %>
第三篇:Struts2试题
Struts2 试题
1.struts2的执行流程?
客户端提交一个HttpServletRequest请求(action或JSP页面)
请求被提交到一系列Filter过滤器,如ActionCleanUp和FiterDispatcher等
FilterDispatcher是Struts2控制器的核心,它通常是过滤器链中的最后一个过滤器 询问ActionMapper是否需要调用某个action来处理
如果ActonMapper据诶的那个需要调用某个A传统,FilterDispatcher则把请求教到ActionProxy,由其进行处理
ActionProxy通过Configuration Manager询问框架配置文件,找到需要调用的Action类 ActionProxy创建一个ActionInvocation实例,而ActionInvocation通过代理模式调用action Action执行完毕后,返回一个result字符串,此时再按相反的顺序通过Interceptor拦截器 最后ActionInvocation负责根据struts配置文件中配置的result元素,找到与返回值对应的result
2.struts2的web里需要配置什么?
1.
3.struts.xml文件哪个常量可以实现不用重新启动服务就可以得到反馈?
4.struts中的package的作用是什么?namespace可以为空吗?如果namespace为空会意味着什么呢?
标识
可以、Timmer
如果没写,说明该包为默认空间 只要敲index没有精确对应的package都交给namespackage 如果还没有就崩溃
5.Action 的实现通集成常用那种方法? 一种是直接实现自己的Action 另一种是实现Action的接口 继承ActionSupport
6.路径设置用什么路径,绝对还是相对? 都可以
7.通配符有几种?要遵循什么原则? {1}
方法名
8.Action的属性?
path属性:指定请求访问Action的路径
type属性:指定Action的完整类名
name属性:指定需要传递给Action的ActionForm Bean scope属性:指定ActionForm Bean的存放范围
validate属性:指定是否执行表单验证
input属性:指定当表单验证失败时的转发路径。
9.用于读取资源文件,实现国际化的Struts2的标签(
10.一下关于struts的描述中,不正确的是(D)struts 是一个基于JAVA EE的MVC struts2的框架配置文件是struts-configxml struts2的控制组建核心包括核心控制和业务控制器
在struts中解决中文乱码可以通过修改struts。I18n。encoding的值为GBK来实现 11.不属于Struts2的动作类的常量返回值的是(C)
A.success
B.input
C.never
D.login 12.一下不属于Struts2动作执行的结果类型是(A)a.action b.redirect c.redirectAction d.dispatcher 13.在struts.xml中。假如在一个package中没有配置namespace,那么在其中配置一个Action名字叫做login,它的result是这样配置的
14.正规开发中,在使用struts2时定义Action使用哪种方式。B A.直接定义Action类。B.从ActionSupport继承。C.从Action继承。D.实现Action接口。
15.不同的线程在调用Action时创建几个Action实例。C A.1个 B.2个
C.每个线程创建1个。D.可以自定义配置。
16.struts2的体系结构主要包括那几个部分(A)A.模型层
B.struts控制器组件 C。struts配置文件 D。struts标签库
17.struts提供了Action接口定义了5个标准字符串常量,不包括的有(C)A SUCCESS
B NONE C REG D LOGIN 18.struts中数据验证失败时,通过(B)标签显示错误信息。A
默认结果类型是dispatcher chain:Action链式处理的结果类型,也就是将结果转发到这个action中。dispatcher:用于整合JSP的结果类型
redirect:实际上dispatcher和redirect的区别就是在于转发和重定向的区别。redirect-action:重定向action
20.简述struts2文件上传的方法。
Commons FileUpload通过将HTTP的数据保存到临时文件夹,然后Struts使用fileUpload拦截器将文件绑定到Action的实例中。从而我们就能够以本地文件方式的操作浏览器上传的文件。
21.在struts2标签内部 能否使用el表达式?
不可
22.struts2中result-type的chain和redirectAction的区别
chain,它是一个琏式的,是从一个Action转发另外一个Aciton redirectAction,是从新请求一个Action
23.
24.action的name里边写的名字是什么地方用的? From 表单action引用
25.action的class里面写的是什么? Action相应类的详细路径
26.action的extends里面调用的是什么?
从另外一个包继承、相当于拥有即承包的所有配置
27.如果不写action的后面mothod,默认的调用的方法是什么,如果写的话,写的时候有什么需要注意的?
不写会默认调用最上面的方法
28.输出值的strtus标签是什么?
s:property
28.循环的struts标签是什么?
s:iterator
29.判断的strtuts标签是什么?
s:if
30.显示文本的strtuts标签是什么?
31.要调用struts标签需要导入什么? <%@taglib uri=“/struts-tags” prefix=“s”%>
32.如果需要调用sc:datetimepicker需要导入什么,sc代表的是什么意思?
33.
获取前台文本的值
34.请简述前台提交后是如何到达后台action的。
通过from表单action找到struts配置文件相应action的类 再去找相应action类里的方法
第四篇:struts2课件
—高级软件人才实作培训专家!Struts2的启动配置在struts1.x中,struts框架是通过Servlet启动的。在struts2中,struts框架是通过Filter启动的。他在web.xml中的配置如下:
—高级软件人才实作培训专家!Struts2的配置文件Struts2默认的配置文件为struts.xml,FilterDispatcher过滤器在初始化时将会在WEB-INF/classes下寻找该文件,该文件的配置模版如下:
—高级软件人才实作培训专家!第一个Struts2应用Struts2默认的配置文件为struts.xml,FilterDispatcher过滤器在初始化时将会在WEB-INF/classes下寻找该文件,该文件的配置模版如下:
—高级软件人才实作培训专家!第一个Struts2应用例子中使用到的/WEB-INF/page/hello.jsp如下:<%@ page language=“java” pageEncoding=“UTF-8”%>
第五篇:Struts2习题
Struts2习题1 1.struts中的package的作用是什么?namespace可以为空吗?如果namespace为空会意味着什么呢?
标识、可以、如果没写,说明该包为默认空间 2.Action 的实现通集成常用那种方法? 一种是直接实现自己的Action 另一种是实现Action的接口 继承ActionSupport 3.不属于Struts2的动作类的常量返回值的是(C)
A.success
B.input
C.never
D.login 4.以下不属于Struts2动作执行的结果类型是(A)a.action b.redirect c.redirectAction d.dispatcher 5.在struts.xml中。假如在一个package中没有配置namespace,那么在其中配置一个Action名字叫做login,它的result是这样配置的
配置的
A A.在根路径后输入/login B.在根路径后输入/user/login C.在根路径后输入/login/user D.在根路径后输入/abc/login 6.正规开发中,在使用struts2时定义Action使用哪种方式。B A.直接定义Action类。B.从ActionSupport继承。C.从Action继承。D.实现Action接口。
7.不同的线程在调用Action时创建几个Action实例。C A.1个 B.2个
C.每个线程创建1个。D.可以自定义配置。
8.struts提供了Action接口定义了5个标准字符串常量,不包括的有(C)A SUCCESS
B NONE C REG D LOGIN
9.列举出action执行结果类型,并说明用途。
默认结果类型是dispatcher chain:Action链式处理的结果类型,也就是将结果转发到这个action中。
dispatcher:用于整合JSP的结果类型
redirect:实际上dispatcher和redirect的区别就是在于转发和重定向的区别。
redirect-action:重定向action 10.struts2中result-type的chain和redirectAction的区别 chain,它是一个琏式的,是从一个Action转发另外一个Aciton redirectAction,是从新请求一个Action 11.action的class里面写的是什么? Action相应类的详细路径
12.action的extends里面调用的是什么?
从另外一个包继承、相当于拥有即承包的所有配置
13.如果不写action的后面mothod,默认的调用的方法是什么,如果写的话,写的时候有什么需要注意的?
不写会默认调用最上面的方法
14.输出值的strtus标签是什么?
s:property
15.循环的struts标签是什么?
s:iterator
15.判断的strtuts标签是什么?
s:if
16.显示文本的strtuts标签是什么?
17.请简述前台提交后是如何到达后台action的。
通过form表单action找到struts配置文件相应action的类 再去找相应action类里的方法
Struts2习题2
一、填空题
1.Struts2框架由___________和___________框架发展而来。(Struts&WebWork)2.Struts2以___________为核心,采用___________的机制来处理用户的请求。(WebWork,拦截器)
3.Struts2中的控制器类是一个普通的___________。Class或填过滤器类
二、选择题
1.不属于Action接口中定义的字符串常量的是___________。B A.SUCCESS B.FAILURE
C.ERROR D.INPUT 2.在控制器类中一般需要添加相应属性的___________和___________。AC A.setter方法 B.as方法 C.getter方法 D.is方法 3.控制器需要在___________配置文件中进行配置。B A.web.xml B.struts.xml C.struts2.xml D.webwork.xml 4.不属于Struts 2表单标签库的是___________。D A.
三、简答题
1、简述什么是MVC?
MVC即模型-视图-控制器.MVC是一种设计模式,它强制把应用程序的输入处理和输出分开.视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并能接受用户的输入,但是它并不进行任何实际的业务处理。模型是应用程序的主体部分。模型表示业务数据和业务逻辑。控制器接受用户的输入并调用模型和视图去完成用户的需求
2、简述servlet的生命周期。
Servlet被服务器实例化后,容器运行其init()方法,请求到达时运行其service()方法,service()方法自动派遣运行与请求对应的doGet、doPost方法等,当服务器决定将实例销毁的时候调用其destroy方法