Struts2运行原理
Struts2基于Servlet框架的filter机制实现。所以需要在web.xml
中添加如下配置。
struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /*
当tomcat启动时,会通过初始化StrutsPrepareAndExecuteFilter加载struts2的相关配置信息。
系统运行时,会拦截request请求,执行struts2的流程。request处理流程
客户端发送请求
请求先通过StrutsPrepareAndExecuteFilter
StrutsPrepareAndExecuteFilter通过ActionMapper来决定这个Request需要调用哪个Action
如果ActionMapper决定调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理
交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类
ActionProxy创建一个ActionInvocation的实例
ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用
Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,
可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现,不知道其它还有什么方式?
相关概念
ActionMapper
提供Http请求和Action之间的映射,基本上不需要访问struts的配置文件来确定request和action的关系。
默认的使用DefaultActionMapper,同时可以使用自定义Mapper,但是必须要实现mapper.ActionMapper
接口,并且有一个默认的构造器。
ActionMapper会根据request获取相应的ActionMapping,ActionMapping包含了action的类和方法等详细信息。
ActionProxy & ActionInvocation
ActionProxy由ActionProxyFactory创建,是Action的代理,用来获取获取Action类,也可以被远程使用。
ActionProxy会使用ActionInvocation封装特定request的action,ActionInvocation决定action的动作,执行、拦截、监听。
简而言之,ActionProxy封装request可以获得的action,ActionInvocation封装action如何执行。
ConfigurationProvider & Configuration
ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是使用StrutsXmlConfigurationProvider来载入xml文件的配置信息。
provider可以通过DispatcherListener进行配置。Struts2初始化
Struts2通过初始化StrutsPrepareAndExecuteFilter
,加载配置信息。StrutsPrepareAndExecuteFilter的初始化过程如下:
/* 学习点:1.面向接口编程,耦合度低 2.适配器模式 */ public void init(FilterConfig filterConfig) throws ServletException { //封装了初始化动作的类 InitOperations init = new InitOperations(); Dispatcher dispatcher = null; try { //对filter配置进行封装,此处有点类似代理模式类型 FilterHostConfig config = new FilterHostConfig(filterConfig); //初始化log init.initLogging(config); //创建dispatcher对象 dispatcher = init.initDispatcher(config); //初始化静态资源加载器 init.initStaticContentLoader(config, dispatcher); //初始化prepare(封装请求预处理操作) prepare = new PrepareOperations(dispatcher); //初始化excute (封装了请求处理操作) execute = new ExecuteOperations(dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); //初始化完成回调方法,默认为空 postInit(dispatcher, filterConfig); } finally { //清理 if (dispatcher != null) { dispatcher.cleanUpAfterInit(); } init.cleanup(); } }
配置信息封装
传入FilterConfig
接口的实例,然后封装为HostConfig
的实例FilterHostConfig
。在创建的过程中,调用方法的入参类型大都为HostConfig
类型。
/* FilterConfig 为接口*/public class FilterHostConfig implements HostConfig { private FilterConfig config; public FilterHostConfig(FilterConfig config) { this.config = config; } public String getInitParameter(String key) { return config.getInitParameter(key); } public IteratorgetInitParameterNames() { return MakeIterator.convert(config.getInitParameterNames()); } public ServletContext getServletContext() { return config.getServletContext(); }}
logging信息配置
学习:
利用配置信息很灵活的提供数据,创建
LoggerFactory
命名规范,工厂模式以
Factory
后缀命名
/** * Initializes the internal Struts logging */ public void initLogging( HostConfig filterConfig ) { //从配置信息中,读取loggerFactory配置信息 String factoryName = filterConfig.getInitParameter("loggerFactory"); if (factoryName != null) { try { //利用反射创建LoggerFactory Class cls = ClassLoaderUtil.loadClass(factoryName, this.getClass()); LoggerFactory fac = (LoggerFactory) cls.newInstance(); LoggerFactory.setLoggerFactory(fac); } catch ( InstantiationException e ) { System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default"); e.printStackTrace(); } catch ( IllegalAccessException e ) { System.err.println("Unable to access logger factory: " + factoryName + ", using default"); e.printStackTrace(); } catch ( ClassNotFoundException e ) { System.err.println("Unable to locate logger factory class: " + factoryName + ", using default"); e.printStackTrace(); } } }
学习:
在多线程的环境下,创建对象。
public static void setLoggerFactory(LoggerFactory factory) { //创建写锁 lock.writeLock().lock(); try { LoggerFactory.factory = factory; } finally { //在finally内释放写锁 lock.writeLock().unlock(); } }
在这个过程中,ClassLoaderUtil
是一个载入资源和类的工具,反射类的代码如下。
学习:
1. 异常的处理形式 2. 获取反射的优先级处理
/* @param 类名 The name of the class to load * @param 调用方法的类 The Class object of the calling object * @throws ClassNotFoundException If the class cannot be found anywhere. */public static Class loadClass(String className, Class callingClass) throws ClassNotFoundException { try { //从当前线程获取ClassLoader,然后反射对象 return Thread.currentThread().getContextClassLoader().loadClass(className); } catch (ClassNotFoundException e) { try { //直接获取 return Class.forName(className); } catch (ClassNotFoundException ex) { try { //从ClassLoaderUtil获取ClassLoader return ClassLoaderUtil.class.getClassLoader().loadClass(className); } catch (ClassNotFoundException exc) { //从callingClass获取ClassLoader return callingClass.getClassLoader().loadClass(className); } } } }
创建dispatcher(分配器)
传入配置信息(形参为HostConfig接口,非常灵活!),返回dispatcher。
public Dispatcher initDispatcher( HostConfig filterConfig ) { //利用filterConfig信息,创建Dispatcher对象 Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; }
Dispatcher
的创建过程,其实就是数据转换,赋值的过程
private Dispatcher createDispatcher( HostConfig filterConfig ) { //获取配置信息里的信息 Mapparams = new HashMap (); for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) { String name = (String) e.next(); String value = filterConfig.getInitParameter(name); params.put(name, value); } //利用配置信息和ServletContext创建 return new Dispatcher(filterConfig.getServletContext(), params); }
dispatcher
的初始化过程比较复杂,很多初始化过程都在其中包含。
public void init() { if (configurationManager == null) { configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME); } try { //初始化文件管理 init_FileManager(); //加载org/apache/struts2/default.properties init_DefaultProperties(); // [1] //加载struts-default.xml,struts-plugin.xml,struts.xml init_TraditionalXmlConfigurations(); // [2] init_LegacyStrutsProperties(); // [3] //用户自己实现的ConfigurationProviders类 init_CustomConfigurationProviders(); // [5] //Filter的初始化参数 init_FilterInitParameters() ; // [6] init_AliasStandardObjects() ; // [7] //容器 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckWebLogicWorkaround(container); //监听器 if (!dispatcherListeners.isEmpty()) { for (DispatcherListener l : dispatcherListeners) { l.dispatcherInitialized(this); } } errorHandler.init(servletContext); } catch (Exception ex) { if (LOG.isErrorEnabled()) LOG.error("Dispatcher initialization failed", ex); throw new StrutsException(ex); } }