struts2源码学习笔记(一)

上篇文章已经介绍了struts2的简单使用,现在开始源码的学习。

本篇主要介绍struts2的初始化。对应的源码为StrutsPrepareAndExecuteFilter中的init方法。

先贴源码:

public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { //这里的StrutsStatics接口中没有方法,只有常量
    protected PrepareOperations prepare;   //封装http预处理的操作的对象
    protected ExecuteOperations execute;   //封装http处理的操作的对象
    protected List<Pattern> excludedPatterns = null; //封装配置filter时指定的不处理的action请求的pattern成一个集合

    public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations(); --封装了一些初始化的操作
        Dispatcher dispatcher = null; --一个转发器
        try {
            FilterHostConfig config = new FilterHostConfig(filterConfig); --这里主要是对filterConfig进行了简单的封装
            init.initLogging(config); --如果用户在web.xml文件中配置了loggerFactory,初始化日志记录器
            dispatcher = init.initDispatcher(config); --初始化转发器
            init.initStaticContentLoader(config, dispatcher); --初始化静态资源加载器

            prepare = new PrepareOperations(dispatcher);
            execute = new ExecuteOperations(dispatcher);
            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); 

            postInit(dispatcher, filterConfig); --回调方法,用做用户拓展
        } finally {
            if (dispatcher != null) {
                dispatcher.cleanUpAfterInit();
            }
            init.cleanup();  --垃圾清理
        }
    }
一个个对以上的对象跟方法进行分析:

1. InitOperations init = new InitOperations(); --封装了一些初始化的操作
   源码中的大纲视图(具体方法下文会详细讲解):
从这个视图我们可以发现,这个对象中封装的都是一些初始化的方法(从名字也不难发现),对照我们

StrutsPrepareAndExecuteFilter中的init方法的源码,也可以得出相同的结论。

  2.FilterHostConfig config = new FilterHostConfig(filterConfig);
 --这里主要是对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 Iterator<String> getInitParameterNames() {  ---------- 只有这个方法真正做了事情
        return MakeIterator.convert(config.getInitParameterNames());
    }

    public ServletContext getServletContext() {
        return config.getServletContext();
    }
}
  可以发现,这个对象只是对FiterConfig进行了简单的封装,

  getInitParameterNames(),这个方法,将枚举类型的参数换成了Iterator

 
 
 
 
 
 
3.init.initLogging(config); --初始化日志记录器

 public void initLogging( HostConfig filterConfig ) {
        // 如果在配置文件中没有配置loggerFactory,这个方法什么都不会做
        String factoryName = filterConfig.getInitParameter("loggerFactory");
        if (factoryName != null) {
            try {
                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();
            }
        }
    }
 
 
 
 
4.dispatcher = init.initDispatcher(config);初始化转发器

    这个类是struts中很重要的一个类,它的工作就是将filter拦截到的请求转发

struts2的请求处理模块。这句代码的作用是初始化一个转发器,或者叫分发器。

源码如下:

 一:
  // 这部分代码,只是执行了创建了一个dispatcher跟执行dispatcher的init方法,并返回了一个对象,
  //  所以我们继续跟踪dcreateDispatcher跟ispatcher的init方法
 public Dispatcher initDispatcher( HostConfig filterConfig ) {
        Dispatcher dispatcher = createDispatcher(filterConfig);
        dispatcher.init();
        return dispatcher;
    }
二:
 private Dispatcher createDispatcher( HostConfig filterConfig ) {
        Map<String, String> params = new HashMap<String, String>();
// 这里实际就是将我们在filter配置的参数跟ServletContext对象保存到dispatcher对象中
        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);
    }
 
三:
源码如下:
 public void init() {
     if (configurationManager == null) {
      configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
     }

        try {
            // 初始化文件管理器
            init_FileManager();
            // 初始化struts2的默认配置加载器
            init_DefaultProperties(); // [1]
            // 初始化xml配置加载器
            init_TraditionalXmlConfigurations(); // [2]
            // 初始化Properties配置加载器
            init_LegacyStrutsProperties(); // [3]
            // 初始化用户自定义的配置加载器
            init_CustomConfigurationProviders(); // [5]
            // 初始化由web.xml传入的参数
            init_FilterInitParameters() ; // [6]
            // 初始化容器内置的对象
            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);
        }
    }
由于篇幅原因,这篇文章中暂时不对这段代码做详细的解释,只做一些简单的注释,下篇文章专门解释这段代码

        5.init.initStaticContentLoader(config, dispatcher); --初始化静态资源加载器

        在完成对dispatcher对象的初始化后,strtus2维护的一个容器Container就创建完成了,可以使用了,

        这里通过dispatcher对象获取一个container的实例(container本身是一个接口),再通过containerd的

        getInstance方法获取了一个StaticContentLoader对象。

 public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
        StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
        loader.setHostConfig(filterConfig);
        return loader;
    }

        我们发现StaticContentLoader也是一个接口,继续看它声明的方法

        查阅大纲视图:

再查找它的实现类DefaultStaticContentLoader

(只有这一个实现类)

 public boolean canHandle(String resourcePath) {
        return serveStatic && (resourcePath.startsWith("/struts/") || resourcePath.startsWith("/static/"));
    }
从这段代码中可以看出,只有请求路径中包含struts或者static时这个方法才会返回true

            实际上,在一般情况下strtus2是不会处理静态资源请求的,除非请求的路径是以struts或static开头,如

有个test应用,那么请求必须是/test/struts/...或者/test/static/...这样才会处理。

在StrutsPrepareAndExecuteFilter中的有如下代码:


很明显,当判断为不需要处理时,会直接放行。

   6.       prepare = new PrepareOperations(dispatcher);  --实例化http请求预处理对象
            execute = new ExecuteOperations(dispatcher);  --示例化http请求处理对象

                PrepareOperations和ExecuteOperations有什么用呢?和InitOperations类似,封装一些操作。只

不过InitOperations是封装初始化的操作,而前两者则是封装请求预处理和请求处理的操作,当处理请求时方法

被调用。先看PrepareOperations有哪些操作,查看源码大纲视图如下:

其次是ExecuteOperations的源码,相对来说简单很多:

public class ExecuteOperations {

    private Dispatcher dispatcher;

    @Deprecated
    public ExecuteOperations(ServletContext servletContext, Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
    }

    public ExecuteOperations(Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
    }

    /**
     * Tries to execute a request for a static resource
     * @return True if it was handled, false if the filter should fall through
     * @throws IOException
     * @throws ServletException
     */
    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);
    }
}

这里对这两段源码也不做详细讲解,下一节再细说。

        7.this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

        封装配置filter时指定的不处理的action请求的pattern成List<Pattern>,源码如下

    public List<Pattern> buildExcludedPatternsList( Dispatcher dispatcher ) {
        return buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN));
    }
            
    private List<Pattern> buildExcludedPatternsList( String patterns ) {
        if (null != patterns && patterns.trim().length() != 0) {
            List<Pattern> list = new ArrayList<Pattern>();
            String[] tokens = patterns.split(",");
            for ( String token : tokens ) {
                list.add(Pattern.compile(token.trim()));
            }
            return Collections.unmodifiableList(list);
        } else {
            return null;
        }
    }
         8.postInit(dispatcher, filterConfig); --回调方法,用做用户拓展

        这个方法实际上什么都没做,源码如下:

          protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
          }

        9.init.cleanup();--垃圾清理

        源码如下:

public void cleanup() {
        ActionContext.setContext(null);
        }
    public static void setContext(ActionContext context) {
        actionContext.set(context);
    }

    实际上就是将actionContext中的ActionContext对象置为null,我们跟踪actionContext这个对象发现,它其

实是一个本地线程变量,所以这个操作实际上就是在清空线程中的ActionContext对象,这里不对这个对象做太

多说明,接下来的文章会详细介绍。

public class ActionContext implements Serializable {

    static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();

    源码的第一篇学习笔记就写到这。希望多多交流,有误的地方欢迎大家指正!




猜你喜欢

转载自blog.csdn.net/qq_41907991/article/details/80633398