Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Struts2-初始化流程 #6

Open
aCoder2013 opened this issue Aug 23, 2017 · 0 comments
Open

Struts2-初始化流程 #6

aCoder2013 opened this issue Aug 23, 2017 · 0 comments
Labels

Comments

@aCoder2013
Copy link
Owner

aCoder2013 commented Aug 23, 2017

Apache Struts is a free, open-source, MVC framework for creating elegant, modern Java web applications. It favors convention over configuration, is extensible using a plugin architecture, and ships with plugins to support REST, AJAX and JSON.

运行主线

入口程序

StrutsPrepareAndExecuteFilter是Struts2的入口点,实现了Filter和StrutsStatics接口,其中StrutsStatics定义了一些常量

public interface StrutsStatics {

    /**
     * Constant for the HTTP request object.
     */
    public static final String HTTP_REQUEST = "com.opensymphony.xwork2.dispatcher.HttpServletRequest";

   	...
   	...
    /**
     * Set as an attribute in the request to let other parts of the framework know that the invocation is happening inside an
     * action tag
     */
    public static final String STRUTS_ACTION_TAG_INVOCATION= "struts.actiontag.invocation";
}

而实现了Filter接口,让Struts2能够过滤请求,如静态资源、Servlet等,在doFilter()方法中实现过滤逻辑,而init()方法会在且只在Filter被初始化的时候被调用一次,让我们来看看StrutsPrepareAndExecuteFilter的init()方法

protected PrepareOperations prepare; 
protected ExecuteOperations execute;	
protected List<Pattern> excludedPatterns = null;

public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations();//类似一个工具类,包含了一些初始化操作
        Dispatcher dispatcher = null;//Dispatcher:Struts2的核心分发器
        try {
            /**
             * 封装filterConfig,提供了一个便利的方法
             * getInitParameterNames(),将枚举类型的参数转换成Iterator(EnumerationIterator)
             */
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            init.initLogging(config);//初始化日志
            //初始化Dispatcher
            dispatcher = init.initDispatcher(config);
            init.initStaticContentLoader(config, dispatcher);//初始化静态文件加载器

            prepare = new PrepareOperations(dispatcher);//初始化HTTP预处理的操作类
            execute = new ExecuteOperations(dispatcher);//初始化进行HTTP请求处理的逻辑执行操作类
            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

            postInit(dispatcher, filterConfig);//回调方法,留作用户拓展
        } finally {
            if (dispatcher != null) {
                dispatcher.cleanUpAfterInit();
            }
            init.cleanup();
        }
    }

初始化核心分发器:Dispatcher

init()方法主要是对Dispatcher,PrepareOperations,ExecuteOperations三个类进行初始化,其中Dispatcher在Struts2中占有很重要的地位,无论是初始化Struts2还是对HTTP请求的处理,同时也架起了Struts2和XWork之间的一道桥梁,因此我们先深入 dispatcher = init.initDispatcher(config); 这段代码看一看

    public Dispatcher initDispatcher( HostConfig filterConfig ) {
        Dispatcher dispatcher = createDispatcher(filterConfig);
        dispatcher.init();//初始化方法
        return dispatcher;
    }

    private Dispatcher createDispatcher( HostConfig filterConfig ) {
        //将filterConfig中的参数名值对封装到Map中
        Map<String, String> params = new HashMap<String, String>();
        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
            String name = (String) e.next();
            String value = filterConfig.getInitParameter(name);
            params.put(name, value);
        }
        return new Dispatcher(filterConfig.getServletContext(), params);
    }
    

上面的没什么,dispatcher.init();才是重头戏,继续深入

public void init() {
        //初始化配置文件管理器
    	if (configurationManager == null) {
            //根据name进行对象寻址
            //DEFAULT_BEAN_NAME = "struts"
            //<bean type="org.apache.struts2.dispatcher.DispatcherErrorHandler" name="struts".../>
            //<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
            configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
    	}

        try {
            init_FileManager(); //初始化文件管理器

            // 初始化Struct2的默认配置加载器:
            // org/apache/struts2/default.properties,
            // 如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
            init_DefaultProperties(); // [1]
            //初始化Xml配置加载器:
            // 如struts-default.xml,struts-plugin.xml,struts.xml
            init_TraditionalXmlConfigurations(); // [2]
            //初始化Properties配置加载器
            init_LegacyStrutsProperties(); // [3]
            //初始化用户自定义的配置加载器
            init_CustomConfigurationProviders(); // [5]
            //初始化由web.xml传入的参数
            init_FilterInitParameters() ; // [6]
            //初始化容器内置的对象
            //eg:ObjectFactory,FreemarkerManager....
            init_AliasStandardObjects() ; // [7]
            //创建容器, 初始化并预加载配置
            Container container = init_PreloadConfiguration();
            //对容器进行依赖注入
            container.inject(this);
            //检查对WebLogic的特殊支持
            init_CheckWebLogicWorkaround(container);
            //初始化所有的DispatcherListener
            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);
        }
    }

    private void init_DefaultProperties() {
        configurationManager.addContainerProvider(new DefaultPropertiesProvider());
    }
    
    private void init_LegacyStrutsProperties() {
        configurationManager.addContainerProvider(new PropertiesConfigurationProvider());
    }

    private void init_TraditionalXmlConfigurations() {
        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)) {
                    configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
                } else {
                    configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
                }
            } else {
                throw new IllegalArgumentException("Invalid configuration file name");
            }
        }
    }
    //...其他的省略

ConfigurationProvider

所有的初始化方法都是以init_开头,其核心只不过是调用configurationManager#addContainerProvider()方法,那到底什么是配置元素加载器(ContainerProvider)呢,比如init_DefaultProperties(),看一下DefaultPropertiesProvider的继承关系

public class DefaultPropertiesProvider extends PropertiesConfigurationProvider
	->public class PropertiesConfigurationProvider implements ConfigurationProvider
		 ->public interface ConfigurationProvider extends ContainerProvider, PackageProvider

其他的ContainerProvider也都实现了ConfigurationProvider这个接口,我们知道Struts2的配置文件形式有很多种,比如.xml,.properties等,所以Struts2就定义了ConfigurationProvider这个统一的接口,让框架支持处理所有的配置形式,而每一个ContainerProvider的实现类都可以根据不同的配置文件的特点进行设计。
同时ConfigurationProvider继承了ContainerProvider和PackageProvider两个接口,ContainerProvider的子类有:FileManagerFactoryProvider,StubConfigurationProvider, XmlConfigurationProvider, BeanSelectionProvider等,它的用途大概就是处理诸如XML,Properties等格式的配置文件。而PackageProvider的操作对象是PackageConfig,从源码可以看出,PackageConfig对应了XML配置文件中的package节点,这样PackageProvider的作用也不言而喻

public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {

    private static final Logger LOG = LoggerFactory.getLogger(PackageConfig.class);

    protected Map<String, ActionConfig> actionConfigs;
    protected Map<String, ResultConfig> globalResultConfigs;
    protected Map<String, Object> interceptorConfigs;
    protected Map<String, ResultTypeConfig> resultTypeConfigs;
    protected List<ExceptionMappingConfig> globalExceptionMappingConfigs;
    protected List<PackageConfig> parents;
    protected String defaultInterceptorRef;
    protected String defaultActionRef;
    protected String defaultResultType;
    protected String defaultClassRef;
    protected String name;
    protected String namespace = "";
    protected boolean isAbstract = false;
    protected boolean needsRefresh;
    ...
}

初始化容器

Struts2中的所有内置对象都会交给Container去管理,比如XML中的Bean,Constant节点以及Properties文件中的参数,Container的实现类会扫描@Inject注解,进行依赖注入,下面我们看看Container container = init_PreloadConfiguration();这行代码做了什么事情

    private Container init_PreloadConfiguration() {
        Container container = getContainer();

        boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
        LocalizedTextUtil.setReloadBundles(reloadi18n);

        boolean devMode = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_DEVMODE));
        LocalizedTextUtil.setDevMode(devMode);

        return container;
    }
public Container getContainer() {
        if (ContainerHolder.get() != null) {
            return ContainerHolder.get();
        }
        //ConfigurationManager类对所有的配置管理中心
        ConfigurationManager mgr = getConfigurationManager();
        if (mgr == null) {
            throw new IllegalStateException("The configuration manager shouldn't be null");
        } else {
            Configuration config = mgr.getConfiguration();
            if (config == null) {
                throw new IllegalStateException("Unable to load configuration");
            } else {
                Container container = config.getContainer();
                ContainerHolder.store(container);
                return container;
            }
        }
    }

我们再看看Container的实现类ContainerImpl,它的内部缓存了两个实例factories和factoryNamesByType,而factories根据Key缓存了不同对象的制造工厂,Key中有两个变量:type,name,factoryNamesByType则在factories基础之上根据名称进行寻址。
getInstance()方法的每次调用,都会根据传进来的type,class构造一个Key对象,然后到factories中查找到对应的工厂类,调用Factory的create()方法,创建对象

class ContainerImpl implements Container {

	final Map<Key<?>, InternalFactory<?>> factories;
	final Map<Class<?>, Set<String>> factoryNamesByType;

	@SuppressWarnings("unchecked")
	<T> T getInstance( Class<T> type, String name, InternalContext context ) {
		ExternalContext<?> previous = context.getExternalContext();
		Key<T> key = Key.newInstance(type, name);
		context.setExternalContext(ExternalContext.newInstance(null, key, this));
		try {
			InternalFactory o = getFactory(key);
			if (o != null) {
				return getFactory(key).create(context);
			} else {
				return null;
			}
		} finally {
			context.setExternalContext(previous);
		}
	}

	<T> T getInstance( Class<T> type, InternalContext context ) {
		return getInstance(type, DEFAULT_NAME, context);
	}
	/..
}
class Key<T> {

  final Class<T> type;
  final String name;
  final int hashCode;
  ..
}
interface InternalFactory<T> extends Serializable {

  /**
   * Creates an object to be injected.
   *
   * @param context of this injection
   * @return instance to be injected
   */
  T create(InternalContext context);
}
    <bean type="com.opensymphony.xwork2.factory.ActionFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultActionFactory" />
    <bean type="com.opensymphony.xwork2.factory.ConverterFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultConverterFactory" />
    <bean type="com.opensymphony.xwork2.factory.InterceptorFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultInterceptorFactory" />
    <bean type="com.opensymphony.xwork2.factory.ValidatorFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultValidatorFactory" />
    <bean type="com.opensymphony.xwork2.factory.UnknownHandlerFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory" />

PrepareOperations和ExecuteOperations分析

从源码中可以看出,PrepareOperations负责创建ActionContext,清理Request,设置编码等

pre_method

ExecuteOperations则只有两个方法,负责真正的执行操作,executeStaticResourceRequest()负责静态资源,executeAction()是一个代理方法,将真正的执行交给Dispatcher.serviceAction()方法

 public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        // there is no action in this request, should we look for a static resource?
        String resourcePath = RequestUtils.getServletPath(request);

        if ("".equals(resourcePath) && null != request.getPathInfo()) {
            resourcePath = request.getPathInfo();
        }

        StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
        if (staticResourceLoader.canHandle(resourcePath)) {
            staticResourceLoader.findStaticResource(resourcePath, request, response);
            // The framework did its job here
            return true;

        } else {
            // this is a normal request, let it pass through
            return false;
        }
    }

    /**
     * Executes an action
     * @throws ServletException
     */
    public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
        dispatcher.serviceAction(request, response, mapping);
    }

总结

  1. 在Servlet容器(Jetty,Tomcat...)初始化的时候,加载web.xml初始化Filter
  2. 初始化StrutsPrepareAndExecuteFilter,调用init()方法
    (1) 封装FilterConfig->FilterHostConfig
    (2) 初始化日志操作
    (3) 初始化Dispatcher
    (4) 初始化PrepareOperations和ExecuteOperations

Flag Counter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant