Java struts framework implementation

  • 2020-04-01 03:55:45
  • OfStack

This article simply and roughly implements the struts request forwarding function. Other features will be added later.

Recently, while studying javassist, I came across an article   Write it all together. The main simple description of MVC workflow, and the implementation of simple struts2 function.

Here, a simple struts2 framework is written in imitation, and some of my own understanding is added.

This article simply and roughly implements the struts request forwarding function. Other features will be added later.

First, in the struts2 framework, the request implementation and jump is mainly through the relevant configuration in struts.xml. A < Action> The tag represents the definition of a request. The action contains the name of the request. (2) request the corresponding implementation class "class"; The method can also be customized by the "method" property, if the default execute0 method is not configured. < Result tag defines the type "name" of result, including 'SUCCESS', 'NONE', 'LOGIN', 'INPUT', 'ERROR'; (2) the request type "type", including 'dispatcher(default)', 'chain', 'redirect', 'redirectAction', 'stream'; Result of the jump. After struts.xml is configured, the form in the interface can find the corresponding action tag by matching the action attribute with the name attribute value defined by the action, thus finding the corresponding class and the method to execute. The string string returned by the execution method matches the name in the result tag, and the next request action is performed based on the type defined.

Okay, now that we've seen how struts2 connects interface requests to program functionality, we've implemented this part of the functionality in our own code.

So, how do we do this?

We will need to achieve the function simply divided into two parts action part result part

    The action of

            We need to find the corresponding class and the execution method according to the request of the interface

      The result of

              We need to return a string of 'SUCCESS', 'NONE', 'LOGIN', 'INPUT', 'ERROR', etc
 
              (2) you need to specify different next request addresses for different return types
 
              You need to define the type of request, including 'dispatcher(default)', 'chain', 'redirect', 'redirectAction', 'stream'

In this article, the return type of result implements only 'SUCCESS' and 'LOGIN', and the default dispatcher request forwarding type is implemented regardless of the request type. Complete functions will be added later.

So, let's look at how to implement the above functionality through the code.  

We first defined two custom annotations, ActionAnnotation and ResultAnnotation, to request the required method and the jump request for the string returned by the method


 
 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface ActionAnnotation 
{ 
  String ActionName() default ""; 
  ResultAnnotation[] results() default {}; 
}
 
   
  @Retention(RetentionPolicy.RUNTIME) 
  @Target(ElementType.METHOD) 
  public @interface ResultAnnotation 
  { 
    ResultType name() default ResultType.SUCCESS; 
    String value() default "index.jsp"; 
  } 
 

Then we define an ActionContext class to hold the contents needed for a request


 
 
public class ActionContext 
{ 
   
  private String Url; 
    
   
  private String method; 
    
   
  private Map<ResultType, String> results; 
    
   
  private Class<?> classType; 
    
   
  private Object action; 
    
   
  private Class<?>[] paramsType; 
    
   
  private String[] actionParamsName; 
    
   
  private HttpServletRequest request; 
    
   
  private HttpServletResponse response; 
 
 

AnalysePackage is the method needed to assemble the ActionContext


   
    public static void analysePackage(String real_path, String scan_package) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NotFoundException 
    { 
      File file = new File(real_path); 
      if(file.isDirectory()) 
      { 
        File[] files = file.listFiles(); 
        for(File f : files) 
        { 
          analysePackage(f.getAbsolutePath(),scan_package); 
        } 
      } 
      else 
      { 
        String str = real_path.replaceAll("/", "."); 
        if (str.indexOf("classes." + scan_package) <= 0 || !str.endsWith(".class")) 
        { 
          return; 
        } 
        String fileName = str.substring(str.indexOf(scan_package),str.lastIndexOf(".class")); 
        Class<?> classType = Class.forName(fileName); 
        Method[] methods = classType.getMethods(); 
        for(Method method : methods) 
        { 
          if(method.isAnnotationPresent(ActionAnnotation.class)) 
          { 
            ActionContext actionContext = new ActionContext(); 
            ActionAnnotation actionAnnotation = (ActionAnnotation)method.getAnnotation(ActionAnnotation.class); 
            String url = actionAnnotation.ActionName(); 
            ResultAnnotation[] results = actionAnnotation.results(); 
            if(url.isEmpty() || results.length < 1) 
            { 
              throw new RuntimeException("method annotation error! method:" + method + " , ActionName:" + url + " , result.length:" + results.length); 
            } 
            actionContext.setUrl(url); 
            actionContext.setMethod(method.getName()); 
            Map<ResultType, String> map = new HashMap<ResultType, String>(); 
            for(ResultAnnotation result : results) 
            { 
              String value = result.value(); 
              if(value.isEmpty()) 
              { 
                throw new RuntimeException("Result name() is null"); 
              } 
              map.put(result.name(), value); 
            } 
            actionContext.setResults(map); 
            actionContext.setClassType(classType); 
            actionContext.setAction(classType.newInstance()); 
            actionContext.setParamsType(method.getParameterTypes()); 
            actionContext.setActionParamsName(getActionParamsName(classType, method.getName())); 
            urlMap.put(url, actionContext); 
          } 
        } 
      } 
    } 
 
 

getParams Is based on httpServletRequest The request content in the request gets the request parameter array, which is the parameter content of the body of the calling method


   
    public static Object[] getParams(HttpServletRequest request, Class<?>[] paramsType, String[] actionParamsName) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException 
    { 
      Object[] objects = new Object[paramsType.length]; 
      for(int i = 0; i < paramsType.length; i++) 
      { 
        Object object = null; 
        if(ParamsUtils.isBasicType(paramsType[i])) 
        { 
          objects[i] = ParamsUtils.getParam(request, paramsType[i], actionParamsName[i]); 
        } 
        else 
        { 
          Class<?> classType = paramsType[i]; 
          object = classType.newInstance(); 
          Field[] fields = classType.getDeclaredFields(); 
          for(Field field : fields) 
          { 
            Map<String, String[]> map = request.getParameterMap(); 
            for(Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) 
            { 
              String key = iterator.next(); 
              if(key.indexOf(".") <= 0) 
              { 
                continue; 
              } 
              String[] strs = key.split("\."); 
              if(strs.length != 2) 
              { 
                continue; 
              } 
              if(!actionParamsName[i].equals(strs[0])) 
              { 
                continue; 
              } 
              if(!field.getName().equals(strs[1])) 
              { 
                continue; 
              } 
              String value = map.get(key)[0]; 
              classType.getMethod(convertoFieldToSetMethod(field.getName()), field.getType()).invoke(object, value); 
              break; 
            } 
          } 
          objects[i] = object; 
        } 
      } 
      return objects; 
    } 
 
 

Okay, next. We are ready to implement the action method


  public class LoginAction 
  { 
    @ActionAnnotation(ActionName="login.action",results={@ResultAnnotation(name=ResultType.SUCCESS,value="index.jsp"),@ResultAnnotation(name=ResultType.LOGIN,value="login.jsp")}) 
    public ResultType login(String name, String password) 
    { 
      if("hello".equals(name) && "world".equals(password)) 
      { 
        return ResultType.SUCCESS; 
      } 
      return ResultType.LOGIN; 
    } 
      
    @ActionAnnotation(ActionName="loginForUser.action",results={@ResultAnnotation(name=ResultType.SUCCESS,value="index.jsp"),@ResultAnnotation(name=ResultType.LOGIN,value="login.jsp")}) 
    public ResultType loginForUser(int number, LoginPojo loginPojo) 
    { 
      if("hello".equals(loginPojo.getUsername()) && "world".equals(loginPojo.getPassword())) 
      { 
        return ResultType.SUCCESS; 
      } 
      return ResultType.LOGIN; 
    } 
  } 
 
 

Now, what we're going to do is we're going to have the program at startup go through the methods of all the classes in the working directory, find the methods that use the ActionAnnotation, and assemble them into the ActionContext, and that's what we're going to ask for. In this way, when the request arrives, we can find the corresponding ActionContext according to the address of the request and call the method through the reflection mechanism.
 
We ordered two servlets. An initializer to execute. One to filter all action requests


  <servlet> 
    <servlet-name>StrutsInitServlet</servlet-name> 
    <servlet-class>com.bayern.struts.one.servlet.StrutsInitServlet</servlet-class> 
    <init-param> 
      <param-name>scan_package</param-name> 
      <param-value>com.bayern.struts.one</param-value> 
    </init-param> 
    <load-on-startup>10</load-on-startup> 
   </servlet> 
     
   <servlet> 
    <servlet-name>DispatcherServlet</servlet-name> 
    <servlet-class>com.bayern.struts.one.servlet.DispatcherServlet</servlet-class> 
   </servlet> 
   <servlet-mapping> 
    <servlet-name>DispatcherServlet</servlet-name> 
    <url-pattern>*.action</url-pattern> 
   </servlet-mapping> 

The DispatcherServlet implements the filtering of the action request used and causes it to execute the corresponding action method, as well as the next jump


ublic void doPost(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException 
  { 
  
    request.setCharacterEncoding("utf-8"); 
    String url = request.getServletPath().substring(1); 
    ActionContext actionContext = DispatcherServletUtil.urlMap.get(url); 
    if(actionContext != null) 
    { 
      actionContext.setRequest(request); 
      actionContext.setResponse(response); 
      try 
      { 
        Object[] params = DispatcherServletUtil.getParams(request, actionContext.getParamsType(), actionContext.getActionParamsName()); 
        Class<?> classType = actionContext.getClassType(); 
        Method method = classType.getMethod(actionContext.getMethod(), actionContext.getParamsType()); 
        ResultType result = (ResultType)method.invoke(actionContext.getAction(), params); 
        Map<ResultType,String> results = actionContext.getResults(); 
        if(results.containsKey(result)) 
        { 
          String toUrl = results.get(result); 
          request.getRequestDispatcher(toUrl).forward(request, response); 
        } 
        else 
        { 
          throw new RuntimeException("result is error! result:" + result); 
        } 
          
      } 
 

Ok, now we have implemented the request forwarding functionality of the simplest struts 2 framework. Function write very rough, a lot of cases are not considered, I hope you give advice ~

The above is all the content of this article, I hope you can enjoy it.


Related articles: