创建完成ActionContext后,strtus2将当Dispatcher分配到当前线程。
prepare.assignDispatcherToThread();
public void assignDispatcherToThread() {
Dispatcher.setInstance(dispatcher);
}
到底是怎么初始化分配的,其实也是放入ThreadLocal中,仔细看
/**
* Store the dispatcher instance for this thread.
*
* @param instance The instance
*/
public static void setInstance(Dispatcher instance) {
Dispatcher.instance.set(instance);
}
/**
* Provide a thread local instance.
*/
private static ThreadLocal<Dispatcher> instance = new ThreadLocal<Dispatcher>();
预处理完成后就判断是否有不需要处理的请求,如果有则 struts不处理,继续执行, 否则struts2过滤过来进行处理
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
}
然后就开始处理了,首先是包装requst
request = prepare.wrapRequest(request);
为什么要包装requst呢?因为是为了处理文件上传 multipart/form-data
public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {
HttpServletRequest request = oldRequest;
try {
// Wrap request first, just in case it is multipart/form-data
// parameters might not be accessible through before encoding (ww-1278)
request = dispatcher.wrapRequest(request, servletContext);
} catch (IOException e) {
String message = "Could not wrap servlet request with MultipartRequestWrapper!";
throw new ServletException(message, e);
}
return request;
}
核心的处理是dispatcher.wrapRequst
public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
// don't wrap more than once
if (request instanceof StrutsRequestWrapper) {
return request;
}
String content_type = request.getContentType();
if (content_type != null && content_type.contains("multipart/form-data")) {
MultiPartRequest mpr = null;
//check for alternate implementations of MultiPartRequest
Set<String> multiNames = getContainer().getInstanceNames(MultiPartRequest.class);
if (multiNames != null) {
for (String multiName : multiNames) {
if (multiName.equals(multipartHandlerName)) {
mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
}
}
}
if (mpr == null ) {
mpr = getContainer().getInstance(MultiPartRequest.class);
}
request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext));
} else {
request = new StrutsRequestWrapper(request);
}
return request;
}
首先是通过request获取getContentType
String content_type = request.getContentType();
然后判断,如果请求中包含multipart/form-data
则创建一个MultiPartRequestWrapper,然后返回,否则创建一个通用的StrutsRequestWrapper
MultiPartRequestWrapper和StrutsRequestWrapper到底是什么关系?
通过源码可以看到,其实
MultiPartRequestWrapper本身继承自StrutsRequestWrapper,
它比StrutsRequestWrapper做了更多的事,就是文件上传的处理
public class MultiPartRequestWrapper extends StrutsRequestWrapper {
protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
Collection<String> errors;
MultiPartRequest multi;
/**
* Process file downloads and log any errors.
*
* @param request Our HttpServletRequest object
* @param saveDir Target directory for any files that we save
* @param multiPartRequest Our MultiPartRequest object
*/
public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
super(request);
multi = multiPartRequest;
try {
multi.parse(request, saveDir);
for (Object o : multi.getErrors()) {
String error = (String) o;
addError(error);
}
} catch (IOException e) {
addError("Cannot parse request: "+e.toString());
}
}
/**
* Get an enumeration of the parameter names for uploaded files
*
* @return enumeration of parameter names for uploaded files
*/
public Enumeration<String> getFileParameterNames() {
if (multi == null) {
return null;
}
return multi.getFileParameterNames();
}
/**
* Get an array of content encoding for the specified input field name or <tt>null</tt> if
* no content type was specified.
*
* @param name input field name
* @return an array of content encoding for the specified input field name
*/
public String[] getContentTypes(String name) {
if (multi == null) {
return null;
}
return multi.getContentType(name);
}
/**
* Get a {@link java.io.File[]} for the given input field name.
*
* @param fieldName input field name
* @return a File[] object for files associated with the specified input field name
*/
public File[] getFiles(String fieldName) {
if (multi == null) {
return null;
}
return multi.getFile(fieldName);
}
/**
* Get a String array of the file names for uploaded files
*
* @param fieldName Field to check for file names.
* @return a String[] of file names for uploaded files
*/
public String[] getFileNames(String fieldName) {
if (multi == null) {
return null;
}
return multi.getFileNames(fieldName);
}
/**
* Get the filename(s) of the file(s) uploaded for the given input field name.
* Returns <tt>null</tt> if the file is not found.
*
* @param fieldName input field name
* @return the filename(s) of the file(s) uploaded for the given input field name or
* <tt>null</tt> if name not found.
*/
public String[] getFileSystemNames(String fieldName) {
if (multi == null) {
return null;
}
return multi.getFilesystemName(fieldName);
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameter(String)
*/
public String getParameter(String name) {
return ((multi == null) || (multi.getParameter(name) == null)) ? super.getParameter(name) : multi.getParameter(name);
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameterMap()
*/
public Map getParameterMap() {
Map<String, String[]> map = new HashMap<String, String[]>();
Enumeration enumeration = getParameterNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
map.put(name, this.getParameterValues(name));
}
return map;
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameterNames()
*/
public Enumeration getParameterNames() {
if (multi == null) {
return super.getParameterNames();
} else {
return mergeParams(multi.getParameterNames(), super.getParameterNames());
}
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
*/
public String[] getParameterValues(String name) {
return ((multi == null) || (multi.getParameterValues(name) == null)) ? super.getParameterValues(name) : multi.getParameterValues(name);
}
/**
* Returns <tt>true</tt> if any errors occured when parsing the HTTP multipart request, <tt>false</tt> otherwise.
*
* @return <tt>true</tt> if any errors occured when parsing the HTTP multipart request, <tt>false</tt> otherwise.
*/
public boolean hasErrors() {
return !((errors == null) || errors.isEmpty());
}
/**
* Returns a collection of any errors generated when parsing the multipart request.
*
* @return the error Collection.
*/
public Collection<String> getErrors() {
return errors;
}
/**
* Adds an error message.
*
* @param anErrorMessage the error message to report.
*/
protected void addError(String anErrorMessage) {
if (errors == null) {
errors = new ArrayList<String>();
}
errors.add(anErrorMessage);
}
/**
* Merges 2 enumeration of parameters as one.
*
* @param params1 the first enumeration.
* @param params2 the second enumeration.
* @return a single Enumeration of all elements from both Enumerations.
*/
protected Enumeration mergeParams(Enumeration params1, Enumeration params2) {
Vector temp = new Vector();
while (params1.hasMoreElements()) {
temp.add(params1.nextElement());
}
while (params2.hasMoreElements()) {
temp.add(params2.nextElement());
}
return temp.elements();
}
}
而StrutsRequestWrapper只有一个核心的方法就是getAttribute,
getAttribute的到底到哪里去取得值呢?
其实是到ValueStack中取值,我们所有的请求都被struts2添加到ValueStack中了。
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(s);
}
回到MultiPartRequestWrapper中,
我们知道,当struts2处理文件上传时,支持多文件上传,而且只要在我们的 action中声明
File[] file,
String[] fileName即可,
原因是MultiPartRequest
是这个接口在搞鬼
public interface MultiPartRequest {
public void parse(HttpServletRequest request, String saveDir) throws IOException;
/**
* Returns an enumeration of the parameter names for uploaded files
*
* @return an enumeration of the parameter names for uploaded files
*/
public Enumeration<String> getFileParameterNames();
/**
* Returns the content type(s) of the file(s) associated with the specified field name
* (as supplied by the client browser), or <tt>null</tt> if no files are associated with the
* given field name.
*
* @param fieldName input field name
* @return an array of content encoding for the specified input field name or <tt>null</tt> if
* no content type was specified.
*/
public String[] getContentType(String fieldName);
/**
* Returns a {@link java.io.File} object for the filename specified or <tt>null</tt> if no files
* are associated with the given field name.
*
* @param fieldName input field name
* @return a File[] object for files associated with the specified input field name
*/
public File[] getFile(String fieldName);
/**
* Returns a String[] of file names for files associated with the specified input field name
*
* @param fieldName input field name
* @return a String[] of file names for files associated with the specified input field name
*/
public String[] getFileNames(String fieldName);
/**
* Returns the file system name(s) of files associated with the given field name or
* <tt>null</tt> if no files are associated with the given field name.
*
* @param fieldName input field name
* @return the file system name(s) of files associated with the given field name
*/
public String[] getFilesystemName(String fieldName);
/**
* Returns the specified request parameter.
*
* @param name the name of the parameter to get
* @return the parameter or <tt>null</tt> if it was not found.
*/
public String getParameter(String name);
/**
* Returns an enumeration of String parameter names.
*
* @return an enumeration of String parameter names.
*/
public Enumeration<String> getParameterNames();
/**
* Returns a list of all parameter values associated with a parameter name. If there is only
* one parameter value per name the resulting array will be of length 1.
*
* @param name the name of the parameter.
* @return an array of all values associated with the parameter name.
*/
public String[] getParameterValues(String name);
/**
* Returns a list of error messages that may have occurred while processing the request.
* If there are no errors, an empty list is returned. If the underlying implementation
* (ie: pell, cos, jakarta, etc) cannot support providing these errors, an empty list is
* also returned. This list of errors is repoted back to the
* {@link MultiPartRequestWrapper}'s errors field.
*
* @return a list of Strings that represent various errors during parsing
*/
public List getErrors();
}
看了源码详细不用仔细说什么也能明白
这个类的实现类只有一个,那就是JakartaMultiPartRequest专门用来处理文件上传
我们看到它默认的属性
protected Map<String,List<FileItem>> files = new HashMap<String,List<FileItem>>();
// maps parameter name -> List of param values
protected Map<String,List<String>> params = new HashMap<String,List<String>>();
// any errors while processing this request
protected List<String> errors = new ArrayList<String>();
protected long maxSize;
这就是我们的action中为什么实现文件上传是如此的容易,而且支持多文件上传。
当我们调用getFiles的时候,其实是调用JakartaMultiPartRequest中的getFile
public File[] getFile(String fieldName) {
List<FileItem> items = files.get(fieldName);
if (items == null) {
return null;
}
List<File> fileList = new ArrayList<File>(items.size());
for (FileItem fileItem : items) {
File storeLocation = ((DiskFileItem) fileItem).getStoreLocation();
if(fileItem.isInMemory() && storeLocation!=null && !storeLocation.exists()) {
try {
storeLocation.createNewFile();
} catch (IOException e) {
if(LOG.isErrorEnabled()){
LOG.error("Cannot write uploaded empty file to disk: " + storeLocation.getAbsolutePath(),e);
}
}
}
fileList.add(storeLocation);
}
return fileList.toArray(new File[fileList.size()]);
}
这样把request就包装成strtus2的request了。可以处理普通请求和文件上传请求。
~~~~~~~~~~~~~~~~~