Hibernate + Spring + struts extension

Time:2021-12-23

Introduction:

I see that in many projects, developers have implemented their own MVC framework, not because they want to do something fundamentally different from struts, but because they don’t realize how to extend struts. Developing your own MVC framework can gain full control, but it also means that a lot of resources are needed to realize it (human and material resources). Sometimes this is impossible under the tight schedule.

Struts is not only a powerful framework, but also extensible. You can extend struts in three ways.

1. Plugin: if you want to do some business logic during application startup or shutdown, create your own plugin class.

2. Requestprocessor: if you want to do some business logic at some time during the processing of requests, create your own requestprocessor class. For example, before each request is executed, you can extend the requestprocessor to check whether the user has logged in and whether he has permission to execute a specific action.

3. Actionservlet: if you want to do some business logic during application startup and shutdown and when the request is processed, you can also expand the actionservlet class. However, you should use actionservlet when neither plugin nor requestprocessor can solve your needs.

In this article, we will use an example of a struts application to demonstrate how to extend struts in these three ways. The code of the sample program can be downloaded from http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip Download. Two successful examples of extending struts are struts’s own validation and tiles framework.

Let’s assume that you are familiar with the struts framework and know how to use it to create a simple application. If you want to know more about struts, please refer to the official home page.

  PlugIn
  
Plugin is an interface. You can create a class that implements the interface and do something when application startup or shutdown.

For example, I created a web application using hibernate as the persistence layer. I want to initialize hibernate when the application starts, so that when my web application receives the first request, hibernate is already configured and available. At the same time, we want to close hibernate when the application is closed. We can use a hibernate plugin to realize this requirement through the following two steps:

  1. Create a class that implements the plugin interface

  public class HibernatePlugIn implements PlugIn{
  private String configFile;
  // This method will be called at application shutdown time
  public void destroy() {
  System.out.println(“Entering HibernatePlugIn.destroy()”);
  //Put hibernate cleanup code here
  System.out.println(“Exiting HibernatePlugIn.destroy()”);
  }
  //This method will be called at application startup time
  public void init(ActionServlet actionServlet, ModuleConfig config)
  throws ServletException {
   System.out.println(“Entering HibernatePlugIn.init()”);
   System.out.println(“value of init parameter ” +
   getConfigFile());
   System.out.println(“Exiting HibernatePlugIn.init()”);
  }
  public String getConfigFile() {
  return name;
  }
  public void setConfigFile(String string) {
  configFile = string;
  }
  }

The class implementing the plugin interface must complete two methods: init() and destroy(). The init () method is called during application startup and the destroy () method is called during shutdown. Struts also allows you to pass initialization parameters to your plugin class. In order to pass parameters, you must create a JavaBean setter method for each parameter in the plugin class. In our hibernateplugin class, I will pass the name of configFile as a parameter instead of hard coding it into the program.

 2. In struts config The following code is added to the XML to notify struts of a new plugin:

  <struts-config>
  …
  <!– Message Resources –>
  <message-resources parameter= “sample1.resources.ApplicationResources”/>

  <!– Declare your plugins –>
  <plug-in className=”com.sample.util.HibernatePlugIn”>
  <set-property property=”configFile” value=”/hibernate.cfg.xml”/>
  </plug-in>
  </struts-config>

The property classname is the fully qualified name of the class that implements the plugin interface. For each initialization parameter, you can use the < set property > element to pass parameters. In our example, I want to pass in the name of the config file, so I use a < set property > with the path of the configuration file.

Both the tiles and validator frameworks of struts use plugin to read the configuration file for initialization. Two other things plugin can help you do are:

· if your application depends on some configuration files, you can check whether they are available in the plugin class. If they are not available, throw a ServletException, which can make the actionservlet unavailable.

· the init () method of plugin interface is the last chance for you to change moduleconfig, which is a set of static configuration information used to describe struts based modules. Struts will release moduleconfig after all plugins are processed.
How is the request processed

Actionservlet is the only servlet in the struts framework, which is responsible for processing all requests. Whenever a request is received, it will first try to find a sub application for the current request. Once a sub application is found, the actionservlet will create a requestprocessor object for that sub application, call the process () method of this object, and pass in HttpServletRequest and httpservletresponse objects.

  RequestProcessor. Process () is where most requests are processed. The process () method is implemented using the template method pattern. There are many independent methods to perform each step of request processing. These methods will be called in turn in the process method. For example, there will be an independent method to find the ActionForm class corresponding to the current request, and a method to check whether the current user has the necessary permissions to execute action mapping. These give us great flexibility. In the published struts package, there is a requestprocessor class that provides the default implementation of each step of request processing. This means that you can just rewrite the methods you are interested in, and others use the default implementation. For example, struts calls request. XML by default Isuserinrole() to check whether the user has permission to execute the current actionmapping; At this time, if you want to query the database, all you have to do is rewrite the processroles () method to return true or false by querying whether the user has the necessary permissions.

First, we will see how the process () method is implemented by default, and then I will explain each method in the default requestprocessor class in detail, so that you can decide which part you want to change.

  public void process(HttpServletRequest request,HttpServletResponse response)
  throws IOException, ServletException {
  // Wrap multipart requests with a special wrapper
  request = processMultipart(request);
  // Identify the path component we will
  // use to select a mapping
  String path = processPath(request, response);
  if (path == null) {
  return;
  }
  if (log.isDebugEnabled()) {
  log.debug(“Processing a ‘” + request.getMethod() + “‘ for path ‘” + path + “‘”);
  }
  // Select a Locale for the current user if requested
  processLocale(request, response);
  // Set the content type and no-caching headers
  // if requested
  processContent(request, response);
  processNoCache(request, response);
  // General purpose preprocessing hook
  if (!processPreprocess(request, response)) {
  return;
  }
  // Identify the mapping for this request
  ActionMapping mapping =
  processMapping(request, response, path);
  if (mapping == null) {
  return;
  }
  // Check for any role required to perform this action
  if (!processRoles(request, response, mapping)) {
  return;
  }
  // Process any ActionForm bean related to this request
  ActionForm form = processActionForm(request, response, mapping);
  processPopulate(request, response, form, mapping);
  if (!processValidate(request, response, form, mapping)) {
  return;
  }
  // Process a forward or include specified by this mapping
  if (!processForward(request, response, mapping)) {
  return;
  }
  if (!processInclude(request, response, mapping)) {
  return;
  }
  // Create or acquire the Action instance to
  // process this request
  Action action =
  processActionCreate(request, response, mapping);
  if (action == null) {
  return;
  }
  // Call the Action instance itself
  ActionForward forward = processActionPerform(request, response,action, form, mapping);
  // Process the returned ActionForward instance
  processForwardConfig(request, response, forward);
  }

1. Processmutipart (): in this method, struts will read the request to check whether the contenttype of the request is multipart / form data. If so, the request will be parsed and wrapped in HttpServletRequest. When you create an HTML form to submit data, the content type of the request is application / x-www-form-urlencoded by default. However, if your form uses the input control of file type to allow users to upload files, you must change the contenttype to multipart / form data. If this is the case, you can no longer get the data submitted by the user through getparameter(); You must read the request as an InputStream and parse it yourself to get the parameter value.

2. Processpath (): in this method, struts will read the URI of the request to determine the path element, which is used to obtain the actionmapping element.
3. Processlocale (): in this method, struts will get the locale for the current request. If configured, this object can also be used as org. In httpsession apache. struts. action. The value of the local attribute is saved. As a side effect of this method, httpsession will be created. If you don’t want to create it, you can set the locale attribute to false in controllerconfig and in struts config XML looks like this:

  <controller>
  <set-property property=”locale” value=”false”/>
  </controller>

4. Processcontent (): by calling response Setcontenttype() to set contenttype for response. This method will first try from struts – config Get contenttype from XML configuration. Text / html is used by default. If you want to overwrite it, you can do something like this:

  <controller>
  <set-property property=”contentType” value=”text/plain”/>
  </controller>

5. Processnocache(): if the configuration is no cache, struts will set the following three headers for each response:

  requested in struts config.xml
  response.setHeader(“Pragma”, “No-cache”);
  response.setHeader(“Cache-Control”, “no-cache”);
  response.setDateHeader(“Expires”, 1);

If you want to set the no cache header, in struts config Add the following information to the XML:

  <controller>
  <set-property property=”noCache” value=”true”/>
  </controller>

6. Processpreprocess (): this method provides a hook for preprocessing, which can be overridden in subclasses. Its default implementation does nothing and always returns true. If false is returned, the processing of the current request will be terminated.

7. Processmapping (): this method will use the path information to get an actionmapping object. That is struts config Action element in XML file:

  <action path=”/newcontact” type=”com.sample.NewContactAction” name=”newContactForm” scope=”request”>
  <forward name=”sucess” path=”/sucessPage.do”/>
  <forward name=”failure” path=”/failurePage.do”/>
  </action>

The actionmapping element contains information such as the name of the action class and the ActionForm used to process the request. It also contains actionforwards information of the current actionmapping configuration.

8. Processroles (): struts web application provides an authorization scheme. That is, once a user logs into the container, the processroles () method of struts will call request Isuserinrole() to check whether he has the necessary role to run a given actionmapping.

  <action path=”/addUser” roles=”administrator”/>

Suppose you have an adduseraction and you just want the administrator to add a new user. All you have to do is add a role attribute to your adduseraction element, and the value of this attribute is administrator. In this way, this method will ensure that the user has the role of administraotr before running adduseraction.

9. Processactionform (): each actionmapping has a corresponding ActionForm class. When struts processes an actionmapping, it will find the name of the corresponding ActionForm class from the name attribute of the < action > element.

  <form-bean name=”newContactForm” type=”org.apache.struts.action.DynaActionForm”>
  <form-property name=”firstName” type=”java.lang.String”/>
  <form-property name=”lastName” type=”java.lang.String”/>
  </form-bean>

In our example, it will first check whether there is an org. Org in the request scope apache. struts. action. The object of the dynaactionform class does not exist. If it does, it will use this object. If it does not, it will create a new object and set it in the request scope.

10. Processpopulate(): in this method, struts will assemble the instance variable of ActionForm with the matching request parameter.

11. Processvalidate(): struts will call the validate method of your ActionForm class. If you return actionerrors from validate (), it will redirect the user to the page specified by the input attribute of the < action > element.

12. Processforward () and processinclude (): in these methods, struts will check the forward or include attribute of the < action > element. If found, it will place the forward or include request in the configured page.

  <action forward=”/Login.jsp” path=”/loginInput”/>
  <action include=”/Login.jsp” path=”/loginInput”/>

You can guess the difference from the names of these methods: processforward () eventually calls requestdispatcher Forward(), and processinclude() calls requestdispatcher include()。 If you configure both the forward and include properties, it will always call forward because forward is processed first.

13. Processactioncreate(): this method obtains the name of the action class from the type attribute of the < action > element and creates an instance that returns it. In our example, it will create a com sample. An instance of the newcontactaction class.

14. Processactionperform (): this method calls the extract () method of your action class, and your business logic is in the extract method.

15. Processforwardconfig(): the extract() method of your action class will return an actionforward object, which will indicate which page is displayed to the user. Therefore, struts will create a requestdispatcher for that page and call requestdispatcher forward()。

The above list describes the work done by the default requestprocessor implementation at each step when processing requests and the order of execution. As you can see, the requestprocessor is very flexible, allowing you to configure it by setting the properties of the < controller > element. For example, if your application is going to generate XML content instead of HTML, you can notify struts of these situations by setting the properties of the controller element.
  
  Create your own requestprocessor

From the above, we learned how the default implementation of requestprocessor works. Now let’s demonstrate an example of how to customize your own requestprocessor. To show how to create a user-defined requestprocessor, we will let our example implement the following two business requirements:

· we want to create a contactimageaction class, which will generate pictures instead of ordinary HTML pages.

· before each request is processed, we want to check the username attribute in the session to determine whether the user has logged in. If that attribute is not found, we will redirect the user to the login page.

We will implement these business requirements in two steps.

  1. Create your customrequestprocessor class, which will inherit from the requestprocessor class, as follows:

  public class CustomRequestProcessor
  extends RequestProcessor {
  protected boolean processPreprocess (
  HttpServletRequest request,HttpServletResponse response) {
   HttpSession session = request.getSession(false);
   //If user is trying to access login page
   // then don’t check
   if( request.getServletPath().equals(“/loginInput.do”)
  || request.getServletPath().equals(“/login.do”) )
    return true;
   //Check if userName attribute is there is session.
   //If so, it means user has allready logged in
   if( session != null && session.getAttribute(“userName”) != null)
    return true;
   else{
    try{
     //If no redirect user to login Page
     request.getRequestDispatcher(“/Login.jsp”).forward(request,response);
    }catch(Exception ex){
    }
   }
   return false;
  }

  protected void processContent(HttpServletRequest request,
  HttpServletResponse response) {
  //Check if user is requesting ContactImageAction
  // if yes then set image/gif as content type
  if( request.getServletPath().equals(“/contactimage.do”)){
   response.setContentType(“image/gif”);
   return;
  }
   super.processContent(request, response);
  } 
  }

In the processpreprocess method of the customrequestprocessor class, we check the username attribute of the session. If it is not found, we redirect the user to the login page.

For the requirement of generating pictures as output, we must override the processcontent method and first check whether the request is the / contactimage path. If so, we will set contenttype to image / GIF; Otherwise, it is set to text / HTML.

  2. In your struts config Add the following text after the < action mapping > element of the XML file to tell struts customrequestprocessor that it should be used as the requestprocessor class:

  <controller>
  <set-property property=”processorClass”value=”com.sample.util.CustomRequestProcessor”/>
  </controller>

Note that when you have only a few action classes that need to generate non text / HTML output, it is OK for you to override the processcontent () method. If this is not the case, you should create a struts child to handle the action requesting to generate pictures, and set the contenttype to image / GIF for them.

The tiles framework of struts uses its own requestprocessor to decorate the output of struts.

  ActionServlet

If you look at the web of your struts web application XML, you will see the following text:

  <web-app >
  <servlet>
  <servlet-name>action=</servlet-name>
  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
  <!– All your init-params go here–>
  </servlet>
  <servlet-mapping>
  <servlet-name>action</servlet-name>
  <url-pattern>*.do</url-pattern>
  </servlet-mapping>
  </web-app >

This means that actionservlet is responsible for handling all your struts requests. You can create a subclass of actionservlet to do specific things when the application starts, closes and requests each time. However, before inheriting the actionservlet class, you should try to create a plugin or requestprocessor to solve your problem. In servlet1 Before 1, the tiles framework modified the generated response based on actionservlet. But after 1.1, it started using the tilesrequestprocessor class.

Summary

The decision to develop your own MVC framework is a very big decision. You must consider the time and resources spent developing and maintaining the framework code. Struts is a very powerful and stable framework. You can modify it to meet most of your business needs.

On the other hand, don’t make a hasty decision to extend struts. If you write some low performance code in the requestprocessor, it will be executed every request, thus reducing the efficiency of your entire application. Moreover, there are some cases where developing your own MVC framework is better than extending struts.

Recommended Today

Summary of import and export usage in JavaScript

import import 和 require 的区别 import 和js的发展历史息息相关,历史上 js没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。这对开发大型工程非常不方便。在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。也就是我们常见的 require 方法。 比如 `let { stat, exists, readFile } = require(‘fs’);` 。ES6 在语言标准的层面上,实现了模块功能。ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。 import 的几种用法: 1. import defaultName from ‘modules.js’; 2. import { export } from ‘modules’; 3. import { export as ex1 } from […]