使用 Spring 更好地处理 Struts 动作

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

使用 Spring 更好地处理 Struts 动作

三种整合 Struts 应用程序与 Spring 的方式

developerWorks
文档选项
将此页作为电子邮件发送

将此页作为电子邮件发送


拓展 Tomcat 应用

下载 IBM 开源 J2EE 应用服务器 WAS CE 新版本 V1.1


级别: 中级

George Franciscus (george.franciscus@nexcel.ca), 负责人, Nexcel

2005 年 10 月 11 日

Struts Recipes 的合著者 George Franciscus 将介绍另一个重大的 Struts 整合窍门 —— 这次是将 Struts 应用程序导入 Spring 框架。请跟随 George,他将向您展示如何改变 Struts 动作,使得管理 Struts 动作就像管理 Spring beans 那样。结果是一个增强的 web 框架,这个框架可以方便地利用 Spring AOP 的优势。

您肯定已经听说过控制反转 (IOC) 设计模式,因为很长一段时间以来一直在流传关于它的信息。如果您在任何功能中使用过 Spring 框架,那么您就知道其原理的作用。在本文中,我利用这一原理把一个 Struts 应用程序注入 Spring 框架,您将亲身体会到 IOC 模式的强大。

将一个 Struts 应用程序整合进 Spring 框架具有多方面的优点。首先,Spring 是为解决一些关于 JEE 的真实世界问题而设计的,比如复杂性、低性能和可测试性,等等。第二,Spring 框架包含一个 AOP 实现,允许您将面向方面技术应用于面向对象的代码。第三,一些人可能会说 Spring 框架只有处理 Struts 比 Struts 处理自己好。但是这是观点问题,我演示三种将 Struts 应用程序整合到 Spring 框架的方法后,具体由您自己决定使用哪一种。

我所演示的方法都是执行起来相对简单的,但是它们却具有明显不同的优点。我为每一种方法创建了一个独立而可用的例子,这样您就可以完全理解每种方法。请参阅 下载 部分获得完整例子源代码。请参阅 参考资料,下载 Struts MVC 和 Spring 框架。

为什么 Spring 这么了不起?

Spring 的创立者 Rod Johnson 以一种批判的眼光看待 Java™ 企业软件开发,并且提议很多企业难题都能够通过战略地使用 IOC 模式(也称作依赖注入)来解决。当 Rod 和一个具有奉献精神的开放源码开发者团队将这个理论应用于实践时,结果就产生了 Spring 框架。简言之,Spring 是一个轻型的容器,利用它可以使用一个外部 XML 配置文件方便地将对象连接在一起。每个对象都可以通过显示一个 JavaBean 属性收到一个到依赖对象的引用,留给您的简单任务就只是在一个 XML 配置文件中把它们连接好。

IOC 和 Spring

IOC 是一种使应用程序逻辑外在化的设计模式,所以它是被注入而不是被写入客户机代码中。将 IOC 与接口编程应用结合,就像 Spring 框架那样,产生了一种架构,这种架构能够减少客户机对特定实现逻辑的依赖。请参阅 参考资料 了解更多关于 IOC 和 Spring 的信息。

依赖注入是一个强大的特性,但是 Spring 框架能够提供更多特性。Spring 支持可插拔的事务管理器,可以给您的事务处理提供更广泛的选择范围。它集成了领先的持久性框架,并且提供一个一致的异常层次结构。Spring 还提供了一种使用面向方面代码代替正常的面向对象代码的简单机制。

Spring AOP 允许您使用拦截器 在一个或多个执行点上拦截应用程序逻辑。加强应用程序在拦截器中的日志记录逻辑会产生一个更可读的、实用的代码基础,所以拦截器广泛用于日志记录。您很快就会看到,为了处理横切关注点,Spring AOP 发布了它自己的拦截器,您也可以编写您自己的拦截器。





回页首


整合 Struts 和 Spring

与 Struts 相似,Spring 可以作为一个 MVC 实现。这两种框架都具有自己的优点和缺点,尽管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多开发团队已经学会在时间紧迫的时候利用 Struts 作为构造高品质软件的基础。Struts 具有如此大的推动力,以至于开发团队宁愿整合 Spring 框架的特性,而不愿意转换成 Spring MVC。没必要进行转换对您来说是一个好消息。Spring 架构允许您将 Struts 作为 Web 框架连接到基于 Spring 的业务和持久层。最后的结果就是现在一切条件都具备了。

在接下来的小窍门中,您将会了解到三种将 Struts MVC 整合到 Spring 框架的方法。我将揭示每种方法的缺陷并且对比它们的优点。 一旦您了解到所有三种方法的作用,我将会向您展示一个令人兴奋的应用程序,这个程序使用的是这三种方法中我最喜欢的一种。





回页首


三个小窍门

接下来的每种整合技术(或者窍门)都有自己的优点和特点。我偏爱其中的一种,但是我知道这三种都能够加深您对 Struts 和 Spring 的理解。在处理各种不同情况的时候,这将给您提供一个广阔的选择范围。方法如下:

  • 使用 Spring 的 ActionSupport 类整合 Structs
  • 使用 Spring 的 DelegatingRequestProcessor 覆盖 Struts 的 RequestProcessor
  • 将 Struts Action 管理委托给 Spring 框架

装载应用程序环境

无论您使用哪种技术,都需要使用 Spring 的 ContextLoaderPlugin 为 Struts 的 ActionServlet 装载 Spring 应用程序环境。就像添加任何其他插件一样,简单地向您的 struts-config.xml 文件添加该插件,如下所示:

<plug-in className=
  "org.springframework.web.struts.ContextLoaderPlugIn">
    <set-property property=
      "contextConfigLocation" value="/WEB-INF/beans.xml"/>
 </plug-in>

前面已经提到过,在 下载 部分,您能够找到这三个完全可使用的例子的完整源代码。每个例子都为一个书籍搜索应用程序提供一种不同的 Struts 和 Spring 的整合方法。您可以在这里看到例子的要点,但是您也可以下载应用程序以查看所有的细节。





回页首


窍门 1. 使用 Spring 的 ActionSupport

手动创建一个 Spring 环境是一种整合 Struts 和 Spring 的最直观的方式。为了使它变得更简单,Spring 提供了一些帮助。为了方便地获得 Spring 环境,org.springframework.web.struts.ActionSupport 类提供了一个 getWebApplicationContext() 方法。您所做的只是从 Spring 的 ActionSupport 而不是 Struts Action 类扩展您的动作,如清单 1 所示:



清单 1. 使用 ActionSupport 整合 Struts
package ca.nexcel.books.actions;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import org.springframework.context.ApplicationContext;
import org.springframework.web.struts.ActionSupport;
import ca.nexcel.books.beans.Book;
import ca.nexcel.books.business.BookService;
public class SearchSubmit extends ActionSupport {   |(1)
  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {
    DynaActionForm searchForm = (DynaActionForm) form;
    String isbn = (String) searchForm.get("isbn");
		
    //the old fashion way
    //BookService bookService = new BookServiceImpl();
		
    ApplicationContext ctx = 
      getWebApplicationContext();    |(2)
    BookService bookService = 
      (BookService) ctx.getBean("bookService");   |(3)
        
  Book book = bookService.read(isbn.trim());
    if (null == book) {
      ActionErrors errors = new ActionErrors();
      errors.add(ActionErrors.GLOBAL_ERROR,new ActionError
        ("message.notfound"));
      saveErrors(request, errors);
      return mapping.findForward("failure") ;
  }
    request.setAttribute("book", book);
    return mapping.findForward("success");
  }
}

让我们快速思考一下这里到底发生了什么。在 (1) 处,我通过从 Spring 的 ActionSupport 类而不是 Struts 的 Action 类进行扩展,创建了一个新的 Action。在 (2) 处,我使用 getWebApplicationContext() 方法获得一个 ApplicationContext。为了获得业务服务,我使用在 (2) 处获得的环境在 (3) 处查找一个 Spring bean。

这种技术很简单并且易于理解。不幸的是,它将 Struts 动作与 Spring 框架耦合在一起。如果您想替换掉 Spring,那么您必须重写代码。并且,由于 Struts 动作不在 Spring 的控制之下,所以它不能获得 Spring AOP 的优势。当使用多重独立的 Spring 环境时,这种技术可能有用,但是在大多数情况下,这种方法不如另外两种方法合适。





回页首


窍门 2. 覆盖 RequestProcessor

将 Spring 从 Struts 动作中分离是一个更巧妙的设计选择。分离的一种方法是使用 org.springframework.web.struts.DelegatingRequestProcessor 类来覆盖 Struts 的 RequestProcessor 处理程序,如清单 2 所示:



清单 2. 通过 Spring 的 DelegatingRequestProcessor 进行整合
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
 <form-beans>
    <form-bean name="searchForm" 
      type="org.apache.struts.validator.DynaValidatorForm">
               <form-property name="isbn"    type="java.lang.String"/>
    </form-bean>
  
  </form-beans>
 <global-forwards type="org.apache.struts.action.ActionForward">
     <forward   name="welcome"                path="/welcome.do"/>
     <forward   name="searchEntry"            path="/searchEntry.do"/>
     <forward   name="searchSubmit"           path="/searchSubmit.do"/>
 </global-forwards>
 <action-mappings>
    <action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
    <action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
    <action    path="/searchSubmit" 
               type="ca.nexcel.books.actions.SearchSubmit"
               input="/searchEntry.do"
               validate="true"
               name="searchForm">
              <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
              <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
    </action>  
 </action-mappings>
 <message-resources parameter="ApplicationResources"/>
 <controller processorClass="org.springframework.web.struts.
   DelegatingRequestProcessor"/> |(1)
 <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property property="pathnames" 
      value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
 </plug-in>
 <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
    <set-property property="csntextConfigLocation" value="/WEB-INF/beans.xml"/>
 </plug-in>
 
</struts-config>

我利用了 <controller> 标记来用 DelegatingRequestProcessor 覆盖默认的 Struts RequestProcessor。下一步是在我的 Spring 配置文件中注册该动作,如清单 3 所示:



清单 3. 在 Spring 配置文件中注册一个动作
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
  "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
  <bean name="/searchSubmit" 
    class="ca.nexcel.books.actions.SearchSubmit"> |(1)
     <property name="bookService">
        <ref bean="bookService"/>
     </property>
  </bean>
</beans>

注意:在 (1) 处,我使用名称属性注册了一个 bean,以匹配 struts-config 动作映射名称。SearchSubmit 动作揭示了一个 JavaBean 属性,允许 Spring 在运行时填充属性,如清单 4 所示:



清单 4. 具有 JavaBean 属性的 Struts 动作
package ca.nexcel.books.actions;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import ca.nexcel.books.beans.Book;
import ca.nexcel.books.business.BookService;
public class SearchSubmit extends Action {
	
  private BookService bookService;
  public BookService getBookService() {
    return bookService;
  }
  public void setBookService(BookService bookService) { | (1)
    this.bookService = bookService; 
  } 
  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {
    DynaActionForm searchForm = (DynaActionForm) form;
    String isbn = (String) searchForm.get("isbn");
		
  Book book = getBookService().read(isbn.trim());  |(2)
    if (null == book) {
      ActionErrors errors = new ActionErrors();
      errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.notfound"));
      saveErrors(request, errors);
      return mapping.findForward("failure") ;
  }
      request.setAttribute("book", book);
      return mapping.findForward("success");
  }
}

在清单 4 中,您可以了解到如何创建 Struts 动作。在 (1) 处,我创建了一个 JavaBean 属性。DelegatingRequestProcessor自动地配置这种属性。这种设计使 Struts 动作并不知道它正被 Spring 管理,并且使您能够利用 Sping 的动作管理框架的所有优点。由于您的 Struts 动作注意不到 Spring 的存在,所以您不需要重写您的 Struts 代码就可以使用其他控制反转容器来替换掉 Spring。

DelegatingRequestProcessor 方法的确比第一种方法好,但是仍然存在一些问题。如果您使用一个不同的 RequestProcessor,则需要手动整合 Spring 的 DelegatingRequestProcessor。添加的代码会造成维护的麻烦并且将来会降低您的应用程序的灵活性。此外,还有过一些使用一系列命令来代替 Struts RequestProcessor 的传闻。 这种改变将会对这种解决方法的使用寿命造成负面的影响。





回页首


窍门 3. 将动作管理委托给 Spring

一个更好的解决方法是将 Strut 动作管理委托给 Spring。您可以通过在 struts-config 动作映射中注册一个代理来实现。代理负责在 Spring 环境中查找 Struts 动作。由于动作在 Spring 的控制之下,所以它可以填充动作的 JavaBean 属性,并为应用诸如 Spring 的 AOP 拦截器之类的特性带来了可能。

清单 5 中的 Action 类与清单 4 中的相同。但是 struts-config 有一些不同:



清单 5. Spring 整合的委托方法
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
 <form-beans>
    <form-bean name="searchForm" 
      type="org.apache.struts.validator.DynaValidatorForm">
               <form-property name="isbn"    type="java.lang.String"/>
    </form-bean>
  
  </form-beans>
 <global-forwards type="org.apache.struts.action.ActionForward">
     <forward   name="welcome"                path="/welcome.do"/>
     <forward   name="searchEntry"            path="/searchEntry.do"/>
     <forward   name="searchSubmit"           path="/searchSubmit.do"/>
 </global-forwards>
 <action-mappings>
    <action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
    <action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
    <action    path="/searchSubmit" 
             type="org.springframework.web.struts.DelegatingActionProxy" |(1)
             input="/searchEntry.do"
             validate="true"
             name="searchForm">
             <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
             <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
    </action>  
 </action-mappings>
 <message-resources parameter="ApplicationResources"/>
 <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property 
    property="pathnames" 
    value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
 </plug-in>
 <plug-in 
    className="org.springframework.web.struts.ContextLoaderPlugIn">
    <set-property property="contextConfigLocation" value="/WEB-INF/beans.xml"/>
 </plug-in>
 
</struts-config>

清单 5 是一个典型的 struts-config.xml 文件,只有一个小小的差别。它注册 Spring 代理类的名称,而不是声明动作的类名,如(1)处所示。DelegatingActionProxy 类使用动作映射名称查找 Spring 环境中的动作。这就是我们使用 ContextLoaderPlugIn 声明的环境。

将一个 Struts 动作注册为一个 Spring bean 是非常直观的,如清单 6 所示。我利用动作映射使用 <bean> 标记的名称属性(在这个例子中是 "/searchSubmit")简单地创建了一个 bean。这个动作的 JavaBean 属性像任何 Spring bean 一样被填充:



清单 6. 在 Spring 环境中注册一个 Struts 动作
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
 "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
  <bean name="/searchSubmit"   
        class="ca.nexcel.books.actions.SearchSubmit">
     <property name="bookService">
        <ref bean="bookService"/>
     </property>
  </bean>
</beans>





回页首


动作委托的优点

动作委托解决方法是这三种方法中最好的。Struts 动作不了解 Spring,不对代码作任何改变就可用于非 Spring 应用程序中。RequestProcessor 的改变不会影响它,并且它可以利用 Spring AOP 特性的优点。

动作委托的优点不止如此。一旦让 Spring 控制您的 Struts 动作,您就可以使用 Spring 给动作补充更强的活力。例如,没有 Spring 的话,所有的 Struts 动作都必须是线程安全的。如果您设置 <bean> 标记的 singleton 属性为“false”,那么不管用何种方法,您的应用程序都将在每一个请求上有一个新生成的动作对象。您可能不需要这种特性,但是把它放在您的工具箱中也很好。您也可以利用 Spring 的生命周期方法。例如,当实例化 Struts 动作时,<bean> 标记的 init-method 属性被用于运行一个方法。类似地,在从容器中删除 bean 之前,destroy-method 属性执行一个方法。这些方法是管理昂贵对象的好办法,它们以一种与 Servlet 生命周期相同的方式进行管理。





回页首


拦截 Struts

前面提到过,通过将 Struts 动作委托给 Spring 框架而整合 Struts 和 Spring 的一个主要的优点是:您可以将 Spring 的 AOP 拦截器应用于您的 Struts 动作。通过将 Spring 拦截器应用于 Struts 动作,您可以用最小的代价处理横切关注点。

虽然 Spring 提供很多内置拦截器,但是我将向您展示如何创建自己的拦截器并把它应用于一个 Struts 动作。为了使用拦截器,您需要做三件事:

  1. 创建拦截器。
  2. 注册拦截器。
  3. 声明在何处拦截代码。

这看起来非常简单的几句话却非常强大。例如,在清单 7 中,我为 Struts 动作创建了一个日志记录拦截器。 这个拦截器在每个方法调用之前打印一句话:



清单 7. 一个简单的日志记录拦截器
package ca.nexcel.books.interceptors;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LoggingInterceptor implements MethodBeforeAdvice {
   public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("logging before!");
    }
}

这个拦截器非常简单。before() 方法在拦截点中每个方法之前运行。在本例中,它打印出一句话,其实它可以做您想做的任何事。下一步就是在 Spring 配置文件中注册这个拦截器,如清单 8 所示:



清单 8. 在 Spring 配置文件中注册拦截器
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
  "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
  <bean name="/searchSubmit" 
        class="ca.nexcel.books.actions.SearchSubmit">
     <property name="bookService">
        <ref bean="bookService"/>
     </property>
  </bean>
  <!--  Interceptors --> 
  <bean name="logger"    
    class="ca.nexcel.books.interceptors.LoggingInterceptor"/> |(1)
  <!-- AutoProxies -->
  <bean name="loggingAutoProxy" 
        class="org.springframework.aop.framework.autoproxy.
          BeanNameAutoProxyCreator"> |(2)
    <property name="beanNames">
          <value>/searchSubmit</valuesgt; |(3)
    </property>
    <property name="interceptorNames">
        <list>
          <value>logger</value> |(4)
        </list>
    </property>
   </bean>
</beans>

您可能已经注意到了,清单 8 扩展了 清单 6 中所示的应用程序以包含一个拦截器。具体细节如下:

  • 在 (1) 处,我注册了这个拦截器。
  • 在 (2) 处,我创建了一个 bean 名称自动代理,它描述如何应用拦截器。还有其他的方法定义拦截点,但是这种方法常见而简便。
  • 在 (3) 处,我将 Struts 动作注册为将被拦截的 bean。如果您想要拦截其他的 Struts 动作,则只需要在 "beanNames" 下面创建附加的 <value> 标记。
  • 在 (4) 处,当拦截发生时,我执行了在 (1) 处创建的拦截器 bean 的名称。这里列出的所有拦截器都应用于“beanNames”。

就是这样。就像这个例子所展示的,将您的 Struts 动作置于 Spring 框架的控制之下,为处理您的 Struts 应用程序提供了一系列全新的选择。在本例中,使用动作委托可以轻松地利用 Spring 拦截器提高 Struts 应用程序中的日志记录能力。





回页首


结束语

在本文中,您已经学习了将 Struts 动作整合到 Spring 框架中的三种窍门。使用 Spring 的 ActionSupport 来整合 Struts(第一种窍门中就是这样做的)简单而快捷,但是会将 Struts 动作与 Spring 框架耦合在一起。如果您需要将应用程序移植到一个不同的框架,则需要重写代码。第二种解决方法通过委托 RequestProcessor 巧妙地解开代码的耦合,但是它的可扩展性不强,并且当 Struts 的 RequestProcessor 变成一系列命令时,这种方法就持续不了很长时间。第三种方法是这三种方法中最好的:将 Struts 动作委托给 Spring 框架可以使代码解耦,从而使您可以在您的 Struts 应用程序中利用 Spring 的特性(比如日志记录拦截器)。

三种 Struts-Spring 整合窍门中的每一种都被实现成一个完整可用的应用程序。请参阅 下载 部分仔细研究它们。






回页首


下载

描述名字大小下载方法
ActionSupport sample codej-sr2-actionsupport.zip5 MB FTP
RequestProcessor sample codej-sr2-requestprocessor.zip5 MB FTP
Delegate sample codej-sr2-delegate.zip5 MB FTP
关于下载方法的信息Get Adobe® Reader®


参考资料

学习
  • Spring 系列(Naveen Balani,developerWorks,2005 年 6 到 9 月):对 Spring 框架的组件和特性(包括 Spring AOP)的分为四部分的介绍。

  • Best practices in Struts development(Palaniyappan Thiagarajan 和 Pagadala Suresh,developerWorks,2004 年 6 月):概述了 Struts Action 组件。

  • Inversion of Control Containers and the Dependency Injection pattern(Martin Fowler,2004 年 1 月):介绍 IOC 容器。

  • Introduction to the Spring Framework(Rod Johnson,The ServerSide,2005 年 5 月):Spring 的创立者解释了为什么 Spring 框架很重要。

  • Struts Recipes(George Franciscus 和 Danilo Gurovich;Manning,2004 年):关于 Struts 窍门和最佳实践的一个流行的纲要。

  • Struts In Action(Ted Husted、Cedric Dumoulin、George Franciscus、David Winterfeldt;Manning,2002 年):针对专业 Struts 开发人员的综合资源。

  • Spring In Action(Craig Walls 和 Ryan Breidenbach;Manning,2005 年):一本用于开发人员的 Spring 资源书籍。

  • Java 技术专区:找到关于 Java 编程各个方面的文章。


获得产品和技术

讨论


关于作者

 

George Franciscus 是 Java 公司的一名顾问和 Struts 方面的权威。他是 Manning 出版的 Struts RecipesStruts in Action 的合著者。 George 通过 nexcel.ca 提供有关技术和管理方面的咨询服务。

托盘四向车与子母车对比 四向穿梭车可在货架平面内任意方向行驶(前后、左右、换轨),无需额外转弯空间,货架布局更紧凑,存储密度更高。例如,在多层货架系统中,通过提升机配合,四向车可覆盖全仓货位,减少巷道占用面积138。支持多车协同调度,系统冗余性强。此外,子母车换层需依赖提升机,货架设计需预留垂直通道,进一步限制存储密度268。:智能化程度高,减少人工干预,能耗低(电力驱动+能量回收),且通过算法优化可提升设备利用率。在同等仓库面积和建筑高度的条件下,托盘四向穿梭车与托盘子母车在存储托盘数量、作业效率、性价比等方面存在显著差异。 阅读详情

相关推荐

写出一手烂代码的19条准则

要是想写个烂代码,我们只需遵守这十九条准则? 第一条:打字越少越好 如果我们键入的东西越少,那么就有越多的时间去思考代码逻辑等问题。如下所示,「Good」表示遵循该规则的示例,Bad 表示没遵循该规则的示例。 第二条:变量/函数混合命名风格 我们需要混合命名方法与变量,这样才能体现命名的多样性。 第三条:不要写注释 反正代码都看得懂,为什么要写注释?或者说,反正没人看我的代码,为什么要写注释? ...

drddzh的博客 2164

三种整合 Struts 应用程序与 Spring 的方式

1.窍门 1. 使用 SpringActionSupport手动创建一个 Spring 环境是一种整合 StrutsSpring 的最直观的方式。为了使它变得更简单,Spring 提供了一些帮助。为了方便地获得 Spring 环境,org.springframework.web.struts.ActionSupport类提供了一个 getWebApplication

单眼皮的猫 791

整合StrutsSpring

1、使用 SpringActionSupport 类整合 Structs 2、使用 Spring 的 DelegatingRequestProcessor 覆盖 Struts 的 RequestProcessor 3、将 Struts Action 管理委托给 Spring 框架  首先要建立"装载应用程序环境" 无论您使用哪种技术,都需要使用 Spring 的 ContextLoade

Johnson技术天地 1273

spring整合struts2

因为最近工作的需要不得不学习struts2,整理了一下spring整合struts2的过程。在resources目录下创建该文件,有几处地方需要根据自己的项目进行修改,请认真阅读。需要详细了解struts2的朋友可以自行百度,这里只是简单的讲述如何使用。我是用的是idea进行项目构建的,eclipse的话可以自己百度一下。需要自己手动在main目录下创建java,resources目录。在java目录下自行创建一个类,根据自己的的项目进行创建。这里可以自己选择自己的maven版本以及设置等信息。

一般社员的博客 1439

关于spring的DelegatingActionProxy

ad198622的专栏 1472

小烂车

木木原创,请勿转载上大学后家里帮我买了一辆可爱的小50(白色的) 但经过四年的征战加上四年来只洗过它二次 因此大四那年变成很破的小烂车 而且颜色也由原本的白色变成很黄的颜色好了 故事要开始了 由于小烂车到大四已经很破了 因此对待它的原则是『克勤克俭』 能不花钱绝不破费只要能动就绝不维修 因此整台车能坏的大概都已经坏了 能脱落的也大概脱落光了 仪表板从大二就没动过了 加油全凭感觉 根本不知道甚么时候

木木的网络天空 1296

mfc combox 右侧小三角怎么_上海学车娃:科目二考试遇到烂车怎么办?科目二考试技巧介绍...

  近年来,随着考驾照的人数越来越多,因此,车管所的考试车使用的也是非常的频繁,我们在考试时,难免会遇到一些不太好操作的车,那么科目二考试遇到烂车怎么办呢,下面就跟学车娃一起来看看吧。  科目二考试遇到烂车怎么办  考试遇到烂车,一般有的是座位无法调整,倒车镜坏了无法调整,还有的更严重的是,离合器片磨的差不多了,车辆没有多少动力,半坡频繁遭遇熄火,遇到这些烂车,该如何处理?  一、座椅后视镜无法调...

weixin_33057949的博客 155

SUV靠边站,离地间隙最高的几台热销车,跑烂路毫无压力!

离地间隙是大家买车的时候比较少关注的一个参数,很多人都只是凭感觉去目测,虽然说离地间隙在汽车的众多参数之中并没有那么的重要,但是对于经常跑烂路的人来说,还是具有一定的参考意义的,而昨天我们说了几台离地间隙比较低的热销车,今天我们就来说几台离地间隙比较高的热销车。吉利帝豪价格:6.98万到10.08万离地间隙:空载180MM 满载150MM特点:配置丰富,性...

皙姑娘的博客 1162

ant man什么意思_《速度与激情:特别行动》里都有什么车?剧情虽烂,车依然惊喜...

对于90后而言,歌王是周杰伦、动漫之王是火影忍者、而电影之王绝对是速度激情了!从1走到8,《速度与激情》已经火爆了近20年。虽然人物、故事在变,但不变的是电影中的汽车依然壕、依然帅!不过,近期上映了一部番外篇令我有些唏嘘,感觉它的剧情变化糟蹋了速度与激情的灵魂。但是,爱车的小伙伴即使不看剧情,还是能看出很多嗨点的!对很多人来说,只要是《速度与激情》系列,看的就是汽车大戏。所以,今天不是来聊电影的,...

weixin_28728265的博客 693

ant man什么意思_速度与激情: 特别行动里都有什么车? 剧情虽烂,车依然惊喜!...

《速度与激情: 特别行动》是烂片!但电影中的车依然让我高潮!对于90后而言,歌王是周杰伦、动漫之王是火影忍者、而电影之王绝对是速度激情了!从1走到8,《速度与激情》已经火爆了近20年。虽然人物、故事在变,但不变的是电影中的汽车依然壕、依然帅!不过,近期上映了一部番外篇令我有些唏嘘,感觉它的剧情变化糟蹋了速度与激情的灵魂。但是,爱车的小伙伴即使不看剧情,还是能看出很多嗨点的!对很多人来说,...

weixin_42400619的博客 762

骑记——清明假期青龙峡骑行

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33788244的博客 147

java的车不好吗_算是第一个比较复杂的java程序了,大大粗车系统,有没有大神来挑挑毛病...

源自:-算是第一个比较复杂的java程序了,大大粗车系统,有没有大神来挑挑毛病//主类packagepackage1;importjava.util.Scanner;publicclassMain{Scannerin=newScanner(System.in);Ordero;//主菜单publicintmainMenu(){System.out.println("欢迎使用大...

weixin_39754142的博客 105

不纠烂事,不缠烂人

人生要有不较劲的智慧。 曾经看过一个故事:小学徒外出取货,回来的路上被狗咬了。 处理好伤口之后,他找到一根长长的竹竿,准备去打狗。 师傅见状,过来询问:“你的伤口还疼吗?” 小学徒说不疼了。 “既然不疼了,为什么还要去打狗?” “因为它咬了我,我要给它点颜色瞧瞧!” 师傅说:“你知道狮子为什么从来不和狗打架吗?” 小学徒愣住了,呆呆地望着师傅。 师傅笑着说:“因为不是谁都配做狮子的对手,打败一只狗...

齐鸣網的博客 366

前端要给力之:代码可以有多烂?

本文从第2小节,至第6小节,仅供对架构、框架、库等方面有兴趣的同学学习研究,有志于在语言设计、架构抽象等,或基础项目中使用相关技术的,欢迎探讨,切勿滥用于一般应用项目。

aimingoo的专栏 2万+

windows提权—烂土豆(RottenPotato)及Juicy Potato提权

介绍 烂土豆(Rotten Potato) MS16-075 提权是一个本地提权,只针对本地用户,不支持域用户 适用版本:Windows 7、8、10、2008、2012 烂土豆下载地址: https://github.com/foxglovesec/Potato 烂土豆提权原理: 欺骗 “NT AUTHORITY\SYSTEM”账户通过NTLM认证到我们控制的TCP终端。 对这个认证过程使用中间人攻击(NTLM重放),为“NT AUTHORITY\SYSTEM”账户本地协商一个安全令牌。这个过程是通

negnegil的博客 1万+

对不起,因为之前的代码写的烂,所以我也只能继续烂

作者|王晔倞,责编 | 郭芮头图 | CSDN 下载自视觉中国上周四,我团队里的某技术经理在一次代码评审会上跟一位开发同学发生了争吵,而事情的起因是某段看似合理,却存在明显性能问题...

程序人生的博客 1303

这5个让人窒息的烂代码,你看完都忍不了!

摘要:下面就为大家带来个人认为的常见的烂注释风格。 相信作为程序员的大家,只要写代码,就会自己写及看到别人写的代码注释。所以,我们往往会遇到“百花齐放,百家争鸣”般的注释。程序员最讨厌的10件事,0:写注释,1:别人不写注释。 作为一个老IT人,看了那么多年代码,也就看了那么多年注释。可以说,好代码不一定有好注释,但烂代码基本和烂注释共存。下面就为大家带来个人认为的常见的烂注释风格,希望能对大家在日后的工作中,带来一丝丝的帮助。排名不分先后: 1. 注释上带联系方式,TODO事项,问题单需求链接等。这

华为云官方博客 6237

驯服烂代码场景之二:要我改烂代码,但是烂代码烂得让我无法理解,该怎么办?

心法之一:“理解烂代码”与“写新代码”是一样重要的工作,嫌烂代码臭,就不去闻它,闭着眼睛捏着鼻子写新代码,似乎看起来效率高,其实是没头的苍蝇在乱撞,反而浪费时间。 心法之二(当没有时间重写烂代码时):烂代码虽然烂,但毕竟还能work,当进度压力大时,就没有推倒重写的时间。相比之下重构既不会影响烂代码的功能,也会让代码质量向好的方向转变。 手法之一:草稿式重构,即找一个新目录,把烂代码check

独立匠艺程序员。本土匠艺、中西合璧;编程悟道,心无挂碍。 1024
上一篇: DB2索引问题
下一篇: 怎么发布使用了dll的Activex呢?
fan_7
博客等级 码龄26年 4粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值