Struts2的初始化
StrutsPrepareAndExecuteFilter
属性摘要
protected List<Pattern> | excludedPatterns |
protected ExecuteOperations | execute |
protected PrepareOperations | prepare |
1、初始化过程
1. public void init(FilterConfig filterConfig){
2. InitOperations init = new InitOperations();
3. //封装filterConfig。方法getInitParameterNames将参数名以String存入List
4. FilterHostConfig config = new FilterHostConfig(filterConfig);
5. // 初始化struts内部日志
6. init.initLogging(config);
7. //创建dispatcher 。并初始化,这部分下面我们重点分析,初始化时加载那些资源
8. Dispatcher dispatcher = init.initDispatcher(config);
9. init.initStaticContentLoader(config, dispatcher);
10. //初始化类属性:prepare 、execute
11. prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
12. execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
13. this .excludedPatterns = init.buildExcludedPatternsList(dispatcher);
14. }
1.1、封装filterConfig
1. public class FilterHostConfig implements HostConfig {
2. private FilterConfig config;
3. public FilterHostConfig(FilterConfig config) {
4. this.config = config;
5. }
6. //根据init-param配置的param-name获取param-value的值
7. public String getInitParameter(String key) {
8. return config.getInitParameter(key);
9. }
10. //返回初始化参数名的List
11. public Iterator<String> getInitParameterNames() {
12. return MakeIterator.convert(config.getInitParameterNames());
13. }
14. public ServletContext getServletContext() {
15. return config.getServletContext();
16. }
17. }
只有短短的几行代码,将Filter初始化参数名称有枚举类型转为Iterator,此类的主要作为是对filterConfig 封装。
创建并初始化Dispatcher
1. public Dispatcher initDispatcher( HostConfig filterConfig ) {
2. Dispatcher dispatcher = createDispatcher(filterConfig);
3. dispatcher.init();
4. return dispatcher;
5. }
创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,然后根绝servlet上下文和参数Map构造Dispatcher:
1. private Dispatcher createDispatcher( HostConfig filterConfig ) {
2. Map<String, String> params = new HashMap<String, String>();
3. for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); )
4. String name = (String) e.next();
5. String value = filterConfig.getInitParameter(name);
6. params.put(name, value);
7. }
8. return new Dispatcher(filterConfig.getServletContext(), params);
9. }
Dispatcher初始化过程主要内容如图,
1. public void init() {
2. if (configurationManager == null) {
3. configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
4. }
5. //封装org/apache/struts2/default.properties
6. init_DefaultProperties(); // [1]
7. //封装struts-default.xml,struts-plugin.xml,struts.xml
8. init_TraditionalXmlConfigurations(); // [2]
9. init_LegacyStrutsProperties(); // [3]
10. //封装用户自己实现的ConfigurationProviders类
11. init_CustomConfigurationProviders(); // [5]
12. //Filter的初始化参数
13. init_FilterInitParameters() ; // [6]
14. init_AliasStandardObjects() ; // [7]
15. //此处最为关键,加载[1-7]对象代表的配置中的数据。
16. Container container = init_PreloadConfiguration();
17. container.inject(this);
18. init_CheckWebLogicWorkaround(container);
19. //添加dispatcher的listener
20. if (!dispatcherListeners.isEmpty()) {
21. for (DispatcherListener l : dispatcherListeners) {
22. l.dispatcherInitialized(this);
23. }
24. }
25. }
所要加载struts2的相关配置文件包括default.properties,struts-default.xml,struts-plugin.xml,struts.xml……
[1-7]的过程是将所有的配置信息封装为ConfigurationProviders对象,并在创建container的时候加载所有的配置数据,init_PreloadConfiguration函数执行加载过程。
封装为ConfigurationProvier对象
以default.properties为例,具体的封装操作如下:
1. private void init_DefaultProperties() {
2. configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
3. }
配置文件加载过程
Dispatcher.java文件
1.private Containerinit_PreloadConfiguration() {
2. //创建配置管理和Container对象
3. Configuration config = configurationManager.getConfiguration();
4. Container container =config.getContainer();
5. //设置多语言变量
6. boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
7. LocalizedTextUtil.setReloadBundles(reloadi18n);
8. ContainerHolder.store(container);
9. return container;
10. }
ConfigurationManager.java
1. publicsynchronizedConfiguration getConfiguration() {
2. //创建配置管理对象
3. setConfiguration(createConfiguration(defaultFrameworkBeanName));
4. //添加所有配置文件的文件管理器到配置管理器中
5. configuration.reloadContainer(getContainerProviders());
6. returnconfiguration;
7. }
8. protected Configuration createConfiguration(String beanName) {
9. returnnewDefaultConfiguration(beanName);
10. }
DefaultConfiguration.java
开始加载Provider中的数据
1. publicsynchronizedList<PackageProvider> reloadContainer(List<ContainerProvider>providers) {
2.
3. packageContexts.clear();
4. loadedFileNames.clear();
5. List<PackageProvider>packageProviders =new ArrayList<PackageProvider>();
6. ContainerProperties props =new ContainerProperties();
7. ContainerBuilder builder =new ContainerBuilder();
8. //ContainerBuilder使用反射机制和注入建立基本的container的factory
9. Container bootstrap =createBootstrapContainer(providers);
10. for (final ContainerProvider containerProvider :providers)
11. {
12. bootstrap.inject(containerProvider);
13. //配置文件的ConfigurationProviders读取各自的配置文件,将数据以文档对象的加入container
14. containerProvider.init(this);
15. //调用register方法开始加载document对象中的部分非以包组合的信息,以StrutsXmlConfigurationProvider为例加载bean和constant,其他provider各有不同
16. containerProvider.register(builder,props);
17. }
18. props.setConstants(builder);
19. builder.factory(Configuration.class,newFactory<Configuration>() {
20. public Configuration create(Context context)throws Exception {
21. return DefaultConfiguration.this;
22. }
23. });
24. ActionContext oldContext =ActionContext.getContext();
25. // Set the bootstrap container forthe purposes of factory creation
26. setContext(bootstrap);
27. container = builder.create(false);
28. setContext(container);
29. objectFactory =container.getInstance(ObjectFactory.class);
30. //先处理配置文件的ContainerProvider,加载其中的包节点下的数据,并将container放入 packageProviders
31. for (final ContainerProvider containerProvider :providers)
32. {
33. if (containerProviderinstanceof PackageProvider){
34. container.inject(containerProvider);
35. //此处是重点,里面加载了包节点下的所有数据
36. ((PackageProvider)containerProvider).loadPackages();
37. packageProviders.add((PackageProvider)containerProvider);
38. }
39. }
40. //处理来自插件的PackageProvider,同上的处理数据
41. Set<String>packageProviderNames = container.getInstanceNames(PackageProvider.class);
42. for (String name : packageProviderNames) {
43. PackageProvider provider =container.getInstance(PackageProvider.class, name);
44. provider.init(this);
45. provider.loadPackages();
46. packageProviders.add(provider);
47. }
48. rebuildRuntimeConfiguration();
49. return packageProviders;
50. }
以StrutsXmlConfigurationProvider为例
1. publicvoid loadPackages()throws ConfigurationException{
2. List<Element> reloads =newArrayList<Element>();
3. verifyPackageStructure();
4. //逐个文档处理对象
5. for (Document doc :documents) {
6. Element rootElement =doc.getDocumentElement();
7. NodeList children =rootElement.getChildNodes();
8. int childSize = children.getLength();
9. for (int i = 0; i < childSize; i++) {
10. Node childNode = children.item(i);
11. if (childNodeinstanceof Element) {
12. Element child =(Element) childNode;
13. final String nodeName =child.getNodeName();
14. if ("package".equals(nodeName)){ //处理包节点下的数据
15. PackageConfig cfg =addPackage(child);
16. }
17. }
18. }
19. }
20. documents.clear();
21. declaredPackages.clear();
22. configuration =null;
23. }
以下过程是按照不同的类型加载包节点下的相应的节点信息
1. protected PackageConfigaddPackage(Element packageElement) {
2. String packageName =packageElement.getAttribute("name");
3. PackageConfig packageConfig =configuration.getPackageConfig(packageName);
4. PackageConfig.Builder newPackage =buildPackageContext(packageElement);
5. // add result types (and default result)to this package
6. addResultTypes(newPackage, packageElement);
7. // load theinterceptors and interceptor stacks for this package
8. loadInterceptors(newPackage, packageElement);
9. // load the default interceptor reference for thispackage
10. loadDefaultInterceptorRef(newPackage,packageElement);
11. // load the defaultclass ref for this package
12. loadDefaultClassRef(newPackage,packageElement);
13. // load the globalresult list for this package
14. loadGlobalResults(newPackage,packageElement);
15. // load the global exception handler list for thispackage
16. loadGobalExceptionMappings(newPackage,packageElement);
17. // get actions
18. NodeListactionList = packageElement.getElementsByTagName("action");
19. for (inti = 0; i < actionList.getLength(); i++) {
20. Element actionElement = (Element)actionList.item(i);
21. addAction(actionElement, newPackage);
22. }
23. // load the defaultaction reference for this package
24. loadDefaultActionRef(newPackage,packageElement);
25. PackageConfig cfg = newPackage.build();
26. configuration.addPackageConfig(cfg.getName(), cfg);
27. return cfg;
28. }
至此配置文件的加载过程完毕
运行过程:doFilter方法
1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
2. HttpServletRequest request = (HttpServletRequest) req;
3. HttpServletResponse response = (HttpServletResponse) res;
4. //设置编码和国际化
5. prepare.setEncodingAndLocale(request, response);
6. //创建Action上下文(重点)
7. prepare.createActionContext(request, response);
8. //以初始化的dispatcher为模板构复制一个dispatcher给线程
9. prepare.assignDispatcherToThread();
10. request = prepare.wrapRequest(request);
11. //根据请求从configureManager获得数据,创建mapping对象
12. ActionMapping mapping = prepare.findActionMapping(request, response, true);
13. //根据mapping执行映射的action
14. execute.executeAction(request, response, mapping);
15. }
1. //Sets the request encoding and locale on the response
2. public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {
3. dispatcher.prepare(request, response);
4. }
下面我们看下prepare方法,这个方法很简单只是设置了encoding 、locale ,做的只是一些辅助的工作:
1. public void prepare(HttpServletRequest request, HttpServletResponse response) {
2. String encoding = null ;
3. if (defaultEncoding != null ) {
4. encoding = defaultEncoding;
5. }
6. Locale locale = null ;
7. if (defaultLocale != null ) {
8. locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
9. }
10. if (encoding != null ) {
11. request.setCharacterEncoding(encoding);
12. }
13. if (locale != null ) {
14. response.setLocale(locale);
15. }
16. if (paramsWorkaroundEnabled) {
17. request.getParameter("foo" ); // simply read any parameter (existing or not) to "prime" the request
18. }
19.}
Action上下文创建(重点)
ActionContext是一个容器,这个容易主要存储request、session、application、parameters等相关信息。其实质是一个Map,key是标志request、session、……的字符串,值是其对应的对象:
1. static ThreadLocal actionContext = new ThreadLocal();
2. Map<String, Object> context;
下面我们看下如何创建action上下文的,代码如下:
1. //创建Action上下文,初始化thread local
2. public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
3. ActionContext ctx;
4. Integer counter = 1 ;
5. Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
6. if (oldCounter != null ) {
7. counter = oldCounter + 1 ;
8. }
9. //从ThreadLocal中获取此ActionContext变量
10. ActionContext oldContext = ActionContext.getContext();
11. if (oldContext != null ) {
12. // detected existing context, so we are probably in a forward
13. ctx = new ActionContext( new HashMap<String, Object>(oldContext.getContextMap()));
14. } else {
15. ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class ).createValueStack();
16. //将数据放入stack._values
17. stack.getContext().putAll(dispatcher.createContextMap(request, response, null , servletContext));
18. //stack.getContext()返回的是一个Map<String,Object>,根据此Map构造一个ActionContext
19. ctx = new ActionContext(stack.getContext());
20. }
21. request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
22. //将ActionContext存如ThreadLocal
23. ActionContext.setContext(ctx);
24. return ctx;
25.}
dispatcher.createContextMap,如何封装相关参数:
1. public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,ActionMapping mapping, ServletContext context) {
2. Map requestMap = new RequestMap(request);
3. Map params = new HashMap(request.getParameterMap());
4. Map session = new SessionMap(request);
5. Map application = new ApplicationMap(context);
6. //requestMap、params、session等封装为map逐个放入extraContext中
7. Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
8. if (mapping != null ) {
9. extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
10. }
11. return extraContext;
12. }
ActionMapping的映射关系的查找过程
1. publicActionMappingfindActionMapping(HttpServletRequest request, HttpServletResponse response,booleanforceLookup) {
2. mapping =dispatcher.getContainer().getInstance(ActionMapper.class)
3. .getMapping(request,dispatcher.getConfigurationManager());
4. if(mapping !=null) {
5. request.setAttribute(STRUTS_ACTION_MAPPING_KEY,mapping);
6. }
7. returnmapping;
8. }
1. publicActionMappinggetMapping(HttpServletRequest request, ConfigurationManager configManager) {
2. ActionMapping mapping = newActionMapping();
3. //从request获取uri
4. Stringuri = getUri(request);
5. intindexOfSemicolon =uri.indexOf(";");
6. uri = (indexOfSemicolon > -1)? uri.substring(0, indexOfSemicolon) : uri;
7. //去除扩展名,如".xhtml"
8. uri = dropExtension(uri,mapping);
9. //根据最长匹配从configureManager获取action、namespace,并放入mapping
10. parseNameAndNamespace(uri,mapping, configManager);
11. //处理特殊格式的请求参数
12. handleSpecialParameters(request,mapping);
13. //如果有DMC的调用则,解析method名
14. return parseActionName(mapping);
15.}
下面是源码展示了如何执行Action控制器:
1. public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) {
2. dispatcher.serviceAction(request, response, servletContext, mapping);
3. }
4.
5. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) {
6. //封装执行的上下文环境,主要讲相关信息存储入map
7. Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
8. // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
9. ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
10. boolean nullStack = stack == null ;
11. if (nullStack) {
12. ActionContext ctx = ActionContext.getContext();
13. stack = ctx.getValueStack();
14. }
15. extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
16. //获取命名空间
17. String namespace = mapping.getNamespace();
18. //获取action配置的name属性
19. String name = mapping.getName();
20. //获取action配置的method属性
21. String method = mapping.getMethod();
22. Configuration config = configurationManager.getConfiguration();
23. //根据执行上下文参数,命名空间,名称等创建用户自定义Action的代理对象
24. ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class ).createActionProxy(namespace, name, method, extraContext, true , false );
25. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
26. //执行execute方法,并转向结果
27. proxy.execute();
28.}
下面是ActionProxy创建的过程
1. public ActionProxycreateActionProxy(String namespace, String actionName, String methodName,Map<String, Object> extraContext,boolean executeResult, boolean cleanupContext) {
2. ActionInvocation inv =newDefaultActionInvocation(extraContext,true);
3. container.inject(inv);
4. return createActionProxy(inv, namespace,actionName, methodName, executeResult, cleanupContext);
5. }
ActionProxy的执行过程
1. public String execute()throws Exception {
2. ActionContext previous =ActionContext.getContext();
3. ActionContext.setContext(invocation.getInvocationContext());
4. returninvocation.invoke();
5. }
如果截拦器全部执行完毕,则执行invokeActionOnly()方法执行Action,invokeActionOnly()方法基本没做什么工作,只调用了invokeAction()方法。为了执行Action,必须先创建该对象,该工作在DefaultActionInvocation的构造方法中调用init()方法早早完成。调用过程是:DefaultActionInvocation()->init()->createAction()。
ActionProxy的invoke函数执行过程如下:
1. public String invoke()throws Exception {
2. //递归的执行拦截器,当拦截器执行完执行action
3. if (interceptors.hasNext()) {
4. final InterceptorMapping interceptor =interceptors.next();
5. String interceptorMsg ="interceptor: "+ interceptor.getName();
6. resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
7. } else {
8. resultCode = invokeActionOnly();
9. }
10. //action执行完后如果存在结果监听器则实行PreResultListener
11. if (preResultListeners !=null){
12. for (Object preResultListener :preResultListeners){
13. PreResultListenerlistener = (PreResultListener) preResultListener;
14. listener.beforeResult(this,resultCode);
15. }
16. }
17. //最后根据结果生成Result,即,渲染引擎对象并执行渲染和页面返回。
18. if (proxy.getExecuteResult()) {
19. executeResult();
20. }
21. returnresultCode;
22. }
Action的调用过程如下
1. public StringinvokeActionOnly()throws Exception {
2. return invokeAction(getAction(),proxy.getConfig());
3. }
4. protected StringinvokeAction(Object action, ActionConfig actionConfig) {
5. String methodName = proxy.getMethod();
6. boolean methodCalled =false;
7. Object methodResult =null;
8. Method method = null;
9. //反射机制获取method,执行并将结果调用saveResult保存
10. method =getAction().getClass().getMethod(methodName,EMPTY_CLASS_ARRAY);
11. methodResult =method.invoke(action,EMPTY_OBJECT_ARRAY);
12. return saveResult(actionConfig, methodResult);
13. }
14. Result(渲染引擎)的执行过程
15. privatevoid executeResult()throws Exception {
16. //创建渲染隐形对象
17. result = createResult();
18. //调用渲染引擎的execute方法执行渲染过程
19. result.execute(this);
20. }
1. public Result createResult()throws Exception {
2. ActionConfig config =proxy.getConfig();
3. Map<String, ResultConfig>results = config.getResults();
4. //resultConfig中是存储了从xml获取的resultCode与渲染引擎的映射
5. ResultConfig resultConfig =null;
6. resultConfig = results.get(resultCode);
7. returnobjectFactory.buildResult(resultConfig,invocationContext.getContextMap());
8. }
使用映射机制建立渲染引擎
1. public ResultbuildResult(ResultConfig resultConfig, Map<String, Object> extraContext){
2. String resultClassName =resultConfig.getClassName();
3. Result result = null;
4. result = (Result)buildBean(resultClassName, extraContext);
5. Map<String, String> params= resultConfig.getParams();
6. for (Map.Entry<String, String> paramEntry: params.entrySet()) {
7. reflectionProvider.setProperty(paramEntry.getKey(),paramEntry.getValue(), result, extraContext,true);
8. }
9. return result;
10. }
至此,Struts2的核心执行流程分析完毕!
参考资料:
http://www.blogjava.net/lzhidj/archive/2008/07/10/213898.html
http://www.iteye.com/topic/829843
http://qidaoxp.iteye.com/blog/497052
http://struts2.group.iteye.com/group/wiki/
http://struts.apache.org/development/2.x/docs/core-developers-guide.html
《深入浅出Struts 2》
《轻量J2EE应用程序开发—MVC with Webwork2》