struts2架构中核心对象的探索—— Dispatcher&ConfigurationProvider
来源:互联网 发布:python ipython 编辑:程序博客网 时间:2024/05/20 00:49
首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,像Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干。
接下来就从Dispatcher开始看起,先看其构造函数:
//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方 public Dispatcher(ServletContext servletContext, Map<String, String> initParams) { this.servletContext = servletContext; //配置在web.xml中的param参数 this.initParams = initParams; }
我们再看在FilterDispatcher创建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig) { Map<String, String> params = new HashMap<String, String>(); for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) { String name = (String) e.nextElement(); String value = filterConfig.getInitParameter(name); params.put(name, value); } //都可以从FilterConfig中得到 return new Dispatcher(filterConfig.getServletContext(), params); }
创建Dispatcher之后,来看init()方法,init()方法是用来Load用户配置文件,资源文件以及默认的配置文件,主要分七步走,看下面注释:
public void init() { if (configurationManager == null) { //设置ConfigurationManager的defaultFrameworkBeanName. //这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等 configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } //读取properties信息,默认的default.properties, init_DefaultProperties(); // [1] //读取xml配置文件 init_TraditionalXmlConfigurations(); // [2] //读取用户自定义的struts.properties init_LegacyStrutsProperties(); // [3] //自定义的configProviders init_CustomConfigurationProviders(); // [5] //载入FilterDispatcher传进来的initParams init_FilterInitParameters() ; // [6] //将配置文件中的bean与具体的类映射 init_AliasStandardObjects() ; // [7] //构建一个用于依赖注射的Container对象 //在这里面会循环调用上面七个ConfigurationProvider的register方法 //其中的重点就是DefaultConfiguration的#reload()方法 Container container = init_PreloadConfiguration(); container.inject(this); init_CheckConfigurationReloading(container); init_CheckWebLogicWorkaround(container); if (!dispatcherListeners.isEmpty()) { for (DispatcherListener l : dispatcherListeners) { l.dispatcherInitialized(this); } } }
分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.
将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.
下面将分析分析7步流程:
- 1:首先是init_DefaultProperties()
private void init_DefaultProperties() { configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); } //接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法 public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { Settings defaultSettings = null; try { defaultSettings = new PropertiesSettings("org/apache/struts2/default"); } catch (Exception e) { throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e); } loadSettings(props, defaultSettings); }
//PropertiesSettings构造方法 //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写 public PropertiesSettings(String name) { URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass()); if (settingsUrl == null) { LOG.debug(name + ".properties missing"); settings = new LocatableProperties(); return; } settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString())); // Load settings InputStream in = null; try { in = settingsUrl.openStream(); settings.load(in); } catch (IOException e) { throw new StrutsException("Could not load " + name + ".properties:" + e, e); } finally { if(in != null) { try { in.close(); } catch(IOException io) { LOG.warn("Unable to close input stream", io); } } } } //loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props //这个props是register的一个入参. protected void loadSettings(LocatableProperties props, final Settings settings) { // We are calling the impl methods to get around the single instance of Settings that is expected for (Iterator i = settings.listImpl(); i.hasNext(); ) { String name = (String) i.next(); props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name)); } }
- 2:再来看第二步:init_TraditionalXmlConfigurations()
private void init_TraditionalXmlConfigurations() { //首先读取web.xml中的config初始参数值 //如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml", //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了 //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可 String configPaths = initParams.get("config"); if (configPaths == null) { configPaths = DEFAULT_CONFIGURATION_PATHS; } String[] files = configPaths.split("\\s*[,]\\s*"); for (String file : files) { if (file.endsWith(".xml")) { if ("xwork.xml".equals(file)) { //XmlConfigurationProvider负责解析xwork.xml configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); } else { //其它xml都是由StrutsXmlConfigurationProvider来解析 configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext)); } } else { throw new IllegalArgumentException("Invalid configuration file name"); } } }
对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。 类XmlConfigurationProvider负责配置文件的读取和解析, 首先通过init()中的loadDocuments(configFileName); 利用DomHelper中的 public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) 将configFileName配置文件通过SAX解析方式按照DtdMappings解析成Document对象. 然后通过Provider的register()方法加载"bean"和"constant"属性, 再通过loadPackages()加载package及package中的属性
addAction()方法负责读取<action>标签,并将数据保存在ActionConfig中;addResultTypes()方法负责将<result-type>标签转化为ResultTypeConfig对象;loadInterceptors()方法负责将<interceptor>标签转化为InterceptorConfi对象;loadInterceptorStack()方法负责将<interceptor-ref>标签转化为InterceptorStackConfig对象;loadInterceptorStacks()方法负责将<interceptor-stack>标签转化成InterceptorStackConfig对象。
而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。
protected PackageConfig addPackage(Element packageElement) throws ConfigurationException { PackageConfig.Builder newPackage = buildPackageContext(packageElement); if (newPackage.isNeedsRefresh()) { return newPackage.build(); } // add result types (and default result) to this package addResultTypes(newPackage, packageElement); // load the interceptors and interceptor stacks for this package loadInterceptors(newPackage, packageElement); // load the default interceptor reference for this package loadDefaultInterceptorRef(newPackage, packageElement); // load the default class ref for this package loadDefaultClassRef(newPackage, packageElement); // load the global result list for this package loadGlobalResults(newPackage, packageElement); // load the global exception handler list for this package loadGobalExceptionMappings(newPackage, packageElement); // get actions NodeList actionList = packageElement.getElementsByTagName("action"); for (int i = 0; i < actionList.getLength(); i++) { Element actionElement = (Element) actionList.item(i); addAction(actionElement, newPackage); } // load the default action reference for this package loadDefaultActionRef(newPackage, packageElement); PackageConfig cfg = newPackage.build(); configuration.addPackageConfig(cfg.getName(), cfg); return cfg; } loadConfigurationFiles解析读取xml中的内容 private List<Document> loadConfigurationFiles(String fileName, Element includeElement) { ... //通过DomHelper调用SAX进行解析xml doc = DomHelper.parse(in, dtdMappings); ... Element rootElement = doc.getDocumentElement(); NodeList children = rootElement.getChildNodes(); int childSize = children.getLength(); for (int i = 0; i < childSize; i++) { Node childNode = children.item(i); if (childNode instanceof Element) { Element child = (Element) childNode; final String nodeName = child.getNodeName(); if ("include".equals(nodeName)) { String includeFileName = child.getAttribute("file"); //解析每个action配置,对于include文件可以使用通配符*来进行配置 //如Struts.xml中可配置成<include file="actions_*.xml"/> if (includeFileName.indexOf('*') != -1) { ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Vector<String> wildcardMatches = wildcardFinder.findMatches(); for (String match : wildcardMatches) { //递归Load子file中的<include/> docs.addAll(loadConfigurationFiles(match, child)); } } else { docs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } docs.add(doc); loadedFileUrls.add(url.toString()); ... return docs; }
- 3:接下来第三步:
init_LegacyStrutsProperties()调用的是LegacyPropertiesConfigurationProvider
通过比较前面DefaultPropertiesProvider也调用的是LegacyPropertiesConfigurationProvider,发现DefaultPropertiesProvider继承自后者,但重写了register()方法,
主要是生成PropertiesSetting的不同,前者是根据org/apache/struts2/default.properties,后者是根据struts.properties
我们展开register()中的Settings.getInstance(),最后是调用getDefaultInstance()
private static Settings getDefaultInstance() { if (defaultImpl == null) { // Create bootstrap implementation //不带参数的DefaultSettings(),区别与DefaultPropertiesProvider中直接带default.properties参数 //不带参数就是默认为struts.propertes,并且加载struts.custom.properties所定义的properties文件 defaultImpl = new DefaultSettings(); // Create default implementation try { //STRUTS_CONFIGURATION为:struts.configuration //在struts.proterties中查找struts.configuration的值,这个值必须是org.apache.struts2.config.Configuration接口的实现类 //所以我有个困惑就是在下面的转换当中怎么将Configuration转换成Setting类型的... //这一点先放下了,有时间再研究 String className = get(StrutsConstants.STRUTS_CONFIGURATION); if (!className.equals(defaultImpl.getClass().getName())) { try { // singleton instances shouldn't be built accessing request or session-specific context data defaultImpl = (Settings) ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLoader().loadClass(className), null); } catch (Exception e) { LOG.error("Settings: Could not instantiate the struts.configuration object, substituting the default implementation.", e); } } } catch (IllegalArgumentException ex) { // ignore
去掉了第四步:init_ZeroConfiguration();
- 5:第五步是自定义的configProviders
private void init_CustomConfigurationProviders() { //从这里可以看到可以将自定义的Provider定义在web.xml中FilterDispatcher的param中:configProviders String configProvs = initParams.get("configProviders"); if (configProvs != null) { String[] classes = configProvs.split("\\s*[,]\\s*"); for (String cname : classes) { try { Class cls = ClassLoaderUtils.loadClass(cname, this.getClass()); ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance(); configurationManager.addConfigurationProvider(prov); } ... } } }
- 6:第六步:init_FilterInitParameters
//从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcher的param中定义 private void init_FilterInitParameters() { configurationManager.addConfigurationProvider(new ConfigurationProvider() { public void destroy() {} public void init(Configuration configuration) throws ConfigurationException {} public void loadPackages() throws ConfigurationException {} public boolean needsReload() { return false; } public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { props.putAll(initParams);//在这里实现滴~ } }); }
第七步:init_AliasStandardObjects
使用BeanSelectionProvider这是将配置文件中定义的与实际的类相映射,就是注入bean的依赖关系,接下来是看怎样调用这些ConfigurationProviders,展开init_PreloadConfiguration()
private Container init_PreloadConfiguration() { Configuration config = configurationManager.getConfiguration(); Container container = config.getContainer(); boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD)); LocalizedTextUtil.setReloadBundles(reloadi18n); return container; } //再看getConfiguration() public synchronized Configuration getConfiguration() { if (configuration == null) { setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName)); try { //重点就是这个reloadContainer configuration.reloadContainer(getContainerProviders()); } catch (ConfigurationException e) { setConfiguration(null); throw new ConfigurationException("Unable to load configuration.", e); } } else { conditionalReload(); } return configuration; }
展开DefaultConfiguration中的reloadContainer
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException { packageContexts.clear(); loadedFileNames.clear(); List<PackageProvider> packageProviders = new ArrayList<PackageProvider>(); //Struts2(xwork2)用Container来完成依赖注入的功能 //首先初始化一个ContainerBuilder,再由builder来保存接口与实现类或工厂类的对应关系 //然后通过builder.create(boolean)方法产生container //由container.getInstance(Class);就可以得到接口的实现实例了 //这一部分比较复杂,后面研究完成了,会单独拿出来讲,这里先弄清楚Xwork依赖注入的实现步骤就可以了 ContainerProperties props = new ContainerProperties(); ContainerBuilder builder = new ContainerBuilder(); for (final ContainerProvider containerProvider : providers) { //循环调用ConfigurationProvider的init和register方法,明白了吧,在这里统一循环调用 containerProvider.init(this); containerProvider.register(builder, props); } props.setConstants(builder); //注入依赖关系,在这里并不产生实例 builder.factory(Configuration.class, new Factory<Configuration>() { public Configuration create(Context context) throws Exception { return DefaultConfiguration.this; } }); ActionContext oldContext = ActionContext.getContext(); try { // Set the bootstrap container for the purposes of factory creation Container bootstrap = createBootstrapContainer(); setContext(bootstrap); //create已经注入依赖关系的Container container = builder.create(false); setContext(container); objectFactory = container.getInstance(ObjectFactory.class); // Process the configuration providers first for (final ContainerProvider containerProvider : providers) { if (containerProvider instanceof PackageProvider) { container.inject(containerProvider); //调用PackageProvider的loadPackages()方法,这里主要是针对XmlConfigurationProvider和StrutsXmlConfigurationProvider ((PackageProvider)containerProvider).loadPackages(); packageProviders.add((PackageProvider)containerProvider); } } // Then process any package providers from the plugins Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class); if (packageProviderNames != null) { for (String name : packageProviderNames) { PackageProvider provider = container.getInstance(PackageProvider.class, name); provider.init(this); provider.loadPackages(); packageProviders.add(provider); } } rebuildRuntimeConfiguration(); } finally { if (oldContext == null) { ActionContext.setContext(null); } } return packageProviders; }
- struts2架构中核心对象的探索—— Dispatcher&ConfigurationProvider
- struts2架构中核心对象的探索—— ActionContextClearUp
- struts2架构中核心对象的探索—— ActionContext和ServletActionContext
- Struts2中dispatcher与redirect的区别
- Struts2源码浅析-ConfigurationProvider
- Struts2源码浅析-ConfigurationProvider
- 简述Struts2中Redirect和Dispatcher的区别
- 简述Struts2中Redirect和Dispatcher的区别
- struts2中Ajax的操作实现及其核心对象XMLHttpRequest对象的使用
- Scrapy研究探索(三)——Scrapy核心架构与代码运行分析
- Scrapy研究探索(三)——Scrapy核心架构与代码运行分析
- Struts2 源码分析——调结者(Dispatcher)之执行action
- Struts2 源码分析——调结者(Dispatcher)之action请求
- Struts2 源码分析——调结者(Dispatcher)之准备工作
- struts2--Dispatcher
- Struts2中出现 Dispatcher initialization failed错误
- Struts2源码阅读(三)_Dispatcher&ConfigurationProvider
- Struts2源码阅读(四)_Dispatcher&ConfigurationProvider续
- 利用poi完成导出excel功能
- ubuntu Git升级
- TabLayout的使用
- 【选择排序】
- 产品需求文档分享:「病利贴」
- struts2架构中核心对象的探索—— Dispatcher&ConfigurationProvider
- vue2 + router + vuex + vux + axios 开发的一点总计
- redis在工程中的使用
- caffe-pvanet
- Java设计模式--观察者模式【Observer Pattern】
- 解析数据,设置图片的圆角,popwindow弹框
- springmvc限流拦截器
- 设计模式7大原则
- 在Windows服务器上启用TLS 1.2及TLS 1.2基本原理