zoukankan      html  css  js  c++  java
  • java 三大框架——Struts²

    Struts2是一种基于MVC模式的javaWeb框架,本质上相当于Servlet。

    所谓MVC,就是模型-视图-控制器。

    Model(模型)表示应用程序核心(比如数据库记录列表)。

    View(视图)显示数据(数据库记录)。

    Controller(控制器)处理输入(写入数据库记录)

    而Struts的作用实际上是作为控制器,建立模型层和视图层的数据交互(就是通常所说控制model和jsp之间的数据交互)

    上面都是废话,你只要知道struts2能控制model和jsp之间的数据交互即可。

    web程序想要使用struts2框架,除了要引用相关jar包外,还要在web.xml中对struts2进行配置,只有配置后,struts2才能对浏览器的请求进行一系列处理。

    怎么在web应用中配置struts2?

    首先在web.xml中添加struts2的使用,代码如下:


    <!-- 配置Struts2 核心 Filter -->
    <filter>
    <filter-name>action2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
    <filter-name>action2</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    上述代码使用了Filter,那我们就回顾一下Filter的作用。

    回顾:Filter,中文名为过滤器,通过Filter可以对web服务器的资源进行管理,例如Jsp,Servlet, 静态图片文件等进行拦截,从而实现一些特殊的功能。

    实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能

    从代码层面来讲,只要创建一个实现了Filter接口的实现类,然后将这个类在web.xml进行配置,那么这个类就可以让用户在访问某个目标资源之前,对访问的请求和响应进行拦截。

    更多介绍可以看我的另外一篇文章:http://blog.csdn.net/u012605477/article/details/75258358

    注意:Struts2在web.xml中看来就是一个Filter,只不过其实现类StrutsPrepareAndExecuteFilter不是我们写的,是Struts2的。

    为了验证StrutsPrepareAndExecuteFilter也实现了Filter接口,我们将其和我们自写的过滤器进行对比:

    下面是自写的过滤器FilterA配置:

    <filter>
    <filter-name>FilterA</filter-name>
    <filter-class>com.strategy.jpa.FilterA</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>FilterA</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    自写FilterA的代码:
    package com.strategy.jpa;

    import java.io.IOException;

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;

    public class FilterA implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {


    chain.doFilter(request, response);
    response.reset();

    }

    public void destroy() {

    }

    }
    web中配置Struts2的代码:
    * $Id: DefaultActionSupport.java 651946 2008-04-27 13:41:38Z apetrelli $
    package org.apache.struts2.dispatcher.filter;

    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.struts2.RequestUtils;
    import org.apache.struts2.StrutsStatics;
    import org.apache.struts2.dispatcher.Dispatcher;
    import org.apache.struts2.dispatcher.mapper.ActionMapping;
    import org.apache.struts2.dispatcher.ExecuteOperations;
    import org.apache.struts2.dispatcher.InitOperations;
    import org.apache.struts2.dispatcher.PrepareOperations;

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.List;
    import java.util.regex.Pattern;

    /**
    * Handles both the preparation and execution phases of the Struts dispatching process. This filter is better to use
    * when you don't have another filter that needs access to action context information, such as Sitemesh.
    */
    public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {

    private static final Logger LOG = LogManager.getLogger(StrutsPrepareAndExecuteFilter.class);

    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;
    try {
    FilterHostConfig config = new FilterHostConfig(filterConfig);
    init.initLogging(config);
    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();
    }
    }

    /**
    * Callback for post initialization
    *
    * @param dispatcher the dispatcher
    * @param filterConfig the filter config
    */
    protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    try {
    String uri = RequestUtils.getUri(request);
    if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
    LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
    chain.doFilter(request, response);
    } else {
    LOG.trace("Checking if {} is a static resource", uri);
    boolean handled = execute.executeStaticResourceRequest(request, response);
    if (!handled) {
    LOG.trace("Assuming uri {} as a normal action", uri);
    prepare.setEncodingAndLocale(request, response);
    prepare.createActionContext(request, response);
    prepare.assignDispatcherToThread();
    request = prepare.wrapRequest(request);
    ActionMapping mapping = prepare.findActionMapping(request, response, true);
    if (mapping == null) {
    LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
    chain.doFilter(request, response);
    } else {
    LOG.trace("Found mapping {} for {}", mapping, uri);
    execute.executeAction(request, response, mapping);
    }
    }
    }
    } finally {
    prepare.cleanupRequest(request);
    }
    }

    public void destroy() {
    prepare.cleanupDispatcher();
    }

    }
    对比可知,struts2也实现了Filter接口,只是他比我们自己写的FilterA多实现了一个接口StrutsStatics,其它的没有变化。
    我们对上面的代码进行了解:

    在Init方法里面,我们看到有一个config对象,该类对象的作用是对FilterConfig进行封装。

    package org.apache.struts2.dispatcher.filter;

    import org.apache.struts2.util.MakeIterator;

    import javax.servlet.FilterConfig;
    import javax.servlet.ServletContext;
    import java.util.Iterator;

    import org.apache.struts2.dispatcher.HostConfig;

    /**
    * Host configuration that wraps 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();
    }
    }
    然后又在Init方法里面通过创建的config对象,并调用initDispatcher方法创建了转发器对象dispatcher对象。以及通过initLogging方法初始化日志记录器。

    private Dispatcher createDispatcher( HostConfig filterConfig ) {
    Map<String, String> params = new HashMap<>();
    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 cleanup() {
    ActionContext.setContext(null);
    }
    public void initLogging( HostConfig filterConfig ) {
    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();
    }
    }
    }
    接下来调用了init对象initStaticContentLoader(config, dispatcher);方法加载一些静态资源。

    public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
    StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
    loader.setHostConfig(filterConfig);
    return loader;
    }
    最重要的就是dispatcher,它主要将filter拦截到的请求转入struts2的请求处理模块,我们必须知道这一点。

    其次是FilterConfig,它将我们在web.xml中的Filter的配置信息也保存到了dispatcher中。下面是该对象的构造方法。

    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {
    this.servletContext = servletContext;
    this.initParams = initParams;
    }
    接下来就是prepare和execute对象,和InitOperations类似,也是进行了封装一些操作,都是截取一部分重要的代码。

    public PrepareOperations(Dispatcher dispatcher) {
    this.dispatcher = dispatcher;
    }
    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
    ActionContext ctx;
    Integer counter = 1;
    Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
    if (oldCounter != null) {
    counter = oldCounter + 1;
    }

    ActionContext oldContext = ActionContext.getContext();
    if (oldContext != null) {
    // detected existing context, so we are probably in a forward
    ctx = new ActionContext(new HashMap<>(oldContext.getContextMap()));
    } else {
    ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
    stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
    ctx = new ActionContext(stack.getContext());
    }
    request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
    ActionContext.setContext(ctx);
    return ctx;
    }
    execute对象
    public ExecuteOperations(Dispatcher dispatcher) {
    this.dispatcher = dispatcher;
    }

    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;
    }
    }
    从上面的代码可以看出,这两个对象封装了请求预处理和请求处理的操作,当处理请求时方法被调用

    在上述的代码中有个很重要的方法未罗列出来,那就是Disptcher中得init方法,该方法初始读取一些配置文件,包含我们想要知道的读取struts2.xml的方法。

    public void init() {

    if (configurationManager == null) {
    configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
    }

    try {
    init_FileManager();
    init_DefaultProperties(); // [1]
    init_TraditionalXmlConfigurations(); // [2]
    init_LegacyStrutsProperties(); // [3]
    init_CustomConfigurationProviders(); // [5]
    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) {
    LOG.error("Dispatcher initialization failed", ex);
    throw new StrutsException(ex);
    }
    }
    上面是init方法,里面调用了诸多读取配置文件的方法,包含我们想要知道的读取struts.xml的方法:
    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");
    }
    }
    }
    在上面的代码中,设定了读取struts.xml的默认路径,其内容如下:
    private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml";
    至此,web应用配置struts2框架,以及struts2如何将Filter封装成struts2的,甚至如何读取struts.xml以及其他配置,并进行相关struts2参数初始化都有了大概了解,接下来说一下Action。
    回到StrutsPrepareAndExecuteFilter类的doFilter中,

    //每次发送一个Request,StrutsPrepareAndExecuteFilter都会调用doFilter方法
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    try {
    //设置编码和国际化
    prepare.setEncodingAndLocale(request, response);
    //ActionContext创建
    prepare.createActionContext(request, response);
    prepare.assignDispatcherToThread();
    if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
    chain.doFilter(request, response);
    } else {
    request = prepare.wrapRequest(request);
    ActionMapping mapping = prepare.findActionMapping(request, response, true);
    //如果找不到对应的action配置
    if (mapping == null) {
    /*
    * 就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,
    * 当然.class文件除外。如果再没有则跳转到404
    */
    boolean handled = execute.executeStaticResourceRequest(request, response);
    if (!handled) {
    chain.doFilter(request, response);
    }
    } else {
    /*
    * 找到对应action配置文件后,调用ExecuteOperations类中executeAction,
    * 开始谳用Action的方法。
    */
    execute.executeAction(request, response, mapping);
    }
    }
    } finally {
    prepare.cleanupRequest(request);
    }
    }
    相关准备(prepare)方法,设置编码的方法不再叙述(setEncodingAndLocale),我们主要说一下ActionContext。
    prepare.createActionContext(request, response);

    ActionContext是一个struts2容器,主要存储request、session、application、parameters等相关信息。

    ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问题

    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
    ActionContext ctx;
    Integer counter = 1;
    Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
    if (oldCounter != null) {
    counter = oldCounter + 1;
    }

    ActionContext oldContext = ActionContext.getContext();
    if (oldContext != null) {
    // detected existing context, so we are probably in a forward
    ctx = new ActionContext(new HashMap<>(oldContext.getContextMap()));
    } else {
    ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
    stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
    ctx = new ActionContext(stack.getContext());
    }
    request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
    ActionContext.setContext(ctx);
    return ctx;
    }

    我们再看一下ActionContext是什么,取部分代码,由此可知ActionContext是一个键值对集合,跟Spring中的bean类似:
    public class ActionContext implements Serializable {

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

    private Map<String, Object> context;

    public ActionContext(Map<String, Object> context) {
    this.context = context;
    }
    有了创建Action的方法,肯定有使用Action的方法,我们现在去找使用的方法:
    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
    throws ServletException {

    Map<String, Object> extraContext = createContextMap(request, response, mapping);

    // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
    ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
    boolean nullStack = stack == null;
    if (nullStack) {
    ActionContext ctx = ActionContext.getContext();
    if (ctx != null) {
    stack = ctx.getValueStack();
    }
    }
    if (stack != null) {
    extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
    }

    String timerKey = "Handling request from Dispatcher";
    try {
    UtilTimerStack.push(timerKey);
    String namespace = mapping.getNamespace();
    String name = mapping.getName();
    String method = mapping.getMethod();

    ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
    namespace, name, method, extraContext, true, false);

    request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

    // if the ActionMapping says to go straight to a result, do it!
    if (mapping.getResult() != null) {
    Result result = mapping.getResult();
    result.execute(proxy.getInvocation());
    } else {
    proxy.execute();
    }

    // If there was a previous value stack then set it back onto the request
    if (!nullStack) {
    request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
    }
    } catch (ConfigurationException e) {
    logConfigurationException(request, e);
    sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
    } catch (Exception e) {
    if (handleException || devMode) {
    sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
    } else {
    throw new ServletException(e);
    }
    } finally {
    UtilTimerStack.pop(timerKey);
    }
    }
    在上面的try语句里面我们可以得知,struts是怎么通过命名空间,读取action的配置,以及action的method方法,

    尤其是如何从容器中获得ActionProxyFactory代理工厂 ,是怎么创建ActionProxy来执行一个特定的命名空间和动作的。

    以及怎么通过我们在struts2中设定的跳转方法,跳转到指定页面的。

    //执行execute方法,并转向结果
    if (mapping.getResult() != null) {
    Result result = mapping.getResult();
    result.execute(proxy.getInvocation());
    } else {
    proxy.execute();
    }
    至此,对于Struts2是如何工作的,怎么读取xml的,如何搭建在web应用中基本叙述完了,更详细的实在说不完,各位大佬可以看下下面这位大神的博客,很给力,就是看起来太繁琐

    原文链接:https://blog.csdn.net/u012605477/article/details/76129675

  • 相关阅读:
    windows下用Python把pdf文件转化为图片(png格式)
    SQL优化实战:外层查询条件放到内层查询中(predicate push down)
    SQL优化实战:临时表+分批提交+按日结存
    SQL优化:重新编译存储过程和表
    论坛中的问题:47 数据库的事务是100%的吗?
    代理模式
    建造者模式
    工厂模式
    面向对向设计
    单例设计模式
  • 原文地址:https://www.cnblogs.com/hewanli/p/12271961.html
Copyright © 2011-2022 走看看