`

关于struts2中的拦截器和登陆验证

 
阅读更多

今天给几个热爱JAVA的同学们讲了Struts2的一些知识,重点讨论了其最具价值的拦截器。

<wbr></wbr>

不知道大家还记不记得,在《Struts2之服务器端验证》里我说过这样一句话“在到达Login Action之前,验证已经完成了”。我很希望有人能提出这是为什么,这样我就可以说,这是拦截器的功劳,我们就可以研究拦截器了。<wbr></wbr>

其实,拦截器并不难,也不是十分难懂的东西。在这里再做一些补充.

<wbr></wbr>

打开帮助文档(struts-2.0.6\\docs\\docs\\interceptors.html)的拦截器部分。

<wbr></wbr>

能读懂英文技术文档是程序员必备的基本素质之一,慢慢来吧,只要静下心来逐字逐句的推敲,没有什么理解不了的。实在看不懂,那就看看[Action Lifecyle]这张图吧。Action生命周期,Action被一些拦截器包围着,也就是说在Action执行之前或之后,拦截器会被执行。

<wbr></wbr>

这就是拦截器,把程序看作是一个顺序执行的流,在执行Action的代码之前或之后,拦截器会打断Action的执行,让程序先执行拦截器的代码,这也许就是为什么把它叫做拦截器的原因。有的时候,拦截器还会阻止Action的执行,比如说在验证失败的情况下,应该让程序返回到输入界面让用户重新输入。

<wbr></wbr>

从图上还可以看出,一个Action之外不是只有一个拦截器,那么先执行哪个拦截器呢?Struts2把多个拦截器放在了一个栈中,我们把它叫做拦截器栈。一方面,栈可以解决拦截器的执行顺序问题,另一方面,把相关的拦截器放在一个栈中,管理起来也比较方便。

<wbr></wbr>

Struts2的一个优点是它的可配置性,我们可以根据实际情况选择需要的功能。当然也给我们带来了一些麻烦,就拿拦截器来说吧,要想让拦截器起作用,先要对它进行配置。配置拦截器,需要在struts.xml中加入相关配置:

<package name="default" extends="struts-default">

<wbr><wbr><wbr><wbr> &lt;interceptors&gt;</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;interceptor name="timer" class=".."/&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;interceptor name="logger" class=".."/&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;/interceptors&gt;</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;action name="login"</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr> class="tutorial.Login"&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="timer"/&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="logger"/&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;result name="input"&gt;login.jsp&lt;/result&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;result name="success"</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> type="redirect-action"&gt;/secure/home&lt;/result&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;/action&gt;</wbr></wbr></wbr></wbr>

</package>

=====================================================================

<interceptors>和</interceptors>标记对表示要配置一些拦截器,里面的每一条是一个拦截器。

<wbr></wbr>

<interceptor name="timer" class=".."/>

interceptor表示这是一个拦截器,name属性是给它起一个名字,class属性是指出它的实现类,也是代码的实际位置。

<wbr></wbr>

拦截器是拦截Action的,当然也要在Action的配置里加入对它的引用,指出这个Action要使用哪个拦截器。

<wbr></wbr>

<interceptor-ref name="timer"/>这句话是告诉Struts框架,login Action需要使用前面的timer拦截器。

<wbr></wbr>

有人也许会说,我们之前并没有配置拦截器,但刚才好像说过,不是也使用到拦截器了么?的确,可能是Struts2的开发者怕配置起来太麻烦了,没有人用吧:P,所以给我预先配置好了一些默认的拦截器和拦截器栈。

<wbr></wbr>

在struts-default.xml(struts-default.xml在struts2-core-2.0.6.jar包中)里面定义了很多拦截器:

<interceptor name="alias" class="com.opensymphony.xwork.interceptor.AliasInterceptor"/>

......

里面的validation和i18n就是我们之前用的验证和国际化功能。

<wbr></wbr>

还有很多拦截器栈:

<!-- Basic stack -->

<interceptor-stack name="basicStack">

<interceptor-ref name="exception"/>

<interceptor-ref name="servlet-config"/>

<interceptor-ref name="prepare"/>

<interceptor-ref name="static-params"/>

<interceptor-ref name="params"/>

<interceptor-ref name="conversionError"/>

</interceptor-stack>

...

<interceptor-stack name="defaultStack">

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="exception"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="alias"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="prepare"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="servlet-config"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="i18n"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="chain"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="model-driven"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="fileUpload"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="static-params"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="params"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="conversionError"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="validation"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="workflow"/&gt;</wbr></wbr></wbr></wbr></wbr>

</interceptor-stack>

<wbr></wbr>

<default-interceptor-ref name="defaultStack"/>

最后这条比较主要,是框架默认使用的拦截器栈,也就是说,defaultStack栈中的拦截器会默认起作用。

<wbr></wbr>

这也是我们没有配置validation和i18n,它们就起作用的原因。

<wbr></wbr>

顺便说一下,拦截器应该属于AOP(面向方面编程)思想的一种实现,它的确给开发带来了很多好处,一方面可以把一个大的问题分解成多个小问题分别处理,另一方面可以使Action专注与处理自己的事情,把相关的功能分散给各个拦截器处理。

<wbr></wbr>

在《Struts2之服务器端验证》中我们详细讨论了关于登陆验证的问题,只有通过登陆验证,拿到令牌的用户才能进入我们的系统。为此我们建立了登陆验证页面和相应的Action类,登陆验证(Login.jsp)负责提供给用户输入帐号和密码的机会,Login Action负责验证用户输入的帐号和密码是否正确,验证通过的情况下把帐号和密码保存到Session中,就相当于用户拿到了一块令牌,就是一个合法用户了。

<wbr></wbr>

我们的目的是限制非法用户,也就是没有拿到令牌的用户,不能访问我们的系统。但是我们只进行了登陆验证和保存令牌的工作,并没有做限制非法用户的工作。现在不守规矩的用户就可以直接在浏览器的地址栏里输入:http://localhost:8080/Success.jsp

访问我们的Success.jsp页面,这当然是不能容忍的。

<wbr></wbr>

在没有学习拦截器之前,我们当然可以在Success.jsp页面里加代码进行限制,先从Session中取出用户名和密码,如果取到了则说明用户已经登陆系统了,否则让浏览器显示Login.jsp让用户登陆系统,这样做一点问题也没有,就相当于我们在每一个房间门口都安排了一个检查令牌的人一样,万无一失。不过,如果系统的受限制资源多起来的时候,比如说像Success.jsp或Action很多,几十个,几百个的时候,这样的代码我们就要写很多遍,这当然也是聪明的我们所不能容忍的:P

<wbr></wbr>

显然,让拦截器来做这项工作再合适不过了。我们可以添加一个拦截器,让它在这些受限制的资源被执行之前先运行,在拦截器里我们检查用户是否登陆了系统,如果登陆了才让他们访问,否则送给用户一个Login.jsp让用户登陆。这样我们的检查代码只需要写一次就一劳永逸了。

<wbr></wbr>

明白了原理之后还需要做几项具体的工作:

1,自己写一个拦截器,实现检查用户是否登陆的功能。

2,添加拦截器的配置,让我们自己写的拦截器起作用。

<wbr></wbr>

首先我们来完成第一个任务,打开:

struts-2.0.6docsdocsguides.html里面的

struts-2.0.6docsdocswriting-interceptors.html帮助。

从这个帮助里我们可以看出,拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口。根据经验,init()方法应该是初始化拦截器的方法,可以把一些初始化工作的代码放在它里面,destroy()方法与之相反,在拦截器被销毁之前,让我们有机会执行一些善后工作。

<wbr></wbr>

显然intercept()方法,是添加真正执行拦截工作的代码的地方,如果我们不需要初始化和清理的操作,可以直接继承com.opensymphony.xwork2.interceptor.AbstractInterceptor类,覆盖它的intercept()方法。这个方法有个ActionInvocation类型的参数,可以用它取得Session或转发请求等操作。

<wbr></wbr>

先在src下建立一个包:tutorial.interceptor

在这个包下建立一个类LogonInterceptor继承于AbstractInterceptor,覆盖intercept()方法:

<wbr></wbr>

package tutorial.interceptor;

import java.util.Map;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

<wbr></wbr>

public class LogonInterceptor extends AbstractInterceptor {

<wbr><wbr><wbr><wbr><wbr> public String intercept(ActionInvocation invocation) throws Exception {</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // 取得请求的Action名</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> String<wbr><wbr><wbr> name = invocation.getInvocationContext().getName();</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> if (name.equals("Login")) {</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // 如果用户想登录,则使之通过</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> return invocation.invoke();</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> } else {</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // 取得Session。</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> ActionContext ac = invocation.getInvocationContext();</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> Map session =<wbr><wbr><wbr> (Map)ac.get(ServletActionContext.SESSION);</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> if (session == null) {</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // 如果Session为空,则让用户登陆。</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> return "login";</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> } else {</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> String username = (String)session.get("username");</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> if (username == null) {</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // Session不为空,但Session中没有用户信息,</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // 则让用户登陆</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> return "login";</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> } else {</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> // 用户已经登陆,放行~</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> return invocation.invoke();</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> }</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> }</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> }</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> }</wbr></wbr></wbr></wbr></wbr>

}

<wbr></wbr>

今天的内容有点多,接下来还要对这个拦截器进行配置:

关于怎样配置一个拦截器使之对所有的Action起作用请参考:

struts-2.0.6docsdocshow-do-we-configure-an-interceptor-to-be-used-with-every-action.html

修改struts.xml,

1,自定义拦截器

2,重定义默认拦截器堆栈

3,添加一个global-results,用户在验证失败的情况下跳转到登陆验证页面

struts.xml的完整内容:

<!DOCTYPE struts PUBLIC

<wbr><wbr><wbr><wbr><wbr> "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> "http://struts.apache.org/dtds/struts-2.0.dtd"&gt;</wbr></wbr></wbr></wbr></wbr>

<struts><!-- Configuration for the default package. -->

<constant name="struts.custom.i18n.resources" value="globalMessages" />

<wbr></wbr>

<include file="struts-validation.xml" />

<package name="default" extends="struts-default">

<wbr></wbr>

<wbr><wbr><wbr> &lt;!-- 自定义拦截器 --&gt;</wbr></wbr></wbr>

<wbr><wbr><wbr> &lt;interceptors&gt;</wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;interceptor name="logon" class="tutorial.interceptor.LogonInterceptor"/&gt;</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;!-- 自定义拦截器堆栈 --&gt;</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;interceptor-stack name="myStack"&gt;</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="logon"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;!-- 引用默认的拦截器堆栈 --&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr> &lt;interceptor-ref name="defaultStack"/&gt;</wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;/interceptor-stack&gt;</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr> &lt;/interceptors&gt;</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> &lt;!-- 重定义默认拦截器堆栈 --&gt;</wbr></wbr></wbr>

<wbr><wbr><wbr> &lt;default-interceptor-ref name="myStack"/&gt;</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> &lt;global-results&gt;</wbr></wbr></wbr>

<wbr><wbr><wbr><wbr> &lt;result name="login" type="redirect-action"&gt;Login!input.action&lt;/result&gt;</wbr></wbr></wbr></wbr>

<wbr><wbr><wbr> &lt;/global-results&gt;</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;action name="HelloWorld" class="tutorial.HelloWorld"&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>&lt;result&gt;/HelloWorld.jsp&lt;/result&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;/action&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;action name="Login" class="tutorial.Login"&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;result&gt;/Success.jsp&lt;/result&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;result name="input"&gt;/Login.jsp&lt;/result&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr> &lt;/action&gt;<wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

</package>

</struts>

<wbr></wbr>

保存修改并布署项目到Tomcat下,启动Tomcat

在浏览器的地址栏中输入:

http://localhost:8080/tutorial/HelloWorld.action

系统会自动转到Login页面。
分享到:
评论

相关推荐

    大学生创新创业训练计划经验分享.zip

    大学生创新创业训练计划(以下简称为“大创计划”)是一项旨在提升大学生创新能力和创业精神的实践活动。通过这项计划,学生可以在导师的指导下,自主开展研究性学习和创业实践。下面我将分享一些关于大创计划的经验和建议。

    node-v12.22.3-x86.msi

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    毕业设计-The coding solutions of Leetcode and 剑指Offer using .zip

    这里为你收集整理了关于毕业设计、课程设计可参考借鉴的资料一份,质量非常高,如果你投入时间去研究几天相信肯定对你有很大的帮助。到时候你会回来感谢我的。 本资源是经过本地编译测试、可打开、可运行的项目、文件或源码,可以用于毕业设计、课程设计的应用、参考和学习需求,请放心下载。 祝愿你在这个毕业设计项目中取得巨大进步,顺利毕业! 但还需强调一下,这些项目源码仅供学习和研究之用。在使用这些资源时,请务必遵守学术诚信原则和相关法律法规,不得将其用于任何商业目的或侵犯他人权益的行为。对于任何因使用本资源而导致的问题,包括但不限于数据丢失、系统崩溃或安全漏洞,风险自担哦!

    【微信小程序毕业设计】宠物店商城系统开发项目(源码+演示视频+说明).rar

    【微信小程序毕业设计】宠物店商城系统开发项目(源码+演示视频+说明).rar 【项目技术】 微信小程序开发工具+java后端+mysql 【演示视频-编号:282】 https://pan.quark.cn/s/cb634e7c02b5 【实现功能】 本系统实现的是和宠物相关的信息管理和发布,加入了商品销售的功能。操作角色为管理员和用户、商家,管理员的功能为用户管理、商家管理、宠物分类管理、宠物信息管理、商品分类管理、宠物用品管理、项目类型管理、服务项目管理、宠物日志管理、订单管理等;用户的功能为购买宠物、商品、预约服务发表日志管理订单等。商家功能为提供宠物、宠物用品、宠物服务,管理订单和服务预约等。

    雷迪森的工具包(95分以上课程大作业).zip

    Java SSM项目是一种使用Java语言和SSM框架(Spring + Spring MVC + MyBatis)开发的Web应用程序。SSM是一种常用的Java开发框架组合,它结合了Spring框架、Spring MVC框架和MyBatis框架的优点,能够快速构建可靠、高效的企业级应用。 1. Spring框架:Spring是一个轻量级的Java开发框架,提供了丰富的功能和模块,用于开发企业级应用。它包括IoC(Inverse of Control,控制反转)容器、AOP(Aspect-Oriented Programming,面向切面编程)等特性,可以简化开发过程、提高代码的可维护性和可测试性。 2. Spring MVC框架:Spring MVC是基于Spring框架的Web框架,用于开发Web应用程序。它采用MVC(Model-View-Controller,模型-视图-控制器)的架构模式,将应用程序分为模型层、视图层和控制器层,提供了处理请求、渲染视图和管理流程的功能。 3. MyBatis框架:MyBatis是一个持久层框架,用于与数据库进行交互。它提供了一种将数据库操作与Java对象映射起来的方式,避免了手动编写繁琐的SQL语句,并提供了事务管理和缓存等功能,简化了数据库访问的过程

    node-v9.10.1.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    基于B2C的网上拍卖系统_秒杀与竞价.zip

    基于B2C的网上拍卖系统主要用于帮助人们应用互联网方便快捷买到自己所中意的商品,并参与到秒杀与竞拍当中。 主要功能包括: 1.前台模块 (1)普通用户登录/注册。 (2)分类查看商品(普通商品与促销商品) (3)查看商品详细信息 (4)查看秒杀商品 (5)查看竞拍商品 (6)将商品加入购物车 (7)购买,结算功能 (8)留言 2.后台模块 (1)修改密码 (2)商品管理: -- 编辑/删除 -- 设置/取消促销 (3)秒杀商品:设置/取消秒杀 (4)竞拍商品:设置/取消竞拍 (5)订单管理:查看订单 (5)留言管理:查看/删除留言 项目访问路径: 前台:http://localhost:8080/sale 后台:http://localhost:8080/sale/user/adminlogin

    【前端素材】大数据-政务大数据共享交换平台.zip

    大数据技术指的是用于处理和分析大规模数据集的技术和工具。以下是一些常见的大数据技术和工具: Hadoop:Apache Hadoop是一个用于分布式存储和处理大规模数据的开源框架。它包括Hadoop Distributed File System(HDFS)用于数据存储和MapReduce用于数据处理。 Spark:Apache Spark是一个快速、通用的集群计算系统,提供了比MapReduce更快的数据处理能力。它支持内存计算和更多复杂的数据处理流程。 NoSQL数据库:NoSQL数据库(如MongoDB、Cassandra等)则更适用于处理这类数据。 数据仓库:数据仓库是一个用于集成和分析大规模数据的存储系统,一些知名的数据仓库包括Snowflake、Amazon Redshift等。 数据湖:数据湖是一个存储结构化和非结构化数据的存储池,用于支持数据分析和机器学习应用。 机器学习:大数据技术也广泛应用于机器学习领域,支持大规模数据的模型训练和预测分析。 流式处理:针对实时数据处理需求,流式处理技术(如Apache Kafka、Apache Flink)可以实时。

    【前端素材】大数据-气象预报大数据平台.zip

    大数据技术指的是用于处理和分析大规模数据集的技术和工具。以下是一些常见的大数据技术和工具: Hadoop:Apache Hadoop是一个用于分布式存储和处理大规模数据的开源框架。它包括Hadoop Distributed File System(HDFS)用于数据存储和MapReduce用于数据处理。 Spark:Apache Spark是一个快速、通用的集群计算系统,提供了比MapReduce更快的数据处理能力。它支持内存计算和更多复杂的数据处理流程。 NoSQL数据库:NoSQL数据库(如MongoDB、Cassandra等)则更适用于处理这类数据。 数据仓库:数据仓库是一个用于集成和分析大规模数据的存储系统,一些知名的数据仓库包括Snowflake、Amazon Redshift等。 数据湖:数据湖是一个存储结构化和非结构化数据的存储池,用于支持数据分析和机器学习应用。 机器学习:大数据技术也广泛应用于机器学习领域,支持大规模数据的模型训练和预测分析。 流式处理:针对实时数据处理需求,流式处理技术(如Apache Kafka、Apache Flink)可以实时。

    数学模型算法多目标决策分析方法.pptx

    数学模型算法

    ############ 光电传感器的描述

    光电传感器

    node-v12.17.0-x86.msi

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    Web开发工具和方法课程的学术项目Java、Spring、Hibernate、Angular

    Java SSM项目是一种使用Java语言和SSM框架(Spring + Spring MVC + MyBatis)开发的Web应用程序。SSM是一种常用的Java开发框架组合,它结合了Spring框架、Spring MVC框架和MyBatis框架的优点,能够快速构建可靠、高效的企业级应用。 1. Spring框架:Spring是一个轻量级的Java开发框架,提供了丰富的功能和模块,用于开发企业级应用。它包括IoC(Inverse of Control,控制反转)容器、AOP(Aspect-Oriented Programming,面向切面编程)等特性,可以简化开发过程、提高代码的可维护性和可测试性。 2. Spring MVC框架:Spring MVC是基于Spring框架的Web框架,用于开发Web应用程序。它采用MVC(Model-View-Controller,模型-视图-控制器)的架构模式,将应用程序分为模型层、视图层和控制器层,提供了处理请求、渲染视图和管理流程的功能。 3. MyBatis框架:MyBatis是一个持久层框架,用于与数据库进行交互。它提供了一种将数据库操作与Java对象映射起来的方式,避免了手动编写繁琐的SQL语句,并提供了事务管理和缓存等功能,简化了数据库访问的过程

    云计算基础课件—云安全dr.pptx

    云计算基础课件—云安全dr.pptx

    jizu3.cod

    jizu3.cod

    线性规划模型及应用中的整数规划讲义.pptx

    数学模型算法

    JAVA毕业设计之医疗挂号管理系统(springboot+mysql)完整源码.zip

    医疗挂号管理系统是一款基于Spring Boot和MySQL的Java毕业设计项目,旨在为医院提供一个高效、便捷的挂号管理解决方案。该系统采用了当下流行的微服务架构,通过Spring Boot框架实现快速开发和部署,同时使用MySQL数据库进行数据存储和管理。 在技术方面,本项目主要使用了以下技术: Spring Boot:一个基于Spring框架的快速开发工具,可以简化项目的搭建、配置和部署过程,提高开发效率。 MySQL:一款开源的关系型数据库管理系统,用于存储和管理医疗挂号管理系统中的数据。 MyBatis:一款优秀的持久层框架,用于实现Java对象与数据库之间的映射关系,简化数据库操作。 Redis:一款高性能的键值对缓存数据库,用于缓存系统中的热点数据,提高系统性能。 Bootstrap:一款前端UI框架,用于构建响应式的用户界面,提高用户体验。 jQuery:一款轻量级的JavaScript库,用于简化前端开发,实现动态效果和Ajax交互。 在功能方面,医疗挂号管理系统主要包括以下模块: 用户注册与登录:用户可以注册并登录系统,创建个人账户。 医生信息管理:管理员可以添加、修改和删除医生信息,包括姓名、职称、科室等。 挂号管理:用户可以选择医生和就诊时间进行挂号,支持在线支付挂号费用。 挂号记录查询:用户可以查看自己的挂号记录,包括挂号时间、医生信息等。 取消挂号:用户可以在规定时间内取消挂号,系统将退还挂号费用。 预约提醒:系统会在预约就诊前通过短信或邮件提醒用户。 统计报表:管理员可以查看系统的挂号统计数据,包括每日挂号人数、各科室挂号人数等。 通过这些功能,医疗挂号管理系统为医院提供了一个便捷、高效的挂号管理解决方案,有助于提高医疗服务质量和患者满意度。

    基于C++ QT的航空订票系统 .zip

    基于QT的系统

    线性规划模型及应用中的单纯形方法讲义.pptx

    数学模型算法

    小程序-43-基于小程序的企业产品推广系统-源码.zip

    提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

Global site tag (gtag.js) - Google Analytics