Struts2
特性
Servlet是单例的,多个程序访问同一个Servelt只会创建一个Servlet实例.Action是多例的,一次请求就创建一个实例,故在Action中创建成员变量不会出现线程安全.
系统架构
- FilterDispatcher:整个Struts2的调度中心,根据ActionMapper的结果来决定是否处理请求,如果ActionMapper指出该URL应该被Struts2处理,那么它将会执行Action处理,并停止过滤器链上还没有执行的过滤器。
- ActionMapper:判断这个请求是否应该被Struts2处理,如果需要Struts2处理,ActionMapper会返回一个对象来描述请求对应的ActionInvocation的信息。
- ActionProxy:创建一个ActionInvocation实例,位于Action和xwork之间,使得我们在将来有机会引入更多的实现方式,比如通过WebService来实现等。
- ConfigurationManager:xwork配置的管理中心,可以把它看做struts.xml这个配置文件在内存中的对应。
- struts.xml:是Stuts2的应用配置文件,负责诸如URL与Action之间映射关系的配置、以及执行后页面跳转的Result配置等。
- ActionInvocation:真正调用并执行Action,它拥有一个Action实例和这个Action所依赖的拦截器实例。ActionInvocation会按照指定的顺序去执行这些拦截器、Action以及相应的Result。
- Interceptor (拦截器):是Struts2的基石,类似于JavaWeb的Filter,拦截器是一些无状态的类**,拦截器可以自动拦截Action**,它们给开发者提供了在Action运行之前或Result运行之后来执行一些功能代码的机会。
- Action:用来处理请求,封装数据。
初始化
Struts2框架初始化搭建运行环境,这是为驱动框架核心功能的准备工作.
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
private static final Logger LOG = LogManager.getLogger(StrutsPrepareAndExecuteFilter.class);
// Http 预处理类
protected PrepareOperations prepare;
// Http 处理执行类
protected ExecuteOperations execute;
// 不进行处理的Url
protected List<Pattern> excludedPatterns = null;
public StrutsPrepareAndExecuteFilter() {
}
// 实现继承自父类 Filter 的init 方法
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化操作类
InitOperations init = this.createInitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
// 初始化核心分发器 Dispatcher
dispatcher = init.initDispatcher(config);
// 静态资源加载器
init.initStaticContentLoader(config, dispatcher);
// 初始化预处理类以及执行类
this.prepare = this.createPrepareOperations(dispatcher);
this.execute = this.createExecuteOperations(dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
this.postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
- 在init()方法中遍历从filterConfig 中获取参数集合,存放到Map中并最终封装成Dispatcher对象,其中filterConfig中的参数集合来自于web.xml文件中的初始化参数配置。
- 核心分发器Dispatcher 为主对各种元素进行转换
- 配置元素的加载器,负责把配置元素转换成框架元素;
- 配置元素的构造器,负责对框架元素进行初始化操作,使用构造模式;
- 配置元素的管理类,负责管理和控制配置元素的初始化;
配置文件加载
- 核心控制器的实质是过滤器,Struts2中的过滤器会在服务器启动时创建。过滤器中的
init();
方法被调用; - 过滤器中的
init();
被Dispatcher 加载,配置文件被调用,加载struts2中的所有配置文件;
public class Dispatcher {
public void init() {
if (this.configurationManager == null) {
this.configurationManager = this.createConfigurationManager("default");
}
try {
this.init_FileManager();
this.init_DefaultProperties(); // [1]
this.init_TraditionalXmlConfigurations(); // [2]
this.init_LegacyStrutsProperties(); // [3]
this.init_CustomConfigurationProviders(); // [5]
this.init_FilterInitParameters(); // [6]
this.init_AliasStandardObjects(); // [7]
Container container = this.init_PreloadConfiguration();
container.inject(this);
this.init_CheckWebLogicWorkaround(container);
if (!dispatcherListeners.isEmpty()) {
Iterator i$ = dispatcherListeners.iterator();
while(i$.hasNext()) {
DispatcherListener l = (DispatcherListener)i$.next();
l.dispatcherInitialized(this);
}
}
this.errorHandler.init(this.servletContext);
} catch (Exception var4) {
LOG.error("Dispatcher initialization failed", var4);
throw new StrutsException(var4);
}
}
}
-
init_DefaultProperties();——org/apache/struts2/default.properties这是struts2中定义的所有常量属性;
-
init_Traditiona;XmlConfigurations();——struts-default.xml(其中定义了拦截器以及需要继承的包),struts-plugin.xml(插件的配置文件),struts.xml;
struts-default.xml默认栈的拦截器会依次执行,每个拦截器都有特有的功能.
[外链图片转存中…(img-yhNMK4hX-1571817771585)]
workflow拦截器:Strut2会提供一个数据存储的区域,其中有错误信息存储的区域.出现错误时将错误信息填装到错误信息区域.**其中最后一个拦截器会检查错误区域,**没有错误会跳转到Action页面,出现错误跳转到input页面视图.(配置input页面,将出错引向Error页面)
-
init_LegacyStrutsProperties();——加载struts.properties用户自定义的文件;
-
init_CustomConfigurationProiders();——加载配置提供类;
-
init_FilterInitParameters();——加载web.xml中过滤器初始化值;
-
init_AliasStandardObjects();——加载Bean对象
所以,文件加载顺序为:
但是,后配置的常量的值会覆盖先配置的常量的值
运行流程
- 用户发出请求
- 请求到达核心过滤器.StrutsPrepareAndExecuteFilter,该过滤器有俩个主要方法,
init()
和doFilter()
方法. (在Struts2.1以前调用FilterDispatcher,Struts2.1以后调用StrutsPrepareAndExecuteFilter )- init():开始执行初始化操作
- doFilter():创建
ActionContext()
,包装request
对象.AtionMapper通过FilterDispatcher对象创建一个Action的代理.- 初始化时会将Action中的name属性以及class属性绑定存储带一个map对象,ActionMapper会加载这个对象通过反射找到指定Action.
- 执行
invocation.invoke()
方法,即ActionInvocation.由此真正调用并执行Struts2.- 递归依次执行拦截器,直到结束调用Action;
- Action会提供一个Result返回值,根据Result模板类型提供页面类型;
- 递归逆序执行拦截器
- 到达页面做出相应,由Result给出response响应到页面.
拦截器.
相较于Filter过滤器拦截从客户端发向服务器的请求,Interceptor拦截器拦截的时客户点对Action的访问,该拦截为更细粒化的拦截,可具体拦截到某一方法.
数据封装
访问servlet
seruts2于servlet是解耦合的,如果使用到servlet中的API对象,例如response、session,需要访问servlet的API有三个方法。
- 完全解耦合的方式
- 使用servelt的原生API方式
- 接口注入方式
完全解耦合
方法:利用Struts2中的对象ActionContext.当请求过来时,执行过滤器方法中创建了ActionContext方法.即Action上下文.可以通过ActionContext方法获得值栈对象.
ActionContext context = ActionContext.getContext();
ActionContext方法 | 作用 |
---|---|
getParameters() | 接收参数 |
put(key,value) | 设置参数 |
getSession() | 获取session |
getApplication() | 获取Application |
getContext().getValueStack() | 获得值栈 |
这种方法只能获得代表request,session,application的数据的map集合,不能操作这些对象.
servelt的原生API
方法:通过ServletActionContext方法提供的的一些静态方法获得原生API.
ServletActionContext方法 | 作用 |
---|---|
getResponse() | 获得response对象 |
getrequest() | 获得request对象 |
getPagrContext() | 获得PagrContext对象 |
既可以操作域对象的数据,同时也可以获得域对象本身的方法
接口注入
实现ServletResquestAware,ServletContextAware接口,通过接口中提供的方法设置值
转发重定向
分俩种,全局配置全部执行,或是局部执行
-
全局页面结果配置:
-
局部页面结果配置:
-
type属性:
-
dispatcher:默认值,Action请求转发JSP;
-
redierct:Action重定向JSP;
-
chain:Action请求转发Action;
-
redirectAction:Action重定向Action;
-
stream:Struts2提供的文件下载
-
-
简单数据封装
前端页面发送的数据由后台获取.
属性驱动
方法一:提供set方法
- 在页面添加类的各个属性
- 为属性添加get,set方法
方法二:页面提供表达式
-
在页面form传输的数据中为每一个属性加上类名
-
在Action页面提供一个User对象,然后配置get(),set()方法.这是因为拦截器需要进行数据封装,使用
模型驱动
- 实现模型驱动的接口ModelDriven
- 实现一个Model()方法,返回一个实体类,手动提供一个成员变量实例对象
复杂数据封装
即前端一次性发送一组数据,将数据封装成数组发送
封装为List
- 前端将发送的数据name属性设置为数组格式;
- Action添加实例对象的List集合,以及对应的get,set方法;
封装为Map
- 前端将发送的数据name属性设置为map[“key”]格式;
- Action添加实例对象的map集合,以及对应的get,set方法;
值栈
struts2将Xwork对ognl扩展这一套机制封存起来,这个对象叫ValueStack.字面意思为值栈**.Struts2中使用OGNL将请求ACtion的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中独享属性值.**
- ValueStack类似于一个数据中转站(Struts框架中的数据都保存到值栈中);
- Action一旦创立,相应就创建一个ValueStack实例;
- 一处保存可以在任何地方读取.
内部结构
ValueStack中俩快主要的区域:
- root区域:其实就是一个ArrayList,里面一般放置对象;
- 获取root区域数据不需要加#
- context区域:其实就是一个Map.里面放置的是web开发过程中对象的引用.
- 获取context区域数据需要加#
- request
- session
- application
- parameters
- attr
- 获取context区域数据需要加#
获得值栈
通过ActionContext
- 过滤器在接收请求时,创建ActionContext对象,期间建立对ValueStack的引用
ValueStack stack = ActionContext.getContext().getValueStack();
通过request
创建Action代理将ValueStack放置到request中
ValueStack stack = (ValueStack)ActionContext.etRequest().getAttribute(ServletAction);
操作数据
存储数据
通过Action中的get()
此方法是利用Action本身在值栈中的特性.在默认情况下,将Action对象压入到值栈中,Action中的属性也随之压入,提供一个get()方法让值栈提取值.
调用ValueStack方法
push(Object obj);
set(String key,Object obj);
手动使用方法将对象压入栈顶位置.
获取数据
通过ognl的方式
- root:不需要使用#
- context:使用#
通过el表达式
容器学习
在框架中,容器被设计为在系统初始化时就进行自身的初始化,并且能够在系统全局任何编程层次中进行访问的对象。通过它可以做到对对象的创建以及引用,对象以及本身子的依赖处理。
-
核心分发器 Dispatcher : Dispatcher 不属于XWork框架的组成,但是是XWork框架的调用者和驱动执行者,在运行过程中起着重要的作用。
-
控制流体系 (ActionProxy, ActionInvocation, Inteceptor, Action, Result)
- Interceptor和Action以及Result作为事件处理节点,是处理事件逻辑的主要地方;
- ActionProxy和ActionInvocation负责对这些事件处理节点进行调用;
- 这里,ActionProxy主要是XWork框架和外部环境,为框架内部元素提供一个环境的同时也提供外部调用XWork内部元素的接口,即负责提供一个执行环境。
- ActionInvocation作为核心调度器,主要负责的就是对事件处理节点进行调用。
-
数据流体系 (ActionContext, ValueStack)ActionContext负责数据的储存和数据的共享
,ValueStack 在ActionContext的基础之上,负责数据的操作。
- ValueStack是ActionContext的内部对象 ;
- ActionContext中封装了许多对框架内部对象的访问接口 ;
- ActionContext中封装了许多对框架内部对象的访问接口,主要分成两种
- 对XWork框架对象的访问,getContainer,getValueStack,getActionInvocation;
- 对数据对象的访问, getSession,getApplication,getParameters等 ;
当connecter传递来包装后的请求进入Xwork容器
功能
对象创建以及依赖注入有俩大功能: 对象创建以及依赖注入
创建对象
public interface Container extends Serializable {
String DEFAULT_NAME = "default";
/*处理对象依赖
* 作用:建立起任意对象到框架元素沟通的桥梁
* 入参:被注入依赖的对象
* 实现
* 1.扫描传入的对象中声明有 @Inject 注解的字段,方法,构造函数,方法参数.
* 2.将它们注入容器托管对象,实现任意对象与容器对象之间的关系。
*/
void inject(Object var1);
<T> T inject(Class<T> var1);
/*获得对象实例
* 获取范围:
* 1.声明在XML文件中的 <bean> 标签(包括框架内部bean以及自定义的bean)
* 2.<constant> 标签
*/
<T> T getInstance(Class<T> var1, String var2);
<T> T getInstance(Class<T> var1);
//对一个接口的多个不同实现类之间的实例获取的管理
Set<String> getInstanceNames(Class<?> var1);
//处理对象作用范围
void setScopeStrategy(Strategy var1);
void removeScopeStrategy();
}
在Container实现类中包含俩个主要的数据结构 Map, factories. 由构造函数发现, factoryNamesByType 是通过遍历 factories 得到的。 factories 即 InternalFactory<?>,factories 由此获得key值
class Key<T> {
final Class<T> type;
/*
* key是由name以及hashCode构成
* 对应<bean> 标签中声明的 name 和 type 属性
*/
final String name;
final int hashCode;
private Key(Class<T> type, String name) {
if(type == null) {
throw new NullPointerException("Type is null.");
} else if(name == null) {
throw new NullPointerException("Name is null.");
} else {
this.type = type;
this.name = name;
this.hashCode = type.hashCode() * 31 + name.hashCode();
}
}
Class<T> getType() {
return this.type;
}
String getName() {
return this.name;
}
public int hashCode() {
return this.hashCode;
}
public boolean equals(Object o) {
if(!(o instanceof Key)) {
return false;
} else if(o == this) {
return true;
} else {
Key other = (Key)o;
return this.name.equals(other.name) && this.type.equals(other.type);
}
}
public String toString() {
return "[type=" + this.type.getName() + ", name='" + this.name + "']";
}
static <T> Key<T> newInstance(Class<T> type, String name) {
return new Key(type, name);
}
}
而InternalFactory 只是定义了一个方法的接口,声明了对象的创建方法, 得知 factories 中存储的是对象的实例构建方法。
interface InternalFactory<T> extends Serializable {
/*
* @param context of this injection
*
* @return instance to be injected
*/
T create(InternalContext var1);
}
依赖注入
在容器实现类ContainerImpl 中,**利用Map存储对象以及对象之间的依赖关系. **通过扫描对象中具有 @Inject 注解的字段或方法,通过该标志初始化相应的注入器,放入到 ContainerImpl 中的 ReferenceCache 类型中的 injectors 中,供之后的对象依赖注入使用。 @Inject 注解在框架内部作为一个标志,被转换成不同的 Injectot 对象, 从而实施依赖注入。
本章借鉴于 https://www.cnblogs.com/forwrader/p/7659708.html