Struts2 is divided into two parts: one is struts2 system initialization, the other is STRUTS2 processing request, respond to request.
The following is a personal understanding of STRUTS2 's request processing process:
The following is the main code in the Dofilter method of the Strutsprepareandexecutefilter filter:
public void Dofilter (ServletRequest req, servletresponse Res, Filterchain chain) throws IOException, servletexception {
httpservletrequest request = (httpservletrequest) req;
HttpServletResponse response = (httpservletresponse) res;
try {
Prepare.setencodingandlocale (request, response);
Prepare.createactioncontext (request, response);
Prepare.assigndispatchertothread ();
if (excludedpatterns!= null && prepare.isurlexcluded (Request, Excludedpatterns)) {
Chain.dofilter ( request, response);
} else {
request = prepare.wraprequest (request);
actionmapping mapping = prepare.findactionmapping (Request, response, true);
if (mapping = = null) {
Boolean handled = execute.executestaticresourcerequest (request, response);
if (!handled) {
chain.dofilter (request, response);
}
} else {
execute.executeaction (request, response, mapping);
}}}
At the time of system initialization, the Strutsprepareandexecutefilter init method was executed, and the Prepareoperations and executeoperations two objects were instantiated. The first object is the encapsulation of some of the preparation operations that were made prior to the actual response request, and the executeoperations is the encapsulation of the response request, but in fact both objects ultimately invoke the method of the core Distributor dispatcher object.
Prepare.setencodingandlocale (request, response); This sentence processing requests the code and the response locale, its internal call is the Dipatcher prepare method, the following is the source code:
public void Setencodingandlocale (HttpServletRequest request, httpservletresponse response) {
Dispatcher.prepare ( request, response);
}
The logic is that the request.setcharacterencoding () method is not invoked if the I18N encoding is written in the Struts2 configuration file using the encoding in the configuration file. As for the response locale value and encoding principle is the same, but the specific logic source a little more, but not difficult, here is not to explain, we should all understand.
Prepare.createactioncontext (request, response);
Public Actioncontext Createactioncontext (httpservletrequest request, httpservletresponse response) {
Actioncontext CTX;
Integer counter = 1;
Integer oldcounter = (integer) request.getattribute (cleanup_recursion_counter);
if (oldcounter!= null) {
counter = oldcounter + 1;
}
Actioncontext Oldcontext = Actioncontext.getcontext ();
if (Oldcontext!= null) {
//Because the project is generally not a distributed application, it will not execute here
CTX = new Actioncontext (new hashmap<string, object> ( Oldcontext.getcontextmap ());
} else {//Here is the code that will execute
valuestack stack = Dispatcher.getcontainer (). getinstance (Valuestackfactory.class). Createvaluestack ();
Stack.getcontext (). Putall (Dispatcher.createcontextmap (request, response, NULL, ServletContext));
CTX = new Actioncontext (Stack.getcontext ());
}
Request.setattribute (Cleanup_recursion_counter, COUNTER);
Actioncontext.setcontext (CTX);
return CTX;
}
Valuestack stack = Dispatcher.getcontainer (). getinstance (Valuestackfactory.class). Createvaluestack (); Get the valuestackfactory from the Struts2 container and create the Valuestack
Stack.getcontext (). Putall (Dispatcher.createcontextmap (request, response, NULL, ServletContext)); Copy the data from the Actioncontext to the valuestack, so we can get all the data we want from Actioncontext and Valuestack.
Dispatcher.createcontextmap
Public map<string,object> Createcontextmap (httpservletrequest request, httpservletresponse response,
Actionmapping mapping, ServletContext context) {
//Request Map wrapping the HTTP request Objects
Map Requestmap = New Requestmap (request);
Parameters Map wrapping the HTTP parameters. Actionmapping parameters are now handled and applied separately
Map params = new HashMap (Request.getparametermap ()); c6/>//Session Map wrapping the HTTP session
Map session = new Sessionmap (request);
Application Map Wrapping the ServletContext
map application = new Applicationmap (context);
map<string,object> Extracontext = Createcontextmap (Requestmap, params, session, application, request, response, context);
if (mapping!= null) {
extracontext.put (servletactioncontext.action_mapping, mapping);
}
return extracontext;
}
This method transforms the servlet native Request,parameters,session,servletcontext into a map, The converted map and the native Servlet object are then passed into the dispatcher another overloaded Createcontextmap method, following its source code:
Public hashmap<string,object> createcontextmap (map Requestmap, map Parametermap,
Map Sessionmap, Map Applicationmap,
HttpServletRequest request, HttpServletResponse response, ServletContext servletcontext) {hashmap<string,object> extracontext = new Hashmap<string,object> ()
;
Extracontext.put (Actioncontext.parameters, New HashMap (Parametermap));
Extracontext.put (Actioncontext.session, Sessionmap);
Extracontext.put (Actioncontext.application, Applicationmap);
Locale Locale;
if (Defaultlocale!= null) {locale = Localizedtextutil.localefromstring (Defaultlocale, Request.getlocale ());
else {locale = Request.getlocale ();
} extracontext.put (Actioncontext.locale, LOCALE); Extracontext.put (Actioncontext.dev_mode, Boolean.valueoF (DevMode));
Extracontext.put (Strutsstatics.http_request, REQUEST);
Extracontext.put (Strutsstatics.http_response, RESPONSE);
Extracontext.put (Strutsstatics.servlet_context, ServletContext);
Helpers to get access to request/session/application scope extracontext.put ("request", Requestmap);
Extracontext.put ("session", Sessionmap);
Extracontext.put ("Application", Applicationmap);
Extracontext.put ("Parameters", Parametermap);
AttributeMap attrmap = new AttributeMap (extracontext);
Extracontext.put ("attr", Attrmap);
return extracontext; }
The main method is to save the converted map and the servlet native object plus a attributemap into a large map, and then return.
In this way, we can get the transformed map and get the servlet native object.
Now back to the Prepare.createactioncontext method.
Actioncontext.setcontext (CTX); Put the created Actioncontext object into the threadlocal<t>, bind to the current line Cheng easily get the Actioncontext object elsewhere.
Add to the core filter, Prepare.assigndispatchertothread (), from the name of the method know is to bind the Dispatcher object to the current thread, that is, put into a Threadlocal<t> object, This is done to solve the problem of multithreaded concurrent access because the Dispathcer object creates only one, the code is created in the Strutsprepareandexecutefilter init method, and the Init method executes only once, Of course, dispatcher object is only one, and Web applications are inherently a multi-threaded environment, so put dispatcher into the threadlocal<t> is the best choice. The reason for putting the Actioncontext object into the threadlocal<t> is different here, because every time a request arrives, the system creates a Actioncontext object for it, which is a actioncontext that is unique to the request. , there is no problem of multithreading, so put the object into the threadlocal<t> for convenience.
if (excludedpatterns!= null && prepare.isurlexcluded (Request, excludedpatterns)) {
Chain.dofilter (request, response);
The decision is made because STRUTS2 supports URL pattern matching that is not included, and a request is entered into the Struts2 filter, but if the URL of the request is not included, Sturts2 simply calls the Chain.dofilter method to release. Other cases enter the else part and invoke the action processing request.
Request = Prepare.wraprequest (request); HttpServletRequest Packaging, See its source code can know if it is file upload httpservletrequest packaging into a Multipartrequestwrapper object, the object specifically processing file upload request. If the file is not uploaded into the HttpServletRequest wrapper into the Strutsrequestwrapper,strutsrequestwrapper class overrides the GetAttribute method of its parent class, the behavior of the method is modified slightly, The GetAttribute method in its parent class, Javax.servlet.http.HttpServletRequestWrapper, simply looks for the specified attribute from the request object, and the Strutsrequestwrapper getattribute method is first Search in the Request object, if not found will also go to the valuestack to find, the following source is proof:
If not found, then try the Valuestack
ctx.put ("__requestwrapper.getattribute", boolean.true);
Valuestack stack = Ctx.getvaluestack ();
if (stack!= null) {
attribute = Stack.findvalue (s);
}
This is why it is possible to use an EL expression in a JSP page to access the properties of a value in Valuestack.
actionmapping mapping = prepare.findactionmapping (Request, response, true); there's nothing to say about it, it's getting action mapping information.
If the requested static page executes execute.executestaticresourcerequest (request, response), the method internally determines whether or not it is capable of processing the request. If there is no processing is called Chain.dofilter release, the implementation of basically all the same is returned to the client.
The real action is the sentence: execute.executeaction (request, response, mapping); The method calls the dispatcher Serviceaction method, and we go into the method to see, The following are important code in this method:
Configuration config = configurationmanager.getconfiguration ();
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 and do it!
if (Mapping.getresult ()!= null) {result result
= Mapping.getresult ();
Result.execute (Proxy.getinvocation ());
} else {
proxy.execute ();
}
If there is a previous value stack then set it back onto the request
if (!nullstack) {
Request.setattribute ( Servletactioncontext.struts_valuestack_key, stack);
Get the Configuration object first, get the container object by configuration, get the Actionproxyfactory,actionproxy factory class from the container, and then create the Actionproxy, It is known that the STRUTS2 is packed inside the webwork frame, and Actionproxy is the dividing line between Sturts and WebWork, Actionproxy is the portal to the WebWork framework.
Struts2 uses the defaultactionproxyfactory to create Actionproxy objects by default, and the following is the Createactionproxy method:
Public Actionproxy Createactionproxy (string namespace, String ActionName, String methodname, Map<string, object> Extracontext, Boolean Executeresult, Boolean cleanupcontext) {
actioninvocation inv = new Defaultactioninvocation ( Extracontext, true);
Container.inject (INV);
Return Createactionproxy (Inv, namespace, ActionName, MethodName, Executeresult, cleanupcontext);
Public Actionproxy Createactionproxy (actioninvocation Inv, string namespace, String ActionName, String methodname, Boolean Executeresult, Boolean cleanupcontext) {
Defaultactionproxy proxy = new Defaultactionproxy (Inv, namespace, ActionName, MethodName, Executeresult, cleanupcontext);
Container.inject (proxy);
Proxy.prepare ();
return proxy;
This is a two overloaded method, the first method calls the second method, creates the Actioninvocation object in the first method, injects the object it relies on using a container, and immediately creates the Actionproxy object and injects its dependent objects. If you objectfctory,configuration objects, and so on. And then using the Proxy.prepare () method, which has a resolvemethod () method, the simple way is to assign the method property value to execute when the action is configured without specifying the methods property. That's why execute is the default execution method for action. There is also a very important method called Invocation.init (this), that is, call the Actioninvocation init method, the Actionproxy itself passed, Of course, the actionproxy is cached in the actioninvocation.
The default implementation of STRUTS2 for Actioninvocation is the Defaultactioninvocation class, where the Init method for the class is placed, and the following are the important code in the method:
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<interceptormapping> ; Interceptorlist = new Arraylist<interceptormapping> (Proxy.getconfig (). Getinterceptors ());
Interceptors = Interceptorlist.iterator ();
The first sentence creates the Action, and if you know something about the Struts2 object creation, the Action,result,intercepter creation is created by the Objectfactory Buildbean method, The inside is called class.newinstance (), so the action must have an parameterless construct method.
Note this: Stack.push (action), where the created action is pressed into the valuesstack, which is why the default action is on top of the stack. The following is all the interceptors that get the action configuration and are cached in Actioninvocation, as is the action, because the execution of action and Interceptor is implemented by Actioninvocation.
Now go back to Dispatcher's Serviceaction method, create the next code after the Actionproxy object and put the Valuestack object into the request, This means that we can also get through the HttpServletRequest object, as long as we know the key on the line.
Proxy.execute (); This sentence executes the Actionproxy Execute method, Struts2 Actionproxy's default implementation is Strutsactionproxy, following the Execute method for the class, the source code is as follows:
Public String Execute () throws Exception {
Actioncontext previous = Actioncontext.getcontext ();
Actioncontext.setcontext (Invocation.getinvocationcontext ());
try {
//the The new API://Return requestcontextimpl.callincontext (invocation, New callable< String> () {
//public String call () throws Exception {
//return Invocation.invoke ();
// }
// });
return Invocation.invoke ();
} Finally {
if (cleanupcontext)
Actioncontext.setcontext (previous);
}
}
The code is very simple, is called actioninvocation invoke method, execution interceptor and action, the following is the Invoke method source code, because this method is more than the attachment, here only picked up the important code:
if (Interceptors.hasnext ()) {
final interceptormapping interceptor = (interceptormapping) interceptors.next ();
String interceptormsg = "Interceptor:" + interceptor.getname ();
Utiltimerstack.push (interceptormsg);
try {
ResultCode = Interceptor.getinterceptor (). Intercept (defaultactioninvocation.this);
}
finally {
utiltimerstack.pop (interceptormsg);
}}
else {
ResultCode = invokeactiononly ();
}
Here's some code
omitted ... if (Proxy.getexecuteresult ()) {
executeresult ();
}
The display here is the execution of all interceptors in the interceptor stack, and when the interceptor is executed, the corresponding method is executed according to the configuration execution, and the system is followed by a resultcode string to find the corresponding result.
Compact is the execution of the Executeresult method, which creates the corresponding result object based on the result configuration, and executes the Execute method of result, such as the most frequently used Servletdispatcherresult, The Execute method is primarily to invoke the Dispatcher.forward (request, Response) method, return a page for Tomcat parsing, and then render the parsed content to the client browser.
At this point, struts2 the entire implementation process is basically finished, if there are errors, please correct me.
The following upload is an individual for struts2 execution process drawing a sequence diagram, interested can see, because the picture is too big to enlarge to see clearly, hope to have help: