`

一步一步跟我学Struts2 —— Result机制,让视图更丰富

阅读更多

专栏地址:http://www.iteye.com/wiki/struts2/1462-result-in-struts2

Struts2将Result列为一个独立的层次,可以说是整个Struts2的Action层架构设计中的另外一个精华所在。Result之所以成为一个层次,其实是为了解决MVC框架中,如何从Control层转向View层这样一个问题而存在的。所以,接下来我们详细讨论一下Result的方方面面。

Result的职责

Result作为一个独立的层次存在,必然有其存在的价值,它也必须完成它所在的层次的职责。Result是为了解决如何从Control层转向View层这样一个问题而存在的,那么Result最大的职责,就是架起Action到View的桥梁。具体来说,我把这些职责大概分成以下几个方面:

封装跳转逻辑

Result的首要职责,是封装Action层到View层的跳转逻辑。之前我们已经反复提到,Struts2的Action是一个与Web容器无关的POJO。所以,在Action执行完毕之后,框架需要把代码的执行权重新交还给Web容器,并转向到相应的页面或者其他类型的View层。
而这个跳转逻辑,就由Result来完成。这样,好处也是显而易见的,对Action屏蔽任何Web容器的相关信息,使得每个层次更加清晰。

View层的显示类型非常多,有最常见的JSP、当下非常流行的Freemarker/Velocity模板、Redirect到一个新的地址、文本流、图片流、甚至是JSON对象等等。所以Result层的独立存在,就能够对这些显示类型进行区分,并封装合理的跳转逻辑。

以JSP转向为例,在Struts2自带的ServletDispatcherResult中就存在着核心的JSP跳转逻辑:

Java代码 复制代码
  1. HttpServletRequest request = ServletActionContext.getRequest();   
  2. RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);   
  3.   
  4. ....   
  5.   
  6. dispatcher.forward(request, response);  
HttpServletRequest request = ServletActionContext.getRequest();
RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);

....

dispatcher.forward(request, response);



再以Redirect重定向为例,在Struts2自带的ServletRedirectResult中,也同样存在着重定向的核心代码:

Java代码 复制代码
  1. HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);   
  2.   
  3. ....   
  4.   
  5. response.sendRedirect(finalLocation);  
HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);

....

response.sendRedirect(finalLocation);



由此可见,绝大多数的Result,都封装了与Web容器相关的跳转逻辑,由于这些逻辑往往需要和Servlet对象打交道,所以,遵循Struts2的基本原则,将它作为一个独立的层次,从而将Action从Web容器中解放出来。

准备显示数据

之前提到,View层的展现方式很多,除了传统的JSP以外,还有类似Freemarker/Velocity这样的模板。根据模板显示的基本原理,需要将预先定义好的模板(Template)和需要展示的数据(Model)组织起来,交给模板引擎,才能够正确显示。而这部分工作,就由Result层来完成。

以Struts2自带的FreemarkerResult为例,在Result中,就存在着为模板准备数据的逻辑代码:

Java代码 复制代码
  1. protected TemplateModel createModel() throws TemplateModelException {   
  2.     ServletContext servletContext = ServletActionContext.getServletContext();   
  3.     HttpServletRequest request = ServletActionContext.getRequest();   
  4.     HttpServletResponse response = ServletActionContext.getResponse();   
  5.     ValueStack stack = ServletActionContext.getContext().getValueStack();   
  6.   
  7.     Object action = null;   
  8.     if(invocation!= null ) action = invocation.getAction(); //Added for NullPointException   
  9.     return freemarkerManager.buildTemplateModel(stack, action, servletContext, request, response, wrapper);   
  10. }  
protected TemplateModel createModel() throws TemplateModelException {
    ServletContext servletContext = ServletActionContext.getServletContext();
    HttpServletRequest request = ServletActionContext.getRequest();
    HttpServletResponse response = ServletActionContext.getResponse();
    ValueStack stack = ServletActionContext.getContext().getValueStack();

    Object action = null;
    if(invocation!= null ) action = invocation.getAction(); //Added for NullPointException
    return freemarkerManager.buildTemplateModel(stack, action, servletContext, request, response, wrapper);
}



有兴趣的读者可以顺着思路去看源码,看看这些Result到底是如何获取各种对象的值的。

控制输出行为

有的时候,针对同一种类型的View展示,我们可能会有不同的输出行为。具体来说,可能有时候,我们需要对输出流指定特定的BufferSize、Encoding等等。Result层,作为一个独立的层次,可以提供极大的扩展性,从而保证我们能够定义自己期望的输出类型。

以Struts2自带的HttpHeaderResult为例:

Java代码 复制代码
  1. public void execute(ActionInvocation invocation) throws Exception {   
  2.     HttpServletResponse response = ServletActionContext.getResponse();   
  3.   
  4.     if (status != -1) {   
  5.         response.setStatus(status);   
  6.     }   
  7.   
  8.     if (headers != null) {   
  9.         ValueStack stack = ActionContext.getContext().getValueStack();   
  10.   
  11.         for (Iterator iterator = headers.entrySet().iterator();   
  12.              iterator.hasNext();) {   
  13.             Map.Entry entry = (Map.Entry) iterator.next();   
  14.             String value = (String) entry.getValue();   
  15.             String finalValue = parse ? TextParseUtil.translateVariables(value, stack) : value;   
  16.             response.addHeader((String) entry.getKey(), finalValue);   
  17.         }   
  18.     }   
  19. }  
public void execute(ActionInvocation invocation) throws Exception {
    HttpServletResponse response = ServletActionContext.getResponse();

    if (status != -1) {
        response.setStatus(status);
    }

    if (headers != null) {
        ValueStack stack = ActionContext.getContext().getValueStack();

        for (Iterator iterator = headers.entrySet().iterator();
             iterator.hasNext();) {
            Map.Entry entry = (Map.Entry) iterator.next();
            String value = (String) entry.getValue();
            String finalValue = parse ? TextParseUtil.translateVariables(value, stack) : value;
            response.addHeader((String) entry.getKey(), finalValue);
        }
    }
}



我们可以在这里添加我们自定义的内容到HttpHeader中去,从而控制Http的输出。

Result的定义

让我们来看看Result的接口定义:

Java代码 复制代码
  1. public interface Result extends Serializable {   
  2.   
  3.     /**  
  4.      * Represents a generic interface for all action execution results, whether that be displaying a webpage, generating  
  5.      * an email, sending a JMS message, etc.  
  6.      */  
  7.     public void execute(ActionInvocation invocation) throws Exception;   
  8. }  
public interface Result extends Serializable {

    /**
     * Represents a generic interface for all action execution results, whether that be displaying a webpage, generating
     * an email, sending a JMS message, etc.
     */
    public void execute(ActionInvocation invocation) throws Exception;
}



这个接口定义非常简单,通过传入ActionInvocation,执行一段逻辑。我们来看看Struts2针对这个接口实现的一个抽象类,它规定了许多默认实现:

Java代码 复制代码
  1. public abstract class StrutsResultSupport implements Result, StrutsStatics {   
  2.   
  3.     private static final Log _log = LogFactory.getLog(StrutsResultSupport.class);   
  4.   
  5.     /** The default parameter */  
  6.     public static final String DEFAULT_PARAM = "location";   
  7.   
  8.     private boolean parse;   
  9.     private boolean encode;   
  10.     private String location;   
  11.     private String lastFinalLocation;   
  12.   
  13.     public StrutsResultSupport() {   
  14.         this(nulltruefalse);   
  15.     }   
  16.   
  17.     public StrutsResultSupport(String location) {   
  18.         this(location, truefalse);   
  19.     }   
  20.   
  21.     public StrutsResultSupport(String location, boolean parse, boolean encode) {   
  22.         this.location = location;   
  23.         this.parse = parse;   
  24.         this.encode = encode;   
  25.     }   
  26.   
  27.     // setter method 省略   
  28.     
  29.     /**  
  30.      * Implementation of the <tt>execute</tt> method from the <tt>Result</tt> interface. This will call  
  31.      * the abstract method {@link #doExecute(String, ActionInvocation)} after optionally evaluating the  
  32.      * location as an OGNL evaluation.  
  33.      *  
  34.      * @param invocation the execution state of the action.  
  35.      * @throws Exception if an error occurs while executing the result.  
  36.      */  
  37.     public void execute(ActionInvocation invocation) throws Exception {   
  38.         lastFinalLocation = conditionalParse(location, invocation);   
  39.         doExecute(lastFinalLocation, invocation);   
  40.     }   
  41.   
  42.     /**  
  43.      * Parses the parameter for OGNL expressions against the valuestack  
  44.      *  
  45.      * @param param The parameter value  
  46.      * @param invocation The action invocation instance  
  47.      * @return The resulting string  
  48.      */  
  49.     protected String conditionalParse(String param, ActionInvocation invocation) {   
  50.         if (parse && param != null && invocation != null) {   
  51.             return TextParseUtil.translateVariables(param, invocation.getStack(),   
  52.                     new TextParseUtil.ParsedValueEvaluator() {   
  53.                         public Object evaluate(Object parsedValue) {   
  54.                             if (encode) {   
  55.                                 if (parsedValue != null) {   
  56.                                     try {   
  57.                                         // use UTF-8 as this is the recommended encoding by W3C to   
  58.                                         // avoid incompatibilities.   
  59.                                         return URLEncoder.encode(parsedValue.toString(), "UTF-8");   
  60.                                     }   
  61.                                     catch(UnsupportedEncodingException e) {   
  62.                                         _log.warn("error while trying to encode ["+parsedValue+"]", e);   
  63.                                     }   
  64.                                 }   
  65.                             }   
  66.                             return parsedValue;   
  67.                         }   
  68.             });   
  69.         } else {   
  70.             return param;   
  71.         }   
  72.     }   
  73.   
  74.     /**  
  75.      * Executes the result given a final location (jsp page, action, etc) and the action invocation  
  76.      * (the state in which the action was executed). Subclasses must implement this class to handle  
  77.      * custom logic for result handling.  
  78.      *  
  79.      * @param finalLocation the location (jsp page, action, etc) to go to.  
  80.      * @param invocation    the execution state of the action.  
  81.      * @throws Exception if an error occurs while executing the result.  
  82.      */  
  83.     protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;   
  84. }  
public abstract class StrutsResultSupport implements Result, StrutsStatics {

    private static final Log _log = LogFactory.getLog(StrutsResultSupport.class);

    /** The default parameter */
    public static final String DEFAULT_PARAM = "location";

    private boolean parse;
    private boolean encode;
    private String location;
    private String lastFinalLocation;

    public StrutsResultSupport() {
        this(null, true, false);
    }

    public StrutsResultSupport(String location) {
        this(location, true, false);
    }

    public StrutsResultSupport(String location, boolean parse, boolean encode) {
        this.location = location;
        this.parse = parse;
        this.encode = encode;
    }

    // setter method 省略
 
    /**
     * Implementation of the <tt>execute</tt> method from the <tt>Result</tt> interface. This will call
     * the abstract method {@link #doExecute(String, ActionInvocation)} after optionally evaluating the
     * location as an OGNL evaluation.
     *
     * @param invocation the execution state of the action.
     * @throws Exception if an error occurs while executing the result.
     */
    public void execute(ActionInvocation invocation) throws Exception {
        lastFinalLocation = conditionalParse(location, invocation);
        doExecute(lastFinalLocation, invocation);
    }

    /**
     * Parses the parameter for OGNL expressions against the valuestack
     *
     * @param param The parameter value
     * @param invocation The action invocation instance
     * @return The resulting string
     */
    protected String conditionalParse(String param, ActionInvocation invocation) {
        if (parse && param != null && invocation != null) {
            return TextParseUtil.translateVariables(param, invocation.getStack(),
                    new TextParseUtil.ParsedValueEvaluator() {
                        public Object evaluate(Object parsedValue) {
                            if (encode) {
                                if (parsedValue != null) {
                                    try {
                                        // use UTF-8 as this is the recommended encoding by W3C to
                                        // avoid incompatibilities.
                                        return URLEncoder.encode(parsedValue.toString(), "UTF-8");
                                    }
                                    catch(UnsupportedEncodingException e) {
                                        _log.warn("error while trying to encode ["+parsedValue+"]", e);
                                    }
                                }
                            }
                            return parsedValue;
                        }
            });
        } else {
            return param;
        }
    }

    /**
     * Executes the result given a final location (jsp page, action, etc) and the action invocation
     * (the state in which the action was executed). Subclasses must implement this class to handle
     * custom logic for result handling.
     *
     * @param finalLocation the location (jsp page, action, etc) to go to.
     * @param invocation    the execution state of the action.
     * @throws Exception if an error occurs while executing the result.
     */
    protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;
}



很显然,这个默认实现是为那些类似JSP,Freemarker或者Redirect这样的页面跳转的Result而准备的一个基类,它规定了Result将要跳转到的具体页面的位置、是否需要解析参数,等等。

如果我们试图编写自定义的Result,我们可以实现Result接口,并在struts.xml中进行声明:

Java代码 复制代码
  1. public class CustomerResult implements Result {   
  2.   
  3.     public void execute(ActionInvocation invocation) throws Exception {   
  4.     // write your code here   
  5. }   
  6. }  
public class CustomerResult implements Result {

    public void execute(ActionInvocation invocation) throws Exception {
    // write your code here
}
}


Xml代码 复制代码
  1. <result-type name="customerResult" class="com.javaeye.struts2.CustomerResult"/>  
<result-type name="customerResult" class="com.javaeye.struts2.CustomerResult"/>



常用的Result

接下来,大致介绍一下Struts2内部已经实现的Result,并看看他们是如何工作的。

dispatcher

Xml代码 复制代码
  1. <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>  
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>



dispatcher主要用于返回JSP,HTML等以页面为基础View视图,这个也是Struts2默认的Result类型。

在使用dispatcher时,唯一需要指定的,是JSP或者HTML页面的位置,这个位置将被用于定位返回的页面:

Xml代码 复制代码
  1. <result name="success">/index.jsp</result>  
<result name="success">/index.jsp</result>



而Struts2本身也没有对dispatcher做出什么特殊的处理,只是简单的使用Servlet API进行forward。

freemarker / velocity

Xml代码 复制代码
  1. <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>  
  2. <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>  
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>



随着模板技术的越来越流行,使用Freemarker或者Velocity模板进行View层展示的开发者越来越多。Struts2同样为模板作为Result做出了支持。由于模板的显示需要模板(Template)与数据(Model)的紧密配合,所以在Struts2中,这两个Result的主要工作是为模板准备数据。

以Freemarker为例,我们来看看它是如何为模板准备数据的:

Java代码 复制代码
  1. public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException {   
  2.     this.location = location;   
  3.     this.invocation = invocation;   
  4.     this.configuration = getConfiguration();   
  5.     this.wrapper = getObjectWrapper();   
  6.   
  7.     // 获取模板的位置   
  8.     if (!location.startsWith("/")) {   
  9.         ActionContext ctx = invocation.getInvocationContext();   
  10.         HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);   
  11.         String base = ResourceUtil.getResourceBase(req);   
  12.         location = base + "/" + location;   
  13.     }   
  14.   
  15.     // 得到模板   
  16.     Template template = configuration.getTemplate(location, deduceLocale());   
  17.     // 为模板准备数据   
  18.     TemplateModel model = createModel();   
  19.   
  20.     // 根据模板和数据进行输出   
  21.     // Give subclasses a chance to hook into preprocessing   
  22.     if (preTemplateProcess(template, model)) {   
  23.         try {   
  24.             // Process the template   
  25.             template.process(model, getWriter());   
  26.         } finally {   
  27.             // Give subclasses a chance to hook into postprocessing   
  28.             postTemplateProcess(template, model);   
  29.         }   
  30.     }   
  31. }  
public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException {
    this.location = location;
    this.invocation = invocation;
    this.configuration = getConfiguration();
    this.wrapper = getObjectWrapper();

    // 获取模板的位置
    if (!location.startsWith("/")) {
        ActionContext ctx = invocation.getInvocationContext();
        HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
        String base = ResourceUtil.getResourceBase(req);
        location = base + "/" + location;
    }

    // 得到模板
    Template template = configuration.getTemplate(location, deduceLocale());
    // 为模板准备数据
    TemplateModel model = createModel();

    // 根据模板和数据进行输出
    // Give subclasses a chance to hook into preprocessing
    if (preTemplateProcess(template, model)) {
        try {
            // Process the template
            template.process(model, getWriter());
        } finally {
            // Give subclasses a chance to hook into postprocessing
            postTemplateProcess(template, model);
        }
    }
}



从源码中,我们可以看到,createModel()方法真正为模板准备需要显示的数据。而之前,我们已经看到过这个方法的源码,这个方法所准备的数据不仅包含ValueStack中的数据,还包含了被封装过的HttpServletRequest,HttpSession等对象的数据。从而使得模板能够以它特定的语法输出这些数据。

Velocity的Result也是类似,有兴趣的读者可以顺着思路继续深究源码。

redirect

Xml代码 复制代码
  1. <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>  
  2. <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>  
  3. <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>  
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>



如果你在Action执行完毕后,希望执行另一个Action,有2种方式可供选择。一种是forward,另外一种是redirect。有关forward和redirect的区别,这里我就不再展开,这应该属于Java程序员的基本知识。在Struts2中,分别对应这两种方式的Result,就是chain和redirect。

先来谈谈redirect,既然是重定向,那么源地址与目标地址之间是2个不同的HttpServletRequest。所以目标地址将无法通过ValueStack等Struts2的特性来获取源Action中的数据。如果你需要对目标地址传递参数,那么需要在目标地址url或者配置文件中指出:

Xml代码 复制代码
  1. <!--   
  2.    The redirect-action url generated will be :   
  3.    /genReport/generateReport.jsp?reportType=pie&width=100&height=100  
  4.    -->  
  5.    <action name="gatherReportInfo" class="...">  
  6.       <result name="showReportResult" type="redirect">  
  7.          <param name="location">generateReport.jsp</param>  
  8.          <param name="namespace">/genReport</param>  
  9.          <param name="reportType">pie</param>  
  10.          <param name="width">${width}</param>  
  11.          <param name="height">${height}</param>  
  12.       </result>  
  13.    </action>  
<!--
   The redirect-action url generated will be :
   /genReport/generateReport.jsp?reportType=pie&width=100&height=100
   -->
   <action name="gatherReportInfo" class="...">
      <result name="showReportResult" type="redirect">
         <param name="location">generateReport.jsp</param>
         <param name="namespace">/genReport</param>
         <param name="reportType">pie</param>
         <param name="width">${width}</param>
         <param name="height">${height}</param>
      </result>
   </action>



同时,Redirect的Result支持在配置文件中,读取并解析源Action中ValueStack的值,并成为参数传递到Redirect的地址中。上面给出的例子中,width和height就是ValueStack中的值。

chain

Xml代码 复制代码
  1. <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>  
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>



再来谈谈chain,之前提到,chain其实只是在一个action执行完毕之后,forward到另外一个action,所以他们之间是共享HttpServletRequest的。在使用chain作为Result时,往往会配合使用ChainingInterceptor。有关ChainingInterceptor,Struts2的Reference说明了其作用:

Struts2 Reference 写道
If you need to copy the properties from your previous Actions in the chain to the current action, you should apply the ChainingInterceptor. The Interceptor will copy the original parameters from the request, and the ValueStack is passed in to the target Action. The source Action is remembered by the ValueStack, allowing the target Action to access the properties of the preceding Action(s) using the ValueStack, and also makes these properties available to the final result of the chain, such as the JSP or Velocity page.



也就是说,ChainingInterceptor的作用是在Action直接传递数据。事实上,源Action中ValueStack的数据会被做一次Copy,这样,2个Action中的数据都在ValueStack中,使得对于前台来说,通过ValueStack来取数据,是透明而共享的。

chain这个Result有一些常用的使用情景,这点在Struts2的Reference中也有说明:

Struts2 Reference 写道

One common use of Action chaining is to provide lookup lists (like for a dropdown list of states). Since these Actions get put on the ValueStack, their properties will be available in the view. This functionality can also be done using the ActionTag to execute an Action from the display page.



比如说,一张页面中,你可能有许多数据要显示,而某些数据的获取方式可能被很多不同的页面共享(典型来说,“推荐文章”这个小栏目的数据获取,可能会被很多页面所共享)。这种情况下,可以把这部分逻辑抽取到一个独立Action中,并使用chain,将这个Action与主Action串联起来。这样,最后到达页面的时候,页面始终可以得到每个Action中的数据。

不过chain这种Result,是在使用时需要慎重考虑的一种Result:

Struts2 Reference 写道
As a rule, Action Chaining is not recommended. First explore other options, such as the Redirect After Post technique.



而Struts2也做出了理由上的说明:

Struts2 Reference 写道
Experience shows that chaining should be used with care. If chaining is overused, an application can turn into "spaghetti code". Actions should be treated as a Transaction Script, rather than as methods in a Business Facade. Be sure to ask yourself why you need to chain from one Action to another. Is a navigational issue, or could the logic in Action2 be pushed back to a support class or business facade so that Action1 can call it too?

Ideally, Action classes should be as short as possible. All the core logic should be pushed back to a support class or a business facade, so that Actions only call methods. Actions are best used as adapters, rather than as a class where coding logic is defined.



从实战上将,使用chain作为Result也的确存在着上面所说的许多问题,我个人也是非常不推崇滥用这种Result。尤其是,对于使用Spring和Hibernate的朋友来说,如果你开启OpenSessionInView模式,那么Hibernate的session是跟随HttpServletRequest的,所以session在整个action链中共享。这会为我们的编程带来极大的麻烦。因为我们知道Hibernate的session会保留一份一级缓存,在action链中,共享一级缓存无疑会为你的调试工作带来很大的不方便。

所以,谨慎使用chain作为你的Result,应该成为一条最佳实践。

stream

Xml代码 复制代码
  1. <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>  
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>



StreamResult等价于在Servlet中直接输出Stream流。这种Result被经常使用于输出图片、文档等二进制流到客户端。通过使用StreamResult,我们只需要在Action中准备好需要输出的InputStream即可。

Xml代码 复制代码
  1. <result name="success" type="stream">  
  2.   <param name="contentType">image/jpeg</param>  
  3.   <param name="inputName">imageStream</param>  
  4.   <param name="contentDisposition">filename="document.pdf"</param>  
  5.   <param name="bufferSize">1024</param>  
  6. </result>  
  7. </o
    分享到:
    评论

相关推荐

    struts2学习资料

    这个“struts2学习资料”压缩包包含了丰富的学习资源,适合初学者系统性地掌握Struts2的核心概念和技术。以下是一些关键知识点的详细说明: 1. **MVC模式**:Struts2是基于Model-View-Controller(MVC)设计模式的...

    跟我一起学struts2之一:环境搭建篇

    本篇文章将带你逐步学习如何搭建Struts2的基础开发环境,让你顺利入门。 首先,我们需要了解Struts2的核心组件和工作原理。Struts2框架主要由以下部分组成: 1. **FilterDispatcher**:这是Struts2的前端控制器,...

    (源码)基于 Koa2 的 FEBLOG API.zip

    # 基于 Koa2 的 FEBLOG API ## 项目简介 FEBLOG API 是一个基于 Node.js 和 Koa2 框架的 RESTful API 服务器,支持多种关系型数据库(如 PostgreSQL、MySQL、MariaDB、SQLite、MSSQL),并使用 Sequelize 作为 ORM。项目支持跨域请求、JSON 数据传输、JWT 身份认证等功能,适用于构建前后端分离的应用。 ## 主要特性和功能 跨域支持通过配置支持跨域请求。 数据传输格式支持 applicationxwwwformurlencoded、multipartformdata、applicationjson 格式的 POST、PUT、DELETE 请求。 JWT 身份认证通过 JWT 实现用户身份认证。 数据库支持使用 Sequelize ORM 支持 PostgreSQL、MySQL、MariaDB、SQLite、MSSQL 等多种数据库。

    存储器实验资料.zip

    存储器实验资料.zip

    (源码)基于 Python 的知乎热榜爬虫及数据处理项目.zip

    # 基于 Python 的知乎热榜爬虫及数据处理项目 ## 项目简介 本项目基于 Python 编程语言,旨在实现知乎热榜的定时跟踪以及相关数据的存储与查询操作。通过爬虫技术获取知乎热榜问题的详细信息,将数据存入数据库,同时提供一系列 SQL 查询示例帮助用户熟悉 SQL 基本语法,还包含使用 Selenium 实现 GPA 计算器的功能。 ## 项目的主要特性和功能 1. 知乎热榜爬虫定期爬取知乎热榜,获取问题摘要、描述、热度、访问人数、回答数量等基本信息,并将数据存入数据库。 2. 可定制爬虫逻辑用户可以选择删除已有代码从零开始编写,也可以完成代码填空实现相应功能。 3. GPA 计算器使用 Selenium 模拟点击登录 WebVPN,登录 info 并访问成绩单页面,查询成绩并计算每学期的绩点。 4. SQL 练习提供一系列基于 MySQL 数据库的 SQL 查询练习,帮助用户熟悉基本的 SQL 语法,如添加新列、数据填充、关键词查询等。

    (源码)基于C语言的学生信息管理系统.zip

    # 基于C语言的学生信息管理系统 ## 项目简介 这是一个基于文本界面的学生信息管理系统,旨在通过简单的文本输入实现学生信息的添加、查找、修改和删除操作。系统采用链表数据结构存储学生信息,并支持文件读写功能以持久化存储数据。 ## 项目的主要特性和功能 ### 主要特性 1. 文本界面操作用户通过控制台输入指令完成操作。 2. 链表数据结构使用链表存储学生信息,方便信息的添加和删除。 3. 文件操作支持将学生信息数据保存到文件,以及从文件中读取数据。 ### 功能详解 登录验证用户需输入正确的学号和密码才能进入系统。 主界面展示显示系统主菜单,包括学生信息查找、删除、添加、修改和录入等功能。 学生信息查找根据学号查找学生信息。 学生信息删除根据学号删除学生信息。 学生信息添加可以添加新的学生信息到系统中。 学生信息修改可以修改已存在的学生信息。 学生信息录入展示所有存储的学生信息。 辅助功能

    (源码)基于VS Code的px到rpx转换工具.zip

    # 基于VS Code的px到rpx转换工具 ## 项目简介 本项目是一款VS Code插件,旨在将前端代码里的单位px转换为rpx。当设计师在设计稿中使用px单位时,开发者能够借助该工具快速把代码中的px转换为小程序适用的rpx单位。它借助语法分析技术实现精准转换,避免误改其他属性里的px。 ## 项目的主要特性和功能 1. 自动转换功能能通过简单命令自动识别并转换style标签内所有声明中的px为rpx。 2. 精准转换利用语法分析,仅对真正的单位值进行转换,防止错误修改其他内容中的px字符。 3. 部分转换支持可选择部分样式代码进行转换,操作灵活便捷。 ## 安装使用步骤 假设用户已下载本项目源码文件且安装了VS Code环境。 1. 安装插件打开VS Code,进入侧边栏的扩展视图,搜索并安装“px2rpx”插件。 2. 重启VS Code安装完成后重启VS Code使插件生效。

    test文件资包 传递使用

    test文件资包。传递使用

    LCCC2701 智能车灯控制系统设计 20250329

    主控:AT89C52 显示:LCD1602 光照检测:光敏电阻 距离检测:超声波测距 远光灯 近光灯 按键(设置阈值) 1、使用光敏电阻实时检测环境光线强度,设置阈值判断是否开启远光灯; 2、利用超声波传感器测量迎面车辆距离,设置安全距离阈值,自动切换到近光灯; 3、加入延时功能(例如在检测到迎面车辆后等待3秒再切换灯光),以减少频繁切换,提升平滑性。 4、所选传感器模块、执行器模块、电源与接口电路等模块的型号需要是便宜的。

    ESP32之阿里云OTA固件升级(源码)

    esp-idf-v5.3.2

    信息安全领域实战项目【信息安全领域】实战项目汇总:涵盖网络渗透测试、Web应用安全加固、企业安全策略制定等内容多个信息安全领域的实战

    内容概要:本文介绍了多个信息安全领域的实战项目,涵盖网络渗透测试、Web应用安全加固、企业安全策略制定与实施、恶意软件分析、数据泄露应急响应、物联网设备安全检测、区块链安全审计和云安全防护。每个项目都详细描述了其目标和具体实施步骤,包括信息收集、漏洞扫描、利用和修复、安全配置、风险评估、制度建设、培训教育、样本获取与分析、事件响应、遏制措施、调查取证、数据恢复、安全检测、架构分析、智能合约审计、共识机制审查、云环境评估、访问管理、网络安全防护等方面。 适合人群:信息安全从业者、IT管理人员、安全顾问、系统管理员、开发人员以及对信息安全感兴趣的人员。 使用场景及目标:①为信息安全从业人员提供实际操作指导,帮助其掌握不同场景下的安全防护技能;②为企业提供全面的信息安全保障方案,确保其信息系统和数据的安全性;③为开发人员提供安全编码和系统设计的最佳实践指南,提高应用程序的安全性;④为安全研究人员提供深入分析恶意软件和区块链系统的工具和方法。 阅读建议:读者可以根据自身需求选择感兴趣的部分进行深入学习,建议结合实际案例进行实践操作,同时关注最新的安全技术和法规要求,以确保所学知识与时俱进并能应用于实际工作中。

    (源码)基于C语言和STM32F0系列微控制器的宏键盘系统.zip

    # 基于C语言和STM32F0系列微控制器的宏键盘系统 ## 项目简介 本项目是基于C语言和STM32F0系列微控制器开发的宏键盘系统。该系统可让用户自定义宏按键,实现快速输入或自动化任务,涵盖硬件的GPIO输入输出控制、USB通信以及中断处理等功能。 ## 项目的主要特性和功能 宏定义用户能通过定义keymappings.h文件中的宏按键,自定义按键行为。 USB通信利用STM32F0系列微控制器的USB库,支持HID类通信。 GPIO控制实现对键盘按键读取和发送操作的控制。 中断处理可处理按键状态变化、USB通信等外部中断请求。 电源管理对微控制器的睡眠、停止和待机等电源模式进行管理。 ## 安装使用步骤 ### 硬件准备 确保STM32F0系列微控制器(如STM32F042K6)的GPIO引脚、USB接口等硬件连接正确。 保证所有必要外设(如LED、按键)正确连接且可用。 ### 软件准备 下载并解压项目源代码。

    COMSOL多物理场仿真中熔池枝晶模型的构建与应用

    内容概要:本文详细介绍了如何利用COMSOL Multiphysics软件构建熔池枝晶模型,用于模拟金属在凝固过程中枝晶的生长行为。主要内容涵盖三个关键模块:传热、流体流动和相场。通过定义相应的偏微分方程(如传热方程、Navier-Stokes方程和相场方程),设置适当的边界条件和初始条件,并进行多物理场耦合求解,最终实现了对熔池温度分布、速度场及枝晶生长过程的精确模拟。此外,还探讨了如何优化求解器配置、处理移动边界条件、引入各向异性效应以及提高计算效率的方法。 适合人群:从事材料科学、冶金工程、增材制造等领域研究的专业人士和技术人员。 使用场景及目标:适用于需要深入了解金属凝固过程中微观结构演变机制的研究项目,特别是在激光熔覆、焊接等工艺中,帮助研究人员预测和优化材料性能。 其他说明:文中不仅提供了详细的建模步骤指导,还包括一些实用技巧,如参数选择、网格划分策略、热源耦合方式等,有助于解决实际建模过程中可能遇到的问题。

    基于COMSOL的地下二氧化碳封存多物理场耦合仿真研究

    内容概要:本文详细介绍了利用COMSOL Multiphysics进行地下二氧化碳封存仿真的方法和技术要点。主要内容涵盖两相流模块设置、温度场耦合、地层分层建模以及力学模块处理等方面。文中不仅提供了具体的数学模型和代码片段,如相对渗透率函数、热膨胀系数函数等,还分享了许多实际操作中的经验和教训,强调了不同物理场之间的相互作用及其对模拟结果的影响。 适合人群:从事地质工程、环境科学、石油工程等领域研究的专业人士,尤其是那些需要进行地下流体运移和储层特性研究的科研工作者。 使用场景及目标:适用于希望深入了解地下二氧化碳封存机制的研究人员,帮助他们掌握如何使用COMSOL软件构建复杂的多物理场耦合模型,从而更好地预测和评估二氧化碳封存的安全性和有效性。 其他说明:文章中提到的技术细节对于确保模拟精度至关重要,例如正确处理单位转换、选择合适的渗透率模型、考虑温度变化对岩石性质的影响等。此外,作者还提醒读者应注意避免一些常见的错误配置,以免导致不可靠的结果。

    ENCAP 2023打分表

    ENCAP 2023打分表

    中国上市公司协会:2022年中国上市公司董事会秘书履职报告.pdf

    中国上市公司协会:2022年中国上市公司董事会秘书履职报告

    MATLAB遗传算法解决电动车带时间窗路径规划与充电优化问题

    内容概要:本文详细介绍了利用MATLAB遗传算法解决带有时间窗约束的电动车路径规划和充电优化问题。首先,构建了客户点、充电站以及电动车的基本参数模型,然后设计了一套完整的遗传算法框架,包括染色体编码、适应度函数、交叉变异操作等。适应度函数综合考虑了总行驶距离、时间窗违约、电量透支等多个因素。通过多次迭代优化,最终得到了较优的路径规划方案,并展示了实验结果的可视化图形。此外,文中还讨论了一些调参技巧和实际应用中的注意事项。 适合人群:具有一定编程基础和技术背景的研究人员、工程师,特别是从事智能交通系统、物流配送优化领域的专业人士。 使用场景及目标:适用于需要进行电动车路径规划和充电管理的实际应用场景,如城市物流配送公司。主要目标是在满足客户需求和服务质量的前提下,最小化运营成本,提高车辆利用率。 其他说明:文中提供了详细的代码实现步骤和部分实验数据,有助于读者理解和复现研究结果。同时提到了一些实用的小技巧,如适当放宽时间窗惩罚系数可以降低总成本等。

    (源码)基于Arduino的超声波距离测量系统.zip

    # 基于Arduino的超声波距离测量系统 ## 项目简介 本项目是一个基于Arduino平台的超声波距离测量系统。系统包含四个超声波传感器(SPS)模块,用于测量与前方不同方向物体的距离,并通过蜂鸣器(Buzz)模块根据距离范围给出不同的反应。 ## 项目的主要特性和功能 1. 超声波传感器(SPS)模块每个模块包括一个超声波传感器和一个蜂鸣器。传感器用于发送超声波并接收回波,通过计算超声波旅行时间来确定与物体的距离。 2. 蜂鸣器(Buzz)模块根据超声波传感器测量的距离,蜂鸣器会给出不同的反应,如延时发声。 3. 主控制器(Arduino)负责控制和管理所有传感器和蜂鸣器模块,通过串行通信接收和发送数据。 4. 任务管理通过主控制器(Arduino)的 loop() 函数持续执行传感器任务(Task),包括测距、数据处理和蜂鸣器反应。 ## 安装使用步骤 1. 硬件连接

    COMSOL仿真中偶极光源的建模与优化方法详解

    内容概要:本文详细介绍了如何使用COMSOL进行偶极光源的建模与仿真。首先解释了偶极子光源的物理本质及其重要性,然后逐步指导读者完成从创建新模型、设置电流源、配置边界条件到最终结果分析的全过程。文中强调了关键步骤如正确设置电流分量、选择合适的边界条件(如PML)、合理划分网格以及如何解读远场辐射图等。此外,还提供了多个实用技巧和常见错误规避方法,帮助用户提高仿真的准确性和效率。 适合人群:从事光学仿真、电磁场研究的专业人士和技术爱好者。 使用场景及目标:适用于需要精确模拟微纳尺度下电磁波行为的研究项目,特别是涉及偶极子光源的应用场合。通过掌握这些技能,可以更好地理解和预测实际物理现象,从而为相关领域的科研工作提供有力支持。 其他说明:文章不仅涵盖了基本的操作流程,还包括了许多作者亲身经历的经验分享,使读者能够避开一些常见的陷阱并获得更好的仿真效果。同时,文中提供的代码片段可以帮助用户快速上手,将理论知识转化为具体实践。

    COMSOL在多物理场扩散模型中的应用及优化:从电化学到地质工程

    内容概要:本文详细介绍了COMSOL Multiphysics在多种扩散模型中的应用,涵盖电化学、多孔介质中的流体运移、岩石裂隙中的浆液扩散等领域。通过具体的代码片段展示了如何模拟电双层纳米电极中的扩散、二氧化碳混相驱替、岩石裂隙中的浆液扩散以及三层顶板随机裂隙浆液扩散等过程。文中强调了COMSOL的强大多物理场耦合能力和灵活性,特别是在处理复杂系统如煤颗粒的吸附/解吸行为时的优势。此外,还讨论了模型参数调整、边界条件设置、数值稳定性等问题,并分享了一些实践经验和技术细节。 适合人群:从事电化学、地质工程、油气田开发等相关领域的科研人员和工程师。 使用场景及目标:①研究电化学过程中离子迁移和电荷分布的变化;②模拟二氧化碳在多孔介质中的运移规律,优化油气采收率;③分析浆液在岩石裂隙中的扩散规律,指导注浆工程的设计;④探讨煤颗粒的吸附/解吸行为,提升煤层气开采和CO2封存的效果。 其他说明:文中提供了大量具体的技术细节和代码片段,有助于读者理解和复现相关模型。同时,作者分享了许多实际操作中的经验和技巧,对于初学者和有一定经验的研究人员都有很高的参考价值。

Global site tag (gtag.js) - Google Analytics