java中struts 框架的实现_java

该文章主要简单粗暴的实现了struts的请求转发功能。 其他的功能后续会慢慢补上。

最近在学习javassist的内容,看到一篇文章  大家一起写mvc  主要简单的描述了mvc的工作流程,同时实现了简单的struts2功能。

这里仿照的写了个简单的struts2框架,同时加上了自己的一些理解。

该文章主要简单粗暴的实现了struts的请求转发功能。 其他的功能后续会慢慢补上。

首先,在struts2框架中,请求的实现、跳转主要是通过在struts.xml进行相关配置。 一个<action>标签表示一个请求的定义,action中包含了①请求的名称“name”;②请求对应的实现类“class” ;③同时还可通过“method”属性自定义执行的方法,若没配置默认执行execute0方法。<result》标签定义了①结果的类型“name”,包括'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR';②请求的类型“type”,包括'dispatcher(默认)'、'chain'、'redirect'、'redirectAction'、'stream';③结果的跳转。 在配置完struts.xml后,界面中的表单就可以通过action属性与action定义的name属性值相匹配找到对应的action标签,从而找到对应的class以及执行的方法。再根据执行方法返回的string字符串同result标签中的name相匹配,根据定义的type类型,进行下一步请求操作。

好了,在了解了struts2是怎么将界面请求同程序功能相连接后,我们通过自己的代码来实现这部分的功能。

那么,我们该如何下手了?

我们将需要实现的功能简单的分为两部分 ①action部分 ②result部分

   action部分

       ①我们需要根据界面的请求找到对应的类以及执行的方法

    result部分

        ①我们需要根据方法执行的逻辑返回'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR'这类型的字符串
 
        ②需要对不同的返回类型,指定不同的下一步请求地址
 
        ③需要定义请求的类型,包括'dispatcher(默认)'、'chain'、'redirect'、'redirectAction'、'stream'

在本文章中,result的返回类型只实现了'SUCCESS'、'LOGIN'两种,并且暂不考虑请求类型,实现的是默认的dispatcher请求转发类型。完善的功能后期会再补充。

那么,下面我们来通过代码看怎么实现如上功能。 

首先定义了ActionAnnotation和ResultAnnotation 两个自定义注解来请求需要对应的方法以及方法返回的字符串对应的跳转请求


/**
 * action注解:ActionName相当于web.xml配置中的url-pattern
 * @author linling
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ActionAnnotation
{
  String ActionName() default "";
  ResultAnnotation[] results() default {};
}

  /**
   * 返回注解对象:name相当于struts配置中的result的name,包括'SUCCESS'、'NONE'、'ERROR'、'INPUT'、'LOGIN';value相当于struts配置中对应返回跳转内容
   * @author linling
   *
   */
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.METHOD)
  public @interface ResultAnnotation
  {
    ResultType name() default ResultType.SUCCESS;
    String value() default "index.jsp";
  } 

然后我们定义一个ActionContext类,来保存一个请求所需要的内容


/**
 * 实现模拟struts根据配置文件跳转至action执行相应方法中需要的内容
 * @author linling
 *
 */
public class ActionContext
{
  /**
   * 相当于web.xml中url-pattern,唯一的
   */
  private String Url; 

  /**
   * ActionAnnotation注解对应方法,也就是action中要执行的方法
   */
  private String method; 

  /**
   * ActionAnnotation中的Result,对应action方法返回的类型。例如:key:'SUCCESS';value:'index.jsp'
   */
  private Map<ResultType, String> results; 

  /**
   * action的类
   */
  private Class<?> classType; 

  /**
   * action的对象
   */
  private Object action; 

  /**
   * 方法参数类型
   */
  private Class<?>[] paramsType; 

  /**
   * 方法参数的名称,注意这里方法名称需要和上面paramType参数一一对应
   * 可以理解为是struts中action中的属性
   */
  private String[] actionParamsName; 

  /**
   * 本次请求的HttpServletRequest
   */
  private HttpServletRequest request; 

  /**
   * 本次请求的HttpServletResponse
   */
  private HttpServletResponse response; 

analysePackage是在组装ActionContext需要的方法

  /**
     * 遍历scan_package包下的class文件,将使用了ActionAnnotation注解的方法进行解析,组装成ActionContext对象 并放入urlMap中
     * @param real_path
     * @param scan_package
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NotFoundException
     */
    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是根据httpServletRequest请求中的请求内容获得请求参数数组,该参数数组为调用方法体的参数内容

  /**
     * 根据 参数类型parasType 和 参数名actinParamsName 来解析请求request 构建参数object[]
     * @param request
     * @param paramsType
     * @param actionParamsName
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @throws SecurityException
     */
    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;
    } 

好了,接下来。我们可以来实现action方法了

  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;
    }
  } 

接下来,我们需要做的是让程序在启动的时候去遍历工作目录下所有类的方法,将使用了ActionAnnotation的方法找出来组装成ActionContext,这就是我们请求需要执行的方法。这样在请求到了的时候我们就可以根据请求的地址找到对应的ActionContext,并通过反射的机制进行方法的调用。
 
我们定了两个Servlet。一个用于执行初始化程序。一个用来过滤所有的action请求

  <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>

DispatcherServlet实现了对所用action请求的过滤,并使之执行对应的action方法,以及进行下一步的跳转

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);
        } 

      } 

好了,现在我们已经实现了最简单的strut2框架的请求转发的功能。功能写得很粗糙,很多情况都还未考虑进来,希望大家多多指点~

以上所述就是本文的全部内容了,希望大家能够喜欢。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索框架
struts
java struts2框架、java struts框架、java struts2框架搭建、java struts框架教程、java框架实现原理,以便于您获取更多的相关知识。

时间: 2024-09-27 18:09:00

java中struts 框架的实现_java的相关文章

Java的Struts框架中的if/else标签使用详解_java

这些标签执行可在每一种语言找到的一种基本条件流程. 'If'标签可用于本身或与"Else If''标签和/或单/多'Else'标签,如下图所示: <s:if test="%{false}"> <div>Will Not Be Executed</div> </s:if> <s:elseif test="%{true}"> <div>Will Be Executed</div>

详解Java的Struts框架中栈值和OGNL的使用_java

值栈:值栈是一个集合中的几个对象保持下列对象提供的顺序: 值栈可以通过JSP,Velocity或者Freemarker的标签.有各种不同的标签在单独的章节中,我们将学习,用于获取和设置Struts 2.0 的值栈. ValueStack的对象里面可以得到动作如下: ActionContext.getContext().getValueStack() 一旦拥有了值对象,就可以用下面的方法来操纵该对象: OGNL:对象图形导航语言(OGNL)是一个功能强大的表达式语言是用来参考值栈上的数据和操纵.

在Java的Struts框架中ONGL表达式的基础使用入门_java

首先了解下OGNL的概念:OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,能够遍历整个对象的结构图,实现对象属性类型的转换等功能. 此外,还得先需弄懂OGNL的一些知识: 1.OGNL表达式的计算是围绕OGNL上下文进行的. OGNL上下文实际上就是一个Map对象,由ognl.OgnlContext类表示.它里面可以存放很多个JavaBean对象.它

Java的Struts框架中&amp;lt;results&amp;gt;标签的使用方法_java

<results>标签在Struts2的MVC框架的视图中所扮演的角色.动作是负责执行业务逻辑.执行业务逻辑后,接下来的步骤是使用<results>标签显示的视图. 经常有一些附带导航规则的结果.例如,如果在操作方法是对用户进行验证,有三种可能的结果. (一)成功登录:(二)不成功的登录,用户名或密码错误:(三)帐户锁定. 在这种情况下的动作方法将被配置呈现的结果有三种可能的结果字符串和三个不同的看法.我们已经看到在前面的例子. 但是,Struts2 不配合使用JSP作为视图技术.

Java的Struts框架中Action的编写与拦截器的使用方法_java

Struts2 Action/动作动作是Struts2框架的核心,因为他们的任何MVC(模型 - 视图 - 控制器)框架.每个URL将被映射到一个特定的动作,它提供了来自用户的请求提供服务所需的处理逻辑. 但动作也提供其他两个重要的能力.首先,操作从请求数据的传输中起着重要的作用,通过向视图,无论是一个JSP或其它类型的结果.二,动作必须协助的框架,在确定结果应该渲染视图,在响应该请求将被退回. 创建动作:在Struts2的动作,唯一的要求是必须有一个无参数的方法返回String或结果的对象,必

简单说明Java的Struts框架中merge标签的使用方法_java

merge标签合并标记需要两个或两个以上的列表作为参数,并把它们合并在一起,如下所示: <s:merge var="myMergedIterator"> <s:param value="%{myList1}" /> <s:param value="%{myList2}" /> <s:param value="%{myList3}" /> </s:merge> <

Java的Struts框架中配置国际化的资源存储的要点解析_java

1.资源文件命名 国际化资源文件命名格式如下: basename_language_country.properties basename_language.properties basename.properties 其中basename为资源文件的基本名称,language和country必须是java支持的语言和国家.获取java支持的语言和国家代码如下: Locale[] locales = Locale.getAvailableLocales(); for(Locale locale:

通过实例深入学习Java的Struts框架中的OGNL表达式使用_java

Struts 2默认的表达式语言是OGNL,原因是它相对其它表达式语言具有下面几大优势: 1. 支持对象方法调用,如xxx.doSomeSpecial(): 2. 支持类静态的方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME: 3. 支持赋值操作和表达式串联,如price=100, discount=0.8, cal

详解Java的Struts框架中上传文件和客户端验证的实现_java

文件上传 Struts 2框架提供了内置支持处理文件上传使用基于HTML表单的文件上传.上传一个文件时,它通常会被存储在一个临时目录中,他们应该由Action类进行处理或移动到一个永久的目录,以确保数据不丢失. 请注意,服务器有一个安全策略可能会禁止写到目录以外的临时目录和属于web应用的目录. 在Struts中的文件上传是通过预先定义的拦截文件上传拦截器这是可通过org.apache.struts2.interceptor.FileUploadInterceptor类的defaultStack