一、Struts 的体系结构
二、Struts 的流程
三、具体原理
1、 Struts的核心是ActionServlet,它本质上就是一个Servlet,在Web.xml中配置ActionServlet为自动启动,指明Struts的配置文件所在位置,并指明Struts中工作条件
<servlet>
<servlet-name>struts</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
//如果有多个配置文件,以逗号分隔
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>struts</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2、 当服务器容器(Tomcat)启动时,自动加载ActionServlet,并执行init()方法,init方法会用Sax解析Web.xml中指定的Struts配置文件(struts-config.xml),读取里面的action、form-bean、forward、exception及资源文件和插件信息,并将其保存到Struts相应的MAP缓存中。
<struts-config>
<data-sources/>
<form-beans>
<form-beanname="insertForm"type="com.form.InsertForm"></form-bean>
<form-beanname="updateForm"type="com.form.UpdateForm"></form-bean>
<form-beanname="searchForm"type="com.form.SearchForm"></form-bean>
</form-beans>
<global-exceptions/>
<global-forwards/>
<action-mappings>
<actionpath="/insert"name="insertForm"
type="com.action.InsertAction"input="/insert.jsp"scope="request"
validate="true">
<forwardname="search"path="/search.do"></forward>
</action>
<actionpath="/update"name="updateForm"
type="com.action.UpdateAction"input="/update.jsp"scope="request"
validate="true">
<forwardname="search"path="/search.do"></forward>
</action>
<actionpath="/search"name="searchForm"
type="com.action.SearchAction"input="/search.jsp"scope="request"
validate="true">
<forwardname="search"path="/search.jsp"></forward>
</action>
<actionpath="/delete"type="com.action.DeleteAction"
input="/search.jsp"scope="request"validate="true">
<forwardname="search"path="/search.do"></forward>
</action>
</action-mappings>
<message-resources
parameter="com.yourcompany.struts.ApplicationResources"/>
<plug-inclassName="org.apache.struts.validator.ValidatorPlugIn">
<set-propertyproperty="pathnames"
value="/WEB-INF/validator-rules.xml,
/WEB-INF/validation.xml"/>
</plug-in>
</struts-config>
3、 在客户端发来请求以后,Tomcat会检测请求的URL,如果以.do结尾,则交于Struts进行处理:Struts首先分析出请求Action的名称,然后在Action的MAP缓存中找到action的path属性与其对应的action信息,查看name属性是否指向form-bean,如果没有则跳转到第4步。如果有则在form-bean的MAP缓存中找到form-bean的name属性与此action的name属性相匹配的form-bean信息,通过form-bean的type属性找到此form-bean的实体类,利用反射机制得到此实体类的所有以set开头的属性并从URL请求参数中得到相对应的参数名称,调用实体类的set方法将其参数值注入到实体类的属性中。注入完成后检测此action信息的validte属性,如果为真,则执行form-bean的validte方法,如果验证不通过,则返回到action的input属性所指向的页面,如果通过则跳转到第4步
4、 查看此action的parameter、type方法,以确认调用Action的默认方法还是自定义方法进行业务处理,返回ActionForward对象,将响应结果返回给前台
<li>商品名称: <html:text property="bean.name" styleClass="input-text" /></li> 比如这种属性也可以直接加入到其中,ProductForm中的bean.name属性,bean为product,至于为什么可以在这样,RTFSC,
Object target = bean;
/* 881*/ int delim = findLastNestedIndex(name); //获取点字符的index,后面根据其进行属性设置。
/* 882*/ if(delim >= 0)
{
/* 884*/ try
{
/* <-MISALIGNED-> */ /* 884*/ target = getPropertyUtils().getProperty(bean, name.substring(0, delim));
}/* 887*/ catch(NoSuchMethodException e)
{
/* <-MISALIGNED-> */ /* 887*/ return;
}
/* <-MISALIGNED-> */ /* 889*/ name = name.substring(delim + 1);
/* <-MISALIGNED-> */ /* 890*/ if(log.isTraceEnabled())
{
/* <-MISALIGNED-> */ /* 891*/ log.trace(" Target bean = " + target);
/* <-MISALIGNED-> */ /* 892*/ log.trace(" Target name = " + name);
}
}/* 897*/ String propName = null;
private int findLastNestedIndex(String expression)
{
/*1035*/ int bracketCount = 0;
/*1036*/ for(int i = expression.length() - 1; i >= 0; i--)
{/*1037*/ char at = expression.charAt(i);
/*1038*/ switch(at)
{
/* <-MISALIGNED-> */ /*1035*/ default:
break;
/* <-MISALIGNED-> */ /*1040*/ case 46: //点“.”字符
/* <-MISALIGNED-> */ /*1040*/ if(bracketCount < 1)
/* <-MISALIGNED-> */ /*1041*/ return i;
break;
/*1048*/ case 40: /*1048*/ case 91: /*1048*/ bracketCount--;
break;
/*1054*/ case 41: /*1054*/ case 93: /*1054*/ bracketCount++;
break;
}
}
/*1059*/ return -1;
}
调用栈片段,红色部分为属性设置部分
Product.setName(String) line: 1258
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 606
PropertyUtilsBean.invokeMethod(Method, Object, Object[]) line: 1773
PropertyUtilsBean.setSimpleProperty(Object, String, Object) line: 1759
PropertyUtilsBean.setNestedProperty(Object, String, Object) line: 1648
PropertyUtilsBean.setProperty(Object, String, Object) line: 1677
BeanUtilsBean.setProperty(Object, String, Object) line: 1022
BeanUtilsBean.populate(Object, Map) line: 811
BeanUtils.populate(Object, Map) line: 298
RequestUtils.populate(Object, String, String, HttpServletRequest) line: 493
RequestProcessor.processPopulate(HttpServletRequest, HttpServletResponse, ActionForm, ActionMapping) line: 805
RequestProcessor.process(HttpServletRequest, HttpServletResponse) line: 203
ActionServlet.process(HttpServletRequest, HttpServletResponse) line: 1194
ActionServlet.doPost(HttpServletRequest, HttpServletResponse) line: 432
ActionServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 637
ActionServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 717
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
OrgFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 204
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
LoginFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 133
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
SQLFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 52
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
FgPageFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 111
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
SiteMeshFilter(ContentBufferingFilter).bufferAndPostProcess(FilterChain, HttpServletRequest, HttpServletResponse, Selector) line: 169
SiteMeshFilter(ContentBufferingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 126
MyConfigurableSiteMeshFilter(ConfigurableSiteMeshFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 163
MyConfigurableSiteMeshFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 81
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
AccountRightChooseFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 98
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
StoreLoginFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 104
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
SetCharacterEncodingFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 142
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
StandardWrapperValve.invoke(Request, Response) line: 233
StandardContextValve.invoke(Request, Response) line: 191
StandardHostValve.invoke(Request, Response) line: 127
ErrorReportValve.invoke(Request, Response) line: 103
StandardEngineValve.invoke(Request, Response) line: 109
CoyoteAdapter.service(Request, Response) line: 293
Http11AprProcessor.process(long) line: 879
Http11AprProtocol$Http11ConnectionHandler.process(long) line: 600
AprEndpoint$Worker.run() line: 1703
Thread.run() line: 745
五、SpringMVC中也支持表单对象,调用栈如下,
Product.setProductId(String) line: 13
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 606
BeanWrapperImpl$BeanPropertyHandler.setValue(Object, Object) line: 344
BeanWrapperImpl(AbstractNestablePropertyAccessor).setPropertyValue(AbstractNestablePropertyAccessor$PropertyTokenHolder, PropertyValue) line: 454
BeanWrapperImpl(AbstractNestablePropertyAccessor).setPropertyValue(PropertyValue) line: 280
BeanWrapperImpl(AbstractPropertyAccessor).setPropertyValues(PropertyValues, boolean, boolean) line: 95
ExtendedServletRequestDataBinder(DataBinder).applyPropertyValues(MutablePropertyValues) line: 810
ExtendedServletRequestDataBinder(DataBinder).doBind(MutablePropertyValues) line: 706
ExtendedServletRequestDataBinder(WebDataBinder).doBind(MutablePropertyValues) line: 189
ExtendedServletRequestDataBinder(ServletRequestDataBinder).bind(ServletRequest) line: 106
ServletModelAttributeMethodProcessor.bindRequestParameters(WebDataBinder, NativeWebRequest) line: 150
ServletModelAttributeMethodProcessor(ModelAttributeMethodProcessor).resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory) line: 110
HandlerMethodArgumentResolverComposite.resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory) line: 78
ServletInvocableHandlerMethod(InvocableHandlerMethod).getMethodArgumentValues(NativeWebRequest, ModelAndViewContainer, Object...) line: 162
ServletInvocableHandlerMethod(InvocableHandlerMethod).invokeForRequest(NativeWebRequest, ModelAndViewContainer, Object...) line: 129
ServletInvocableHandlerMethod.invokeAndHandle(ServletWebRequest, ModelAndViewContainer, Object...) line: 111
RequestMappingHandlerAdapter.invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 806
RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 729
RequestMappingHandlerAdapter(AbstractHandlerMethodAdapter).handle(HttpServletRequest, HttpServletResponse, Object) line: 85
DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse) line: 959
DispatcherServlet.doService(HttpServletRequest, HttpServletResponse) line: 893
DispatcherServlet(FrameworkServlet).processRequest(HttpServletRequest, HttpServletResponse) line: 970
DispatcherServlet(FrameworkServlet).doPost(HttpServletRequest, HttpServletResponse) line: 872
DispatcherServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 641
DispatcherServlet(FrameworkServlet).service(HttpServletRequest, HttpServletResponse) line: 846
DispatcherServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 722
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 305
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 330
FilterSecurityInterceptor.invoke(FilterInvocation) line: 118
FilterSecurityInterceptor.doFilter(ServletRequest, ServletResponse, FilterChain) line: 84
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
ExceptionTranslationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 113
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
SessionManagementFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 103
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
AnonymousAuthenticationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 113
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
SecurityContextHolderAwareRequestFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 154
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
RequestCacheAwareFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 45
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
ZGUsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 199
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
LogoutFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 110
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
WebAsyncManagerIntegrationFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 50
WebAsyncManagerIntegrationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
SecurityContextPersistenceFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 87
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
HeicheAPIFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 43
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 342
FilterChainProxy.doFilterInternal(ServletRequest, ServletResponse, FilterChain) line: 192
FilterChainProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 160
DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain) line: 346
DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 262
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
CharacterEncodingFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 85
CharacterEncodingFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
BasePathFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 38
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
StandardWrapperValve.invoke(Request, Response) line: 222
StandardContextValve.invoke(Request, Response) line: 123
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 472
StandardHostValve.invoke(Request, Response) line: 168
ErrorReportValve.invoke(Request, Response) line: 99
AccessLogValve.invoke(Request, Response) line: 929
StandardEngineValve.invoke(Request, Response) line: 118
CoyoteAdapter.service(Request, Response) line: 407
Http11AprProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1002
Http11AprProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 585
AprEndpoint$SocketProcessor.run() line: 1813
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1145
ThreadPoolExecutor$Worker.run() line: 615
TaskThread(Thread).run() line: 745
表单对象中的属性会set进对应的对象,如下代码
public class Product {
private String productId;
private int quantity;
private double listPrice;
private double unitPrice;
private boolean status;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getListPrice() {
return listPrice;
}
public void setListPrice(double listPrice) {
this.listPrice = listPrice;
}
public double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
/**
*
* @param product
* @return
* @throws JsonProcessingException
*/
@RequestMapping("product/edit")
@ResponseBody
public Map<String, Object> productEdit(Product product)
throws JsonProcessingException {
return this.storeService.updateStoreProduct(JSONUtils
.writeValueAsString(product));
}
<form action="store/product/edit" method="post" accept-charset="gbk">
<input name="productId" type="hidden" value="${p.productId}">
<div class="card_cont seller">
<div class="add_list_ul">
<div class="login_int radio">
<div class="fl">商品状态</div>
<label><span class="fl">上架</span> <input name="status"
value="true" type="radio" [#if p.status]checked="checked"
[/#if] name="State" class="radio_1" required="required">
</label> <label><span class="fl">下架</span> <input name="status"
value="false" type="radio" [#if !p.status]checked="checked"
[/#if] name="State" class="radio_1" required="required">
</label>
<!-- <p class="red">提示:促销中的商品不能下架</p> -->
</div>
<div class="seller_list short">
<span>市场价格</span> <input type="text" value="${p.listPrice?c}" name="listPrice"
placeholder="请输入市场价格" required="required">
<i class="radio_1 "></i>
<p class="fl">元</p>
</div>
<div class="seller_list short">
<span>商城价格</span> <input type="text" value="${p.unitPrice?c}" name="unitPrice"
placeholder="请输入商城价格" required="required" max="${p.listPrice?c}" data-msg-max=" ">
<i class="radio_1"></i>
<p class="fl">元</p>
</div>
<div class="seller_list short">
<span>库存</span> <input type="number" name="quantity"value="${p.quantity?c}"
placeholder="请输入库存" required="required">
<i class="radio_1"></i>
<p class="fl"></p>
</div>
</div>
</div>
</form>