月度归档:2018年10月

Servlet 3.0 新特性详解

Servlet 3.0 新特性概述

Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布。该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发和部署。其中有几项特性的引入让开发者感到非常兴奋,同时也获得了 Java 社区的一片赞誉之声:

  1. 异步处理支持:有了该特性,Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程。在接收到请求之后,Servlet 线程可以将耗时的操作委派给另一个线程来完成,自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况,这将大大减少服务器资源的占用,并且提高并发处理速度。
  2. 新增的注解支持:该版本新增了若干注解,用于简化 Servlet、过滤器(Filter)和监听器(Listener)的声明,这使得 web.xml 部署描述文件从该版本开始不再是必选的了。
  3. 可插性支持:熟悉 Struts2 的开发者一定会对其通过插件的方式与包括 Spring 在内的各种常用框架的整合特性记忆犹新。将相应的插件封装成 JAR 包并放在类路径下,Struts2 运行时便能自动加载这些插件。现在 Servlet 3.0 提供了类似的特性,开发者可以通过插件的方式很方便的扩充已有 Web 应用的功能,而不需要修改原有的应用。

下面我们将逐一讲解这些新特性,通过下面的学习,读者将能够明晰了解 Servlet 3.0 的变化,并能够顺利使用它进行日常的开发工作。

异步处理支持

Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:首先,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;接着,调用业务接口的某些方法,以完成业务处理;最后,根据处理的结果提交响应,Servlet 线程结束。其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。

Servlet 3.0 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:首先,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;接着,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,此时 Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。如此一来, Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。

异步处理特性可以应用于 Servlet 和过滤器两种组件,由于异步处理的工作模式和普通工作模式在实现上有着本质的区别,因此默认情况下,Servlet 和过滤器并没有开启异步处理特性,如果希望使用该特性,则必须按照如下的方式启用:

  1. 对于使用传统的部署描述文件 (web.xml) 配置 Servlet 和过滤器的情况,Servlet 3.0 为 <servlet> 和 <filter> 标签增加了 <async-supported> 子标签,该标签的默认取值为 false,要启用异步处理支持,则将其设为 true 即可。以 Servlet 为例,其配置方式如下所示:
    1
    2
    3
    4
    5
    <servlet>
        <servlet-name>DemoServlet</servlet-name>
        <servlet-class>footmark.servlet.Demo Servlet</servlet-class>
        <async-supported>true</async-supported>
    </servlet>
  2. 对于使用 Servlet 3.0 提供的 @WebServlet 和 @WebFilter 进行 Servlet 或过滤器配置的情况,这两个注解都提供了 asyncSupported 属性,默认该属性的取值为 false,要启用异步处理支持,只需将该属性设置为 true 即可。以 @WebFilter 为例,其配置方式如下所示:
1
2
@WebFilter(urlPatterns = "/demo",asyncSupported = true)
public class DemoFilter implements Filter{...}

一个简单的模拟异步处理的 Servlet 示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@WebServlet(urlPatterns = "/demo", asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws IOException, ServletException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("进入Servlet的时间:" + new Date() + ".");
        out.flush();
        //在子线程中执行业务调用,并由其负责输出响应,主线程退出
        AsyncContext ctx = req.startAsync();
        new Thread(new Executor(ctx)).start();
        out.println("结束Servlet的时间:" + new Date() + ".");
        out.flush();
    }
}
public class Executor implements Runnable {
    private AsyncContext ctx = null;
    public Executor(AsyncContext ctx){
        this.ctx = ctx;
    }
    public void run(){
        try {
            //等待十秒钟,以模拟业务方法的执行
            Thread.sleep(10000);
            PrintWriter out = ctx.getResponse().getWriter();
            out.println("业务处理完毕的时间:" + new Date() + ".");
            out.flush();
            ctx.complete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

除此之外,Servlet 3.0 还为异步处理提供了一个监听器,使用 AsyncListener 接口表示。它可以监控如下四种事件:

  1. 异步线程开始时,调用 AsyncListener 的 onStartAsync(AsyncEvent event) 方法;
  2. 异步线程出错时,调用 AsyncListener 的 onError(AsyncEvent event) 方法;
  3. 异步线程执行超时,则调用 AsyncListener 的 onTimeout(AsyncEvent event) 方法;
  4. 异步执行完毕时,调用 AsyncListener 的 onComplete(AsyncEvent event) 方法;

要注册一个 AsyncListener,只需将准备好的 AsyncListener 对象传递给 AsyncContext 对象的 addListener() 方法即可,如下所示:

1
2
3
4
5
6
7
AsyncContext ctx = req.startAsync();
ctx.addListener(new AsyncListener() {
    public void onComplete(AsyncEvent asyncEvent) throws IOException {
        // 做一些清理工作或者其他
    }
    ...
});

新增的注解支持

Servlet 3.0 的部署描述文件 web.xml 的顶层标签 <web-app> 有一个 metadata-complete 属性,该属性指定当前的部署描述文件是否是完全的。如果设置为 true,则容器在部署时将只依赖部署描述文件,忽略所有的注解(同时也会跳过 web-fragment.xml 的扫描,亦即禁用可插性支持,具体请看后文关于 可插性支持的讲解);如果不配置该属性,或者将其设置为 false,则表示启用注解支持(和可插性支持)。

@WebServlet

@WebServlet 用于将一个类声明为 Servlet,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为 Servlet。该注解具有下表给出的一些常用属性(以下所有属性均为可选属性,但是 vlaue 或者 urlPatterns 通常是必需的,且二者不能共存,如果同时指定,通常是忽略 value 的取值):

表 1. @WebServlet 主要属性列表

下面是一个简单的示例:

1
2
3
4
5
@WebServlet(urlPatterns = {"/simple"}, asyncSupported = true,
loadOnStartup = -1, name = "SimpleServlet", displayName = "ss",
initParams = {@WebInitParam(name = "username", value = "tom")}
)
public class SimpleServlet extends HttpServlet{ … }

如此配置之后,就可以不必在 web.xml 中配置相应的 <servlet> 和 <servlet-mapping> 元素了,容器会在部署时根据指定的属性将该类发布为 Servlet。它的等价的 web.xml 配置形式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<servlet>
    <display-name>ss</display-name>
    <servlet-name>SimpleServlet</servlet-name>
    <servlet-class>footmark.servlet.SimpleServlet</servlet-class>
    <load-on-startup>-1</load-on-startup>
    <async-supported>true</async-supported>
    <init-param>
        <param-name>username</param-name>
        <param-value>tom</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>SimpleServlet</servlet-name>
    <url-pattern>/simple</url-pattern>
</servlet-mapping>

@WebInitParam

该注解通常不单独使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是为 Servlet 或者过滤器指定初始化参数,这等价于 web.xml 中 <servlet> 和 <filter> 的 <init-param> 子标签。@WebInitParam 具有下表给出的一些常用属性:

表 2. @WebInitParam 的常用属性

@WebFilter

@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 ):

表 3. @WebFilter 的常用属性

下面是一个简单的示例:

1
2
@WebFilter(servletNames = {"SimpleServlet"},filterName="SimpleFilter")
public class LessThanSixFilter implements Filter{...}

如此配置之后,就可以不必在 web.xml 中配置相应的 <filter> 和 <filter-mapping> 元素了,容器会在部署时根据指定的属性将该类发布为过滤器。它等价的 web.xml 中的配置形式为:

1
2
3
4
5
6
7
8
<filter>
    <filter-name>SimpleFilter</filter-name>
    <filter-class>xxx</filter-class>
</filter>
<filter-mapping>
    <filter-name>SimpleFilter</filter-name>
    <servlet-name>SimpleServlet</servlet-name>
</filter-mapping>

@WebListener

该注解用于将类声明为监听器,被 @WebListener 标注的类必须实现以下至少一个接口:

  • ServletContextListener
  • ServletContextAttributeListener
  • ServletRequestListener
  • ServletRequestAttributeListener
  • HttpSessionListener
  • HttpSessionAttributeListener

该注解使用非常简单,其属性如下:

表 4. @WebListener 的常用属性

一个简单示例如下:

1
2
@WebListener("This is only a demo listener")
public class SimpleListener implements ServletContextListener{...}

如此,则不需要在 web.xml 中配置 <listener> 标签了。它等价的 web.xml 中的配置形式如下:

1
2
3
<listener>
    <listener-class>footmark.servlet.SimpleListener</listener-class>
</listener>

@MultipartConfig

该注解主要是为了辅助 Servlet 3.0 中 HttpServletRequest 提供的对上传文件的支持。该注解标注在 Servlet 上面,以表示该 Servlet 希望处理的请求的 MIME 类型是 multipart/form-data。另外,它还提供了若干属性用于简化对上传文件的处理。具体如下:

表 5. @MultipartConfig 的常用属性

可插性支持

如果说 3.0 版本新增的注解支持是为了简化 Servlet/ 过滤器 / 监听器的声明,从而使得 web.xml 变为可选配置, 那么新增的可插性 (pluggability) 支持则将 Servlet 配置的灵活性提升到了新的高度。熟悉 Struts2 的开发者都知道,Struts2 通过插件的形式提供了对包括 Spring 在内的各种开发框架的支持,开发者甚至可以自己为 Struts2 开发插件,而 Servlet 的可插性支持正是基于这样的理念而产生的。使用该特性,现在我们可以在不修改已有 Web 应用的前提下,只需将按照一定格式打成的 JAR 包放到 WEB-INF/lib 目录下,即可实现新功能的扩充,不需要额外的配置。

Servlet 3.0 引入了称之为“Web 模块部署描述符片段”的 web-fragment.xml 部署描述文件,该文件必须存放在 JAR 文件的 META-INF 目录下,该部署描述文件可以包含一切可以在 web.xml 中定义的内容。JAR 包通常放在 WEB-INF/lib 目录下,除此之外,所有该模块使用的资源,包括 class 文件、配置文件等,只需要能够被容器的类加载器链加载的路径上,比如 classes 目录等。

现在,为一个 Web 应用增加一个 Servlet 配置有如下三种方式 ( 过滤器、监听器与 Servlet 三者的配置都是等价的,故在此以 Servlet 配置为例进行讲述,过滤器和监听器具有与之非常类似的特性 ):

  • 编写一个类继承自 HttpServlet,将该类放在 classes 目录下的对应包结构中,修改 web.xml,在其中增加一个 Servlet 声明。这是最原始的方式;
  • 编写一个类继承自 HttpServlet,并且在该类上使用 @WebServlet 注解将该类声明为 Servlet,将该类放在 classes 目录下的对应包结构中,无需修改 web.xml 文件。
  • 编写一个类继承自 HttpServlet,将该类打成 JAR 包,并且在 JAR 包的 META-INF 目录下放置一个 web-fragment.xml 文件,该文件中声明了相应的 Servlet 配置。web-fragment.xml 文件示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment
    xmlns=http://java.sun.com/xml/ns/javaee
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
    metadata-complete="true">
    <servlet>
        <servlet-name>fragment</servlet-name>
        <servlet-class>footmark.servlet.FragmentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>fragment</servlet-name>
        <url-pattern>/fragment</url-pattern>
    </servlet-mapping>
</web-fragment>

从上面的示例可以看出,web-fragment.xml 与 web.xml 除了在头部声明的 XSD 引用不同之外,其主体配置与 web.xml 是完全一致的。

由于一个 Web 应用中可以出现多个 web-fragment.xml 声明文件,加上一个 web.xml 文件,加载顺序问题便成了不得不面对的问题。Servlet 规范的专家组在设计的时候已经考虑到了这个问题,并定义了加载顺序的规则。

web-fragment.xml 包含了两个可选的顶层标签,<name> 和 <ordering>,如果希望为当前的文件指定明确的加载顺序,通常需要使用这两个标签,<name> 主要用于标识当前的文件,而 <ordering> 则用于指定先后顺序。一个简单的示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<web-fragment...>
    <name>FragmentA</name>
    <ordering>
        <after>
            <name>FragmentB</name>
            <name>FragmentC</name>
        </after>
    <before>
        <others/>
    </before>
    </ordering>
    ...
</web-fragment>

如上所示, <name> 标签的取值通常是被其它 web-fragment.xml 文件在定义先后顺序时引用的,在当前文件中一般用不着,它起着标识当前文件的作用。

在 <ordering> 标签内部,我们可以定义当前 web-fragment.xml 文件与其他文件的相对位置关系,这主要通过 <ordering> 的 <after> 和 <before> 子标签来实现的。在这两个子标签内部可以通过 <name> 标签来指定相对应的文件。比如:

1
2
3
4
<after>
    <name>FragmentB</name>
    <name>FragmentC</name>
</after>

以上片段则表示当前文件必须在 FragmentB 和 FragmentC 之后解析。<before> 的使用于此相同,它所表示的是当前文件必须早于 <before> 标签里所列出的 web-fragment.xml 文件。

除了将所比较的文件通过 <name> 在 <after> 和 <begin> 中列出之外,Servlet 还提供了一个简化的标签 <others/>。它表示除了当前文件之外的其他所有的 web-fragment.xml 文件。该标签的优先级要低于使用 <name> 明确指定的相对位置关系。

ServletContext 的性能增强

除了以上的新特性之外,ServletContext 对象的功能在新版本中也得到了增强。现在,该对象支持在运行时动态部署 Servlet、过滤器、监听器,以及为 Servlet 和过滤器增加 URL 映射等。以 Servlet 为例,过滤器与监听器与之类似。ServletContext 为动态配置 Servlet 增加了如下方法:

  • ServletRegistration.Dynamic addServlet(String servletName,Class<? extends Servlet> servletClass)
  • ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
  • ServletRegistration.Dynamic addServlet(String servletName, String className)
  • <T extends Servlet> T createServlet(Class<T> clazz)
  • ServletRegistration getServletRegistration(String servletName)
  • Map<String,? extends ServletRegistration> getServletRegistrations()

其中前三个方法的作用是相同的,只是参数类型不同而已;通过 createServlet() 方法创建的 Servlet,通常需要做一些自定义的配置,然后使用 addServlet() 方法来将其动态注册为一个可以用于服务的 Servlet。两个 getServletRegistration() 方法主要用于动态为 Servlet 增加映射信息,这等价于在 web.xml( 抑或 web-fragment.xml) 中使用 <servlet-mapping> 标签为存在的 Servlet 增加映射信息。

以上 ServletContext 新增的方法要么是在 ServletContextListener 的 contexInitialized 方法中调用,要么是在 ServletContainerInitializer 的 onStartup() 方法中调用。

ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口,容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup() 方法处理,我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类,过滤掉不希望给 onStartup() 处理的类。

HttpServletRequest 对文件上传的支持

此前,对于处理上传文件的操作一直是让开发者头疼的问题,因为 Servlet 本身没有对此提供直接的支持,需要使用第三方框架来实现,而且使用起来也不够简单。如今这都成为了历史,Servlet 3.0 已经提供了这个功能,而且使用也非常简单。为此,HttpServletRequest 提供了两个方法用于从请求中解析出上传的文件:

  • Part getPart(String name)
  • Collection<Part> getParts()

前者用于获取请求中给定 name 的文件,后者用于获取所有的文件。每一个文件用一个 javax.servlet.http.Part 对象来表示。该接口提供了处理文件的简易方法,比如 write()、delete() 等。至此,结合 HttpServletRequest 和 Part 来保存上传的文件变得非常简单,如下所示:

1
2
3
Part photo = request.getPart("photo");
photo.write("/tmp/photo.jpg");
// 可以将两行代码简化为 request.getPart("photo").write("/tmp/photo.jpg") 一行。

另外,开发者可以配合前面提到的 @MultipartConfig 注解来对上传操作进行一些自定义的配置,比如限制上传文件的大小,以及保存文件的路径等。其用法非常简单,故不在此赘述了。

需要注意的是,如果请求的 MIME 类型不是 multipart/form-data,则不能使用上面的两个方法,否则将抛异常。

总结

Servlet 3.0 的众多新特性使得 Servlet 开发变得更加简单,尤其是异步处理特性和可插性支持的出现,必将对现有的 MVC 框架产生深远影响。虽然我们通常不会自己去用 Servlet 编写控制层代码,但是也许在下一个版本的 Struts 中,您就能切实感受到这些新特性带来的实质性改变。

相关主题

  • JSR-000315 Java Servlet 3.0 规范:这里除了可以下载 Servlet 3.0 的规范文档,还可以了解关于与该规范相关的一些其他信息。
  • GlassFish 项目主页:可以在这里现在 GlassFish V3 版本,这是 SUN 提供的 Java EE 6 规范的参考实现。
  • “Servlet 2.2 的新特征”(developerWorks,2000 年 12 月):讨论 Servlet 2.2 一些新的比较重要的特征,并给出了一些简单的例子来演示这些特征的用途及用法。
  • developerWorks Java 技术专区:数百篇关于 Java 编程各个方面的文章。

 

来源:https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/

关于web.xml配置的那些事儿

1.简介

web.xml文件是Java web项目中的一个配置文件,主要用于配置欢迎页、Filter、Listener、Servlet等,但并不是必须的,一个java web项目没有web.xml文件照样能跑起来。Tomcat容器/conf目录下也有作用于全局web应用web.xml文件,当一个web项目要启动时,Tomcat会首先加载项目中的web.xml文件,然后通过其中的配置来启动项目,只有配置的各项没有错误时,项目才能正常启动。

那么web.xml文件中到底有些什么内容呢?我们要如何去配置它以适应我们的项目呢?
首先让我们从Tomcat加载资源的顺序开始,一步步分析web.xml文件的作用。

2.Tomcat加载资源顺序

Tomcat启动时加载资源主要有三个阶段:

  • 第一阶段:JVM相关资源
    (1)$JAVA_HOME/jre/lib/ext/*.jar
    (2)系统classpath环境变量中的*.jar和*.class 
  • 第二阶段:Tomcat自身相关资源
    (1)$CATALINA_HOME/common/classes/*.class  
    (2)$CATALINA_HOME/commons/endorsed/*.jar   
    (3)$CATALINA_HOME/commons/i18n/*.jar   
    (4)$CATALINA_HOME/common/lib/*.jar   
    (5)$CATALINA_HOME/server/classes/*.class   
    (6)$CATALINA_HOME/server/lib/*.jar   
    (7)$CATALINA_BASE/shared/classes/*.class   
    (8)$CATALINA_BASE/shared/lib/*.jar 
  • 第三阶段:Web应用相关资源
    (1)具体应用的webapp目录: /WEB-INF/classes/*.class   
    (2)具体应用的webapp: /WEB-INF/lib/*.jar
    

在同一个文件夹下,jar包是按顺序从上到下依次加载,由ClassLoader的双亲委托模式加载机制我们可以知道,假设两个包名和类名完全相同的class文件不再同一个jar包,如果一个class文件已经被加载java虚拟机里了,那么后面的相同的class文件就不会被加载了。

但tomcat的加载运行机制与JAVA虚拟机的父类委托机制稍有不同。
下面来做详细叙述:
1、首先加载TOMCAT_HOME/lib目录下的jar包
2、然后加载TOMCAT_HOME/webapps/项目名/WEB-INF/lib的jar包
3、最后加载的是TOMCAT_HOME/webapps/项目名/WEB-INF/classes下的类文件
值得注意的关键是:tomcat按上述顺序依次加载资源,当后加载的资源与之前加载的资源相重时,后加载的资源会继续加载并将之前的资源覆盖。

Tomcat的具体内部细节及解读可以参考:

  • Apache Tomcat 9 (9.0.0.M26) - Documentation
  • 极客学院wiki - 《Tomcat 8 权威指南》
  • 《深入剖析Tomcat》

3.web.xml配置文件简介

servlet和JSP规范在发展阶段中出现了很多的web.xml配置版本,如3.0、3.1、4.0等,版本的变更会改变web.xml对应的配置代码。下图是来自Tomcat官网的Servlet和JSP规范规范与的Apache Tomcat版本之间的对应关系:

下面是各个版本的web.xml配置示例代码:

servlet 2.5 [Tomcat 6.0.x(archived)]

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   version="2.5">
   ...
</web-app>

servlet 3.0 [Tomcat 7.0.x]

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0" metadata-complete="true">
  ...
</web-app>

servlet 3.1 [Tomcat 8.0.x (superseded) & 8.5.x]

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1" metadata-complete="true">
  ...
</web-app>

servlet 4.0 [Tomcat 9.0.x]

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0" metadata-complete="true">
  ...
</web-app>

在tomcat目录${CATALINA_HOME}/conf下和web应用目录${CATALINA_HOME}/webapps/WebDemo(WebDemo为web应用名)下都有web.xml这个文件,但是内容不一样。

Tomcat在激活、加载、部署web应用时,会解析加载${CATALINA_HOME}/conf目录下所有web应用通用的web.xml,然后解析加载web应用目录中的WEB-INF/web.xml
其实根据他们的位置,我们就可以知道,conf/web.xml文件中的设定会应用于所有的web应用程序,而某些web应用程序的WEB-INF/web.xml中的设定只应用于该应用程序本身。

如果没有WEB-INF/web.xml文件,tomcat会输出找不到的消息,但仍然会部署并使用web应用程序,servlet规范的作者想要实现一种能迅速并简易设定新范围的方法,以用作测试,因此,这个web.xml并不是必要的,不过通常最好还是让每一个上线的web应用程序都有一个自己的WEB-INF/web.xml。

3.web.xml元素配置详解

web.xml文件加载顺序为:(与顺序无关)
ServletContext -> context-param -> listener -> filter -> servlet

web.xml中定义了以下元素:

<web-app> 
    <display-name></display-name>定义了WEB应用的名字 
    <description></description> 声明WEB应用的描述信息 
    
    <context-param></context-param> context-param元素声明应用范围内的初始化参数。 
    <filter></filter> 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。 
    <filter-mapping></filter-mapping> 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。 
    
    <listener></listener>servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。 
    
    <servlet></servlet> 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。 
    
    <servlet-mapping></servlet-mapping> 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName.
    
    但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。 
    
    <session-config></session-config> 如果某个会话在一定时间内未被访问,服务器可以抛弃它以节省内存。 可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值,或者可利用session-config元素制定缺省超时值。 
    
    <mime-mapping></mime-mapping>如果Web应用具有想到特殊的文件,希望能保证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。 
    
    <welcome-file-list></welcome-file-list> 指示服务器在收到引用一个目录名而不是文件名的URL时,使用哪个文件。 
    
    <error-page></error-page> 在返回特定HTTP状态代码时,或者特定类型的异常被抛出时,能够制定将要显示的页面。 
    
    <taglib></taglib> 对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置,而不用编辑使用这些文件的JSP页面。 
    
    <resource-env-ref></resource-env-ref>声明与资源相关的一个管理对象。 
    <resource-ref></resource-ref> 声明一个资源工厂使用的外部资源。 
    <security-constraint></security-constraint> 制定应该保护的URL。它与login-config元素联合使用 
    <login-config></login-config> 指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。 
    
    <security-role></security-role>给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。 
    
    <env-entry></env-entry>声明Web应用的环境项。 
    <ejb-ref></ejb-ref>声明一个EJB的主目录的引用。 
    <ejb-local-ref></ejb-local-ref>声明一个EJB的本地主目录的应用。 
</web-app> 

相应元素的配置:

  1. Web应用图标:指出IDE和GUI工具用来表示Web应用的大图标和小图标
    <icon> 
        <small-icon>/images/app_small.gif</small-icon> 
        <large-icon>/images/app_large.gif</large-icon> 
    </icon> 
  2. Web 应用名称:提供GUI工具可能会用来标记这个特定的Web应用的一个名称
    <display-name>Tomcat Example</display-name>
  3. Web 应用描述: 给出于此相关的说明性文本
    <disciption>Tomcat Example servlets and JSP pages.</disciption>
  4. 上下文参数:声明应用范围内的初始化参数。
    <context-param> 
        <param-name>ContextParameter</para-name> 
        <param-value>test</param-value> 
        <description>It is a test parameter.</description> 
    </context-param> 

    在servlet里面可以通过getServletContext().getInitParameter("context/param")得到。

  5. 过滤器配置:将一个名字与一个实现javaxs.servlet.Filter接口的类相关联。
    <filter> 
        <filter-name>setCharacterEncoding</filter-name> 
        <filter-class>com.myTest.setCharacterEncodingFilter</filter-class> 
        <init-param> 
            <param-name>encoding</param-name> 
            <param-value>GB2312</param-value> 
        </init-param> 
    </filter> 
    <filter-mapping> 
        <filter-name>setCharacterEncoding</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping>
  6. 监听器配置
    <listener> 
        <listerner-class>listener.SessionListener</listener-class> 
    </listener>
  7. Servlet配置
    基本配置

    <servlet> 
    <servlet-name>snoop</servlet-name> 
    <servlet-class>SnoopServlet</servlet-class> 
    </servlet> 
    <servlet-mapping> 
    <servlet-name>snoop</servlet-name> 
    <url-pattern>/snoop</url-pattern> 
    </servlet-mapping> 
    高级配置
       <servlet> 
           <servlet-name>snoop</servlet-name> 
           <servlet-class>SnoopServlet</servlet-class> 
           <init-param> 
               <param-name>foo</param-name> 
               <param-value>bar</param-value> 
           </init-param> 
           <run-as> 
               <description>Security role for anonymous access</description> 
               <role-name>tomcat</role-name> 
           </run-as> 
       </servlet> 
       <servlet-mapping> 
           <servlet-name>snoop</servlet-name> 
           <url-pattern>/snoop</url-pattern> 
       </servlet-mapping> 
    元素说明 
    
    <servlet></servlet> 用来声明一个servlet的数据,主要有以下子元素: 
    <servlet-name></servlet-name> 指定servlet的名称 
    <servlet-class></servlet-class> 指定servlet的类名称 
    <jsp-file></jsp-file> 指定web站台中的某个JSP网页的完整路径 
    <init-param></init-param> 用来定义参数,可有多个init-param。在servlet类中通过getInitParamenter(String name)方法访问初始化参数 
    <load-on-startup></load-on-startup>指定当Web应用启动时,装载Servlet的次序。 
    当值为正数或零时:Servlet容器先加载数值小的servlet,再依次加载其他数值大的servlet. 
    当值为负或未定义:Servlet容器将在Web客户首次访问这个servlet时加载它 
    <servlet-mapping></servlet-mapping> 用来定义servlet所对应的URL,包含两个子元素 
    <servlet-name></servlet-name> 指定servlet的名称 
    <url-pattern></url-pattern> 指定servlet所对应的URL 
  8. 会话超时配置(单位为分钟)
    <session-config> 
    <session-timeout>120</session-timeout> 
    </session-config> 
  9. MIME类型配置
    <mime-mapping> 
    <extension>htm</extension> 
    <mime-type>text/html</mime-type> 
    </mime-mapping> 
  10. 指定欢迎文件页配置
    <welcome-file-list> 
    <welcome-file>index.jsp</welcome-file> 
    <welcome-file>index.html</welcome-file> 
    <welcome-file>index.htm</welcome-file> 
    </welcome-file-list> 
  11. 配置错误页面
    方法1:通过错误码来配置error-page

    <error-page> 
    <error-code>404</error-code> 
    <location>/NotFound.jsp</location> 
    </error-page> 
    上面配置了当系统发生404错误时,跳转到错误处理页面NotFound.jsp。 
    方法2:通过异常的类型配置error-page 
    <error-page> 
    <exception-type>java.lang.NullException</exception-type> 
    <location>/error.jsp</location> 
    </error-page> 

    上面配置了当系统发生java.lang.NullException(即空指针异常)时,跳转到错误处理页面error.jsp

  12. TLD配置
    <taglib> 
    <taglib-uri>http://jakarta.apache.org/tomcat/debug-taglib</taglib-uri> 
    <taglib-location>/WEB-INF/jsp/debug-taglib.tld</taglib-location> 
    </taglib>
    如果MyEclipse一直在报错,应该把<taglib> 放到 <jsp-config>
    <jsp-config> 
    <taglib> 
        <taglib-uri>http://jakarta.apache.org/tomcat/debug-taglib</taglib-uri> 
        <taglib-location>/WEB-INF/pager-taglib.tld</taglib-location> 
    </taglib> 
    </jsp-config> 
  13. 资源管理对象配置
    <resource-env-ref> 
    <resource-env-ref-name>jms/StockQueue</resource-env-ref-name> 
    </resource-env-ref> 
  14. 资源工厂配置
    <resource-ref> 
    <res-ref-name>mail/Session</res-ref-name> 
    <res-type>javax.mail.Session</res-type> 
    <res-auth>Container</res-auth> 
    </resource-ref> 
    配置数据库连接池就可在此配置:
    <resource-ref> 
    <description>JNDI JDBC DataSource of shop</description> 
    <res-ref-name>jdbc/sample_db</res-ref-name> 
    <res-type>javax.sql.DataSource</res-type> 
    <res-auth>Container</res-auth> 
    </resource-ref> 
  15. 安全限制配置
    <security-constraint> 
    <display-name>Example Security Constraint</display-name> 
    <web-resource-collection> 
        <web-resource-name>Protected Area</web-resource-name> 
        <url-pattern>/jsp/security/protected/*</url-pattern> 
        <http-method>DELETE</http-method> 
        <http-method>GET</http-method> 
        <http-method>POST</http-method> 
        <http-method>PUT</http-method> 
    </web-resource-collection> 
    <auth-constraint> 
        <role-name>tomcat</role-name> 
        <role-name>role1</role-name> 
    </auth-constraint> 
    </security-constraint> 
  16. 登陆验证配置
    <login-config> 
    <auth-method>FORM</auth-method> 
    <realm-name>Example-Based Authentiation Area</realm-name> 
    <form-login-config> 
        <form-login-page>/jsp/security/protected/login.jsp</form-login-page> 
        <form-error-page>/jsp/security/protected/error.jsp</form-error-page> 
    </form-login-config> 
    </login-config> 
  17. 安全角色:security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。
    分别地声明角色可使高级IDE处理安全信息更为容易。

    <security-role> 
    <role-name>tomcat</role-name> 
    </security-role> 
  18. Web环境参数:env-entry元素声明Web应用的环境项
    <env-entry> 
    <env-entry-name>minExemptions</env-entry-name> 
    <env-entry-value>1</env-entry-value> 
    <env-entry-type>java.lang.Integer</env-entry-type> 
    </env-entry> 
  19. EJB 声明
    <ejb-ref> 
    <description>Example EJB reference</decription> 
    <ejb-ref-name>ejb/Account</ejb-ref-name> 
    <ejb-ref-type>Entity</ejb-ref-type> 
    <home>com.mycompany.mypackage.AccountHome</home> 
    <remote>com.mycompany.mypackage.Account</remote> 
    </ejb-ref> 
  20. 本地EJB声明
    <ejb-local-ref> 
    <description>Example Loacal EJB reference</decription> 
    <ejb-ref-name>ejb/ProcessOrder</ejb-ref-name> 
    <ejb-ref-type>Session</ejb-ref-type> 
    <local-home>com.mycompany.mypackage.ProcessOrderHome</local-home> 
    <local>com.mycompany.mypackage.ProcessOrder</local> 
    </ejb-local-ref> 
  21. 配置DWR
    <servlet> 
    <servlet-name>dwr-invoker</servlet-name> 
    <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> 
    </servlet> 
    <servlet-mapping> 
    <servlet-name>dwr-invoker</servlet-name> 
    <url-pattern>/dwr/*</url-pattern> 
    </servlet-mapping> 

4.spring项目中web.xml基础配置

配置基于servlet 3.1

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true">

    <display-name>demo</display-name>
    <description>demo</description>
    
    <!-- 在Spring框架中是如何解决从页面传来的字符串的编码问题的呢?  
    下面我们来看看Spring框架给我们提供过滤器CharacterEncodingFilter  
     这个过滤器就是针对于每次浏览器请求进行过滤的,然后再其之上添加了父类没有的功能即处理字符编码。  
      其中encoding用来设置编码格式,forceEncoding用来设置是否理会 request.getCharacterEncoding()方法,设置为true则强制覆盖之前的编码格式。-->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 项目中使用Spring 时,applicationContext.xml配置文件中并没有BeanFactory,要想在业务层中的class 文件中直接引用Spring容器管理的bean可通过以下方式-->
    <!--1、在web.xml配置监听器ContextLoaderListener-->
    <!--ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。  
    在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。  
    它的API说明  
    第一段说明ContextLoader可以由 ContextLoaderListener和ContextLoaderServlet生成。  
    如果查看ContextLoaderServlet的API,可以看到它也关联了ContextLoader这个类而且它实现了HttpServlet    这个接口  
    第二段,ContextLoader创建的是 XmlWebApplicationContext这样一个类,它实现的接口是WebApplicationContext->ConfigurableWebApplicationContext->ApplicationContext->  
    BeanFactory这样一来spring中的所有bean都由这个类来创建  
     IUploaddatafileManager uploadmanager = (IUploaddatafileManager)  
     ContextLoaderListener.getCurrentWebApplicationContext().getBean("uploadManager");-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--2、部署applicationContext的xml文件-->
    <!--如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml,  
    在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。  
    如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:  
    在<param-value> </param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔。  
    也可以这样applicationContext-*.xml采用通配符,比如这那个目录下有applicationContext-ibatis-base.xml,  
    applicationContext-action.xml,applicationContext-ibatis-dao.xml等文件,都会一同被载入。  
    在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>
    <!--如果你的DispatcherServlet拦截"/",为了实现REST风格,拦截了所有的请求,那么同时对*.js,*.jpg等静态文件的访问也就被拦截了。-->
    <!--方案一:激活Tomcat的defaultServlet来处理静态文件-->
    <!--要写在DispatcherServlet的前面, 让 defaultServlet先拦截请求,这样请求就不会进入Spring了,我想性能是最好的吧。-->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.css</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.swf</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.gif</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.jpg</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.png</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.js</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.xml</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.json</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.map</url-pattern>
    </servlet-mapping>
    <!--使用Spring MVC,配置DispatcherServlet是第一步。DispatcherServlet是一个Servlet,,所以可以配置多个DispatcherServlet-->
    <!--DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理。-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <!--在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml 的配置文件,生成文件中定义的bean。-->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--指明了配置文件的文件名,不使用默认配置文件名,而使用dispatcher-servlet.xml配置文件。-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--其中<param-value>**.xml</param-value> 这里可以使用多种写法-->
            <!--1、不写,使用默认值:/WEB-INF/<servlet-name>-servlet.xml-->
            <!--2、<param-value>/WEB-INF/classes/dispatcher-servlet.xml</param-value>-->
            <!--3、<param-value>classpath*:dispatcher-servlet.xml</param-value>-->
            <!--4、多个值用逗号分隔-->
            <param-value>classpath:spring/dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <!--是启动顺序,让这个Servlet随Servletp容器一起启动。-->
    </servlet>
    <servlet-mapping>
        <!--这个Servlet的名字是dispatcher,可以有多个DispatcherServlet,是通过名字来区分的。每一个DispatcherServlet有自己的WebApplicationContext上下文对象。同时保存的ServletContext中和Request对象中.-->
        <!--ApplicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了:P,Spring把Bean放在这个容器中,在需要的时候,用getBean方法取出-->
        <servlet-name>DispatcherServlet</servlet-name>
        <!--Servlet拦截匹配规则可以自已定义,当映射为@RequestMapping("/user/add")时,为例,拦截哪种URL合适?-->
        <!--1、拦截*.do、*.htm, 例如:/user/add.do,这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。-->
        <!--2、拦截/,例如:/user/add,可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。 -->
        <url-pattern>/</url-pattern>
        <!--会拦截URL中带“/”的请求。-->
    </servlet-mapping>
    <welcome-file-list>
        <!--指定欢迎页面-->
        <welcome-file>login.html</welcome-file>
    </welcome-file-list>
    <error-page>
        <!--当系统出现404错误,跳转到页面nopage.html-->
        <error-code>404</error-code>
        <location>/nopage.html</location>
    </error-page>
    <error-page>
        <!--当系统出现java.lang.NullPointerException,跳转到页面error.html-->
        <exception-type>java.lang.NullPointerException</exception-type>
        <location>/error.html</location>
    </error-page>
    <session-config>
        <!--会话超时配置,单位分钟-->
        <session-timeout>360</session-timeout>
    </session-config>
</web-app>

5.流行的web.xml零配置

servlet3.0+规范后,允许servlet,filter,listener不必声明在web.xml中,而是以硬编码的方式存在,实现容器的零配置。

spring框架提供了一些类如WebApplicationInitializer用来配置web.xml,这意味着我们可以舍弃web.xml,仅仅在主程序代码中进行配置。

spring的“约定大于配置”思想也体现在这里,用java注解来优化过去繁杂的xml文件配置,大大提高了开发者的编程速度和体验。

现在很流行的spring boot框架,只要几步就可以搭建一个java web项目,根本无需自己手动配置web.xml,框架已经为你提供了足够多的注解、接口及实现类,让我们能够专注于应用本身。

有关spring对web.xml配置的隐藏实现细节,这里就不详细展开了,欢迎关注我的下一篇博客。

6.参考资料

  • Apache Tomcat 9 (9.0.0.M26) - Documentation
  • 极客学院wiki - 《Tomcat 8 权威指南》
  • Tomcat启动时类加载顺序
  • Tomcat web.xml详解
  • Web启动过程及web.xml配置
  • tomcat下的web.xml和web工程下web.xml的区别
  • web.xml文件的作用及基本配置
  • web.xml结构
  • web.xml格式及其详解
  • java : maven web项目中的 web.xml
  • Spring MVC 配置文件 web.xml文件详解
  • 漫谈Spring的启动与初始化(一)
  • SpringMVC4零配置--web.xml

 

来源: https://segmentfault.com/a/1190000011404088

ajax使用error调试错误的方法

代码:$(document).ready(function() {

            jQuery("#clearCac").click(function() {
jQuery.ajax({
url: "/Handle/Do.aspx",
type: "post",
data: { id: '0' },
dataType: "json",
success: function(msg) {
alert(msg);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.status);
alert(XMLHttpRequest.readyState);
alert(textStatus);
},
complete: function(XMLHttpRequest, textStatus) {
this; // 调用本次AJAX请求时传递的options参数
}
});
});
});

 

一、error:function (XMLHttpRequest, textStatus, errorThrown)
{
}
(默认: 自动判断 (xml 或 html)) 请求失败时调用时间。参数有以下三个:XMLHttpRequest 对象、错误信息、(可选)捕获的错误对象。如果发生了错误,错误信息(第二个参数)除了得到null之外,还可能是"timeout", "error", "notmodified" 和 "parsererror"。

textStatus:

"timeout", "error", "notmodified" 和 "parsererror"。

二、error事件返回的第一个参数XMLHttpRequest有一些有用的信息:

XMLHttpRequest.readyState:

状态码

0 - (未初始化)还没有调用send()方法

1 - (载入)已调用send()方法,正在发送请求

2 - (载入完成)send()方法执行完成,已经接收到全部响应内容

3 - (交互)正在解析响应内容

4 - (完成)响应内容解析完成,可以在客户端调用了

三、data:"{}", data为空也一定要传"{}";不然返回的是xml格式的。并提示parsererror.

四、parsererror的异常和Header 类型也有关系。及编码header('Content-type: text/html; charset=utf8');

五、XMLHttpRequest.status:
1xx-信息提示
这些状态代码表示临时的响应。客户端在收到常规响应之前,应准备接收一个或多个1xx响应。
100-继续。
101-切换协议。
2xx-成功
这类状态代码表明服务器成功地接受了客户端请求。
200-确定。客户端请求已成功。
201-已创建。
202-已接受。
203-非权威性信息。
204-无内容。
205-重置内容。
206-部分内容。
3xx-重定向
客户端浏览器必须采取更多操作来实现请求。例如,浏览器可能不得不请求服务器上的不同的页面,或通过代理服务器重复该请求。
301-对象已永久移走,即永久重定向。
302-对象已临时移动。
304-未修改。
307-临时重定向。
4xx-客户端错误
发生错误,客户端似乎有问题。例如,客户端请求不存在的页面,客户端未提供有效的身份验证信息。400-错误的请求。
401-访问被拒绝。IIS定义了许多不同的401错误,它们指明更为具体的错误原因。这些具体的错误代码在浏览器中显示,但不在IIS日志中显示:
401.1-登录失败。
401.2-服务器配置导致登录失败。
401.3-由于ACL对资源的限制而未获得授权。
401.4-筛选器授权失败。
401.5-ISAPI/CGI应用程序授权失败。
401.7–访问被Web服务器上的URL授权策略拒绝。这个错误代码为IIS6.0所专用。
403-禁止访问:IIS定义了许多不同的403错误,它们指明更为具体的错误原因:
403.1-执行访问被禁止。
403.2-读访问被禁止。
403.3-写访问被禁止。
403.4-要求SSL。
403.5-要求SSL128。
403.6-IP地址被拒绝。
403.7-要求客户端证书。
403.8-站点访问被拒绝。
403.9-用户数过多。
403.10-配置无效。
403.11-密码更改。
403.12-拒绝访问映射表。
403.13-客户端证书被吊销。
403.14-拒绝目录列表。
403.15-超出客户端访问许可。
403.16-客户端证书不受信任或无效。
403.17-客户端证书已过期或尚未生效。
403.18-在当前的应用程序池中不能执行所请求的URL。这个错误代码为IIS6.0所专用。
403.19-不能为这个应用程序池中的客户端执行CGI。这个错误代码为IIS6.0所专用。
403.20-Passport登录失败。这个错误代码为IIS6.0所专用。
404-未找到。
404.0-(无)–没有找到文件或目录。
404.1-无法在所请求的端口上访问Web站点。
404.2-Web服务扩展锁定策略阻止本请求。
404.3-MIME映射策略阻止本请求。
405-用来访问本页面的HTTP谓词不被允许(方法不被允许)
406-客户端浏览器不接受所请求页面的MIME类型。
407-要求进行代理身份验证。
412-前提条件失败。
413–请求实体太大。
414-请求URI太长。
415–不支持的媒体类型。
416–所请求的范围无法满足。
417–执行失败。
423–锁定的错误。
5xx-服务器错误
服务器由于遇到错误而不能完成该请求。
500-内部服务器错误。
500.12-应用程序正忙于在Web服务器上重新启动。
500.13-Web服务器太忙。
500.15-不允许直接请求Global.asa。
500.16–UNC授权凭据不正确。这个错误代码为IIS6.0所专用。
500.18–URL授权存储不能打开。这个错误代码为IIS6.0所专用。
500.100-内部ASP错误。
501-页眉值指定了未实现的配置。
502-Web服务器用作网关或代理服务器时收到了无效响应。
502.1-CGI应用程序超时。
502.2-CGI应用程序出错。application.
503-服务不可用。这个错误代码为IIS6.0所专用。
504-网关超时。
505-HTTP版本不受支持。
FTP
1xx-肯定的初步答复
这些状态代码指示一项操作已经成功开始,但客户端希望在继续操作新命令前得到另一个答复。
110重新启动标记答复。
120服务已就绪,在nnn分钟后开始。
125数据连接已打开,正在开始传输。
150文件状态正常,准备打开数据连接。
2xx-肯定的完成答复
一项操作已经成功完成。客户端可以执行新命令。200命令确定。
202未执行命令,站点上的命令过多。
211系统状态,或系统帮助答复。
212目录状态。
213文件状态。
214帮助消息。
215NAME系统类型,其中,NAME是AssignedNumbers文档中所列的正式系统名称。
220服务就绪,可以执行新用户的请求。
221服务关闭控制连接。如果适当,请注销。
225数据连接打开,没有进行中的传输。
226关闭数据连接。请求的文件操作已成功(例如,传输文件或放弃文件)。
227进入被动模式(h1,h2,h3,h4,p1,p2)。
230用户已登录,继续进行。
250请求的文件操作正确,已完成。
257已创建“PATHNAME”。
3xx-肯定的中间答复
该命令已成功,但服务器需要更多来自客户端的信息以完成对请求的处理。331用户名正确,需要密码。
332需要登录帐户。
350请求的文件操作正在等待进一步的信息。
4xx-瞬态否定的完成答复
该命令不成功,但错误是暂时的。如果客户端重试命令,可能会执行成功。421服务不可用,正在关闭控制连接。如果服务确定它必须关闭,将向任何命令发送这一应答。
425无法打开数据连接。
426Connectionclosed;transferaborted.
450未执行请求的文件操作。文件不可用(例如,文件繁忙)。
451请求的操作异常终止:正在处理本地错误。
452未执行请求的操作。系统存储空间不够。
5xx-永久性否定的完成答复
该命令不成功,错误是永久性的。如果客户端重试命令,将再次出现同样的错误。500语法错误,命令无法识别。这可能包括诸如命令行太长之类的错误。
501在参数中有语法错误。
502未执行命令。
503错误的命令序列。
504未执行该参数的命令。
530未登录。
532存储文件需要帐户。
550未执行请求的操作。文件不可用(例如,未找到文件,没有访问权限)。
551请求的操作异常终止:未知的页面类型。
552请求的文件操作异常终止:超出存储分配(对于当前目录或数据集)。
553未执行请求的操作。不允许的文件名。
常见的FTP状态代码及其原因
150-FTP使用两个端口:21用于发送命令,20用于发送数据。状态代码150表示服务器准备在端口20上打开新连接,发送一些数据。
226-命令在端口20上打开数据连接以执行操作,如传输文件。该操作成功完成,数据连接已关闭。
230-客户端发送正确的密码后,显示该状态代码。它表示用户已成功登录。
331-客户端发送用户名后,显示该状态代码。无论所提供的用户名是否为系统中的有效帐户,都将显示该状态代码。
426-命令打开数据连接以执行操作,但该操作已被取消,数据连接已关闭。
530-该状态代码表示用户无法登录,因为用户名和密码组合无效。如果使用某个用户帐户登录,可能键入错误的用户名或密码,也可能选择只允许匿名访问。如果使用匿名帐户登录,IIS的配置可能拒绝匿名访问。
550-命令未被执行,因为指定的文件不可用。例如,要GET的文件并不存在,或试图将文件PUT到您没有写入权限的目录。

 

来源: https://www.cnblogs.com/Horsonce/p/7911519.html

从锦囊妙计想到的17–同步和异步

很久都没时间写这个系列的东西了, 今天感觉还得继续下去,因此在继续继续一下, 今天要谈的是什么是同步, 什么是异步, 为了说明这个问题,先讲个故事,来说明一下 同步和异步的重要性!

 

一。 听对了,说对了, 不等于都对了!

先说一下, 甲对乙说了一句话, 甲说对了, 乙也听对了, 然后乙就去按照甲的意思去办理了, 那么乙一定能办对吗?当然假设乙在办理事情的过程中没有任何失误等等。

其实结果是大家 未必像大家想象的!

不啰嗦了,说故事。

本人现在在北京居住了很多年, 一天吃饭时, 家里发出了一个奇怪的声音, 于是我就去找一下, 别是什么电器什么坏了前兆, 若是发现了, 好及时修理。

找了几个地方, 都没找到, 于是我就说, 哪里来的声音?  一旁的老婆说了, 是南京大陆分吹过大陆架发出的声音。

我听后就特别奇怪, 我在北京怎么能听到 远在地球最南边的南极的声音?  我真要成为 顺风耳不成? 奇怪呀奇怪!

我不解的说着, 怎么可能是南极的声音, 一边说着一边转转过身, 然后我明白了!

原来老婆在看手机, 手机视频里面播放南极科考站的视频, 声音从视频里面传出来的!

结论:

  • 说对了, 听对了, 不等于都 达成一致的理解了, 很可能都理解错误了
  • 若是说错误了, 那么无论是否 听对, 最后都是错误的
  • 若是 说对了 , 听错误了, 最后 也可能是错误的
  • 说对了, 听对了, 都理解对了, 但是最后做的时候错误了, 那么结果也是错误的!

上面列举的 当有一个说的人, 一个听的人时,  要把一件事情弄好的  一个情况。 可以看到各种情况下都是错误的。

若是人更多, 事情更多, 就更容易错误了!

 

二。错误有什么损害?

这个问题,错误的损害, 没办法直接说, 还是讲个故事

一天, 老张去菜市场去买肉, 老张来到 菜市场卖肉的地方, 前面还有几个人在买肉, 老张在后面等着, 前面的人也买肉,前面的人说这个堆是什么肉呀, 卖肉的人说是猪肉, 然后前面的人买了走了, 过了一会,轮到老张了(前面卖肉后, 卖肉整理了肉案子, 因此一部分肉更换了位置), 老张听到了前面的人说的是猪肉, 于是说来二斤这个肉, 卖肉的称了二斤肉给老张, 老张付了钱回了家, 结果回家老婆一看, 怎么卖了2斤羊肉?  家里没人吃羊肉!自然老张要挨一顿。。。

类似的故事大家可以直接编写自己的版本了,

例如, 本来要买个玩具猪, 但是由于理解错误, 最后买头猪的情况,是否可以编出来!

类似的很多很多!

类似的 生活中的事情很多, 很多, 错误了, 不是闹出笑话就是, 有经济损失, 或者伤害等等, 那么如何避免错误的发生呢?

这个问题后面给出一个不完美的解决办法, 因为没有真正完美的东西, 因此仅仅能给出一个不完美的解决办法来!

 

三。总结一下做事情的过程

总结这个过程, 说了很多, 也未必真正明白, 还是画个图来说明一下!

图1   一般工作过程

如上图, 我们让别人做一件事情时 , 只要包括:   发布人,   执行人

发布人,   发布任务, 或者安排事情, 安排工作, 发布命令, 等等, 你能理解的各种词汇都可以 , 总之明白这个意思就好

执行人,   执行任务, 或者做任务, 做事情, 做任务等等, 都可以。

 

通常的过程是:

过程1:

 

发布人=》1)要求任务 执行人 ,  执行一项任务=》2)执行人接收任务=》3)进行相关工作=》4)把工作成果或者工作结果给发布人=》5)发布人同意完成工作。

本过程可以参考图1中的一般工作过程, 描述的就是这个工作过程。

 

过程2:

这里有一些情况类似但是, 缺少最后的5), 情况如下:

发布人=》1)要求任务 执行人 ,  执行一项任务=》2)执行人接收任务=》3)进行相关工作=》4)把工作成果或者工作结果给发布人(完成了)

下面来讨论一下过程2

对于过程2,   任务的发布人,  不会知道 , 任务执行人是否做了, 更不可能知道  是否对了, 因为, 任务发布人不要求知道结果, 那么一切就靠   执行人自己了! 参考如下图

这个过程, 一般来说不是很好, 由于不知道结果情况, 因此最后的情况无法预计, 好的情况是 一切都很好, 坏的情况是, 什么事情都没做, 你还不知道。

当然这个流程2  , 唯一的好处是, 由于少了一个环节, 因此可能进行的比较快, 因此在非常不关心后果的情况下, 事宜采用他。

 

四。计算机中的任务调用

计算机中, 通常会把执行一定的功能的代码或者指令(就是我们前面说的锦囊)封装的一个大的袋子里面, 并且给这个大的袋子 起个名字, 通常这个大袋子就是函数(通俗的也可以教子函数, 子程序, 过程,方法的, 总之尽管略有差别, 但是大体上是一个东西)了。

有了这个函数, 就可以让别人 使用这个函数了!

函数的使用者(函数的调用者, 同任务的发布者类似), 调用函数(发布任务), 然后执行函数(执行者在执行函数),这个过程同上面的 做事情是极其相似的。

那么是否需要确保执行事情时, 或者在计算机里面确保函数调用执行结果正确, 是非常有必要的。

一个函数执行结果错误会造成重大问题, 很有可能的,  前面已经用多个故事说明 执行结果不正确带来的重大问题

下面是一个非常严重的计算机故障造成的问题:

http://www.sohu.com/a/130983799_670543

从上面 可以看到,  计算机故障引起的重大故障。

因此, 当你在你的程序中调用别的函数时, 一定要确认 那个函数返回的是否是正确的, 然后在根据结果决定后续的工作。  当然这个就能避免 不出问题吗?  前面也说了, 不能。

但是, 检查肯定比 不检查要好的得多。

计算机领域现实生活
函数使用者任务发布者
函数调用者任务发布者
函数执行者(cpu或者程序)工作者,执行人
函数工作、任务等

上面表格简单类比了  生活中的 做事情,同计算机中的调用的关系, 也说明了 调用不正确造成的 危害。

那么如何避免或者减少这些危害?

 

五。 通过明确的结果检后进行补偿减少损失

上面说了不检查结果或者, 发布任务后可能造成很多问题, 尽管他仅仅是个可能, 但是仍然是问题, 一旦发生就会有非常大的危害

如上图,

任务发布人, 发布了一个任务后, 等待执行者去执行, 等执行者执行完成后, 把结果给 任务发布人后, 有任务发布人   立即   检查结果,  并根据检查后结果进行后续工作。 例如经过了检查 执行结果符合原来预期, 然后执行正常后续的事情。  若是检查到 执行结果不符合原来的预期, 那么就要进行补偿的一些事情了, 通过补偿来减少问题的损失等

一般来说, 这种 有发布者自己 通知别人进行相关工作, 并且等待执行完成, 并且  立即  亲自  检查执行结果的调用方式, 叫做同步调用, 一般用Synchronous表示, 如下图

 

相对应的是 异步调用。 什么是异步调用, 主要就是 同步以外的其他调用是  异步调用, 这个不是个准确的定义,

上图是一个典型的异步调用。  请特别注意 图中所示, 发布完成任务后, 可以做别的事情, 然后执行完成后会把结果通过一定方式通知到发布者。 这个时候发布者未必有时间或者有机会处理结果, 需要等待一定时间后再进行结果的处理工作。

这个异步处理方式中,  若是需要工作方面的补偿或者重做, 那么可能有一定的麻烦, 主要是程序的运行条件已经发生变化了! 因此异步是有一定风险的。

 

六。 单线程和同步调用

什么是单线程, 什么是 单cpu?

程序的执行是通过cpu进行的, 没有cpu就没有计算机了, 我们手机中有cpu, 我们硬盘中也有控制器, 其实也是个cpu。

cpu执行指令时, 是按照程序自己指定的顺序进行的执行的, 当然有分支, 也有循环, 但若是按照cpu执行指令的顺序, 并且把每条执行的过程都串到一起, 就会形成一个线索。 这个线索就是一个线程。

一个计算机中若是有多个cpu就可以同时有多个执行线索。  程序可以让自己的程序,启动多个线索来执行不同的任务,这个就是多线程。

计算机中, 网卡, 硬盘等等, 里面都有可编程的芯片,这些芯片基本上可以认为是cpu,因此你电脑上别看仅仅一个cpu但是其实可能是暗含多个类似的cpu的

因此我们说单纯,或者纯粹的单线程就是不能让其他设备同这个线程有相同的工作情况。

如上图, 每个蓝色矩形  代表一个cpu的指令或者一条程序(一般一条程序代码会分解为若干条指令, 简化认为是一条指令)。

从上图由于只有一个线程或者只有一个cpu,因此同时仅仅能做一件事情(复杂情况例外,以后会讨论到)。因此在只有一个cpu是仅仅能做出一个同步的调用。

 

七。网卡或者io的单cpu情况

在计算机上通过网卡进行网络通信时, 可能采用下面的形式, 这个形式就不是同步的了

如上图 核心的事情是 发布者 在发布一条任务给网卡后, 自己可以继续进行后续的其他工作, 而网卡接收工作后, 自己可以不依赖于cpu,然后自己根据数据等进行自行的工作, 等工作完成后,在返回结果给 cpu(程序)

这个工作过程就是典型的  异步操作。

除了网卡, 磁盘io也可以有类似的情况。

特别注意, 网卡, 磁盘等都可以有同步的方式进行通信的, 这里特别举出的是 异步的。

 

同步和异步就到此, 后续有时间在慢慢补充。

 

相关文章

计算机介绍                                  从锦囊妙计想到的01

流程图(分支结构)介绍          从锦囊妙计想到的02

线程介绍                                     从锦囊妙计想到的03

循环结构介绍                             从锦囊妙计想到的05

流程线程总结                             从锦囊妙计想到的06

cpu和线程定义、开始               从锦囊妙计想到的07

分布式计算                                  从锦囊妙计想到的08

分布式中事件和计数                 从锦囊妙计想到的09

内容总结                                     从锦囊妙计想到的10

数据类型,变量简介                从锦囊妙计想到的11

函数和参数                                从锦囊妙计想到的12

用户交互与数据输入输出       从锦囊妙计想到的13

人机交互界面                            从锦囊妙计想到的15 

过程与对象                                从锦囊妙计想到的16

同步和异步                                从锦囊妙计想到的17

顺序打印                                    从锦囊妙计想到的18

数据输入输出                            从锦囊妙计想到的19

屏幕坐标和打印                        从锦囊妙计想到的20

java函数控制输出                     从锦囊妙计想到的21

逐步细化解决复杂问题           从锦囊妙计想到的22

java入门                                    从锦囊妙计想到的23

java复杂过程分析                   从锦囊妙计想到的25

中间辅助功能解决问题          从锦囊妙计想到的26

叠加操作输出复杂图形          从锦囊妙计想到的27

时间和空间                               从锦囊妙计想到的28

编写边测解决问题                  从锦囊妙计想到的29

让程序动起来                          从锦囊妙计想到的30

程序往复运动                           从锦囊妙计想到的31

Spring security框架原理

在SpringSide 3的官方文档中,说安全框架使用的是Spring Security 2.0。乍一看,吓了我一跳,以为Acegi这么快就被淘汰了呢。上搜索引擎一搜,发现原来Spring Security 2.0就是Acegi 2.0。悬着的心放下来了。虽然SpringSide 3中关于Acegi的配置文件看起来很不熟悉,但是读了Acegi 2.0的官方文档后,一切都释然了。
先来谈一谈Acegi的基础知识,Acegi的架构比较复杂,但是我希望我下面的只言片语能够把它说清楚。大家都知道,如果要对Web资源进行保护,最好的办法莫过于Filter,要想对方法调用进行保护,最好的办法莫过于AOP。Acegi对Web资源的保护,就是靠Filter实现的。如下图:

一般来说,我们的Filter都是配置在web.xml中,但是Acegi不一样,它在web.xml中配置的只是一个代理,而真正起作用的Filter是作为Bean配置在Spring中的。web.xml中的代理依次调用这些Bean,就实现了对Web资源的保护,同时这些Filter作为Bean被Spring管理,所以实现AOP也很简单,真的是一举两得啊。
Acegi中提供的Filter不少,有十多个,一个一个学起来比较复杂。但是对于我们Web开发者来说,常用的就那么几个,如下图中的被红圈圈标记出来的:

从上到下,它们实现的功能依次是1、制定必须为https连接;2、从Session中提取用户的认证信息;3、退出登录;4、登录;5、记住用户;6、所有的应用必须配置这个Filter。
一般来说,我们写Web应用只需要熟悉这几个Filter就可以了,如果不需要https连接,连第一个也不用熟悉。但是有人肯定会想,这些Filter怎么和我的数据库联系起来呢?不用着急,这些Filter并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器和决策管理器。如下图:

对于这两种管理器,那也是不需要我们写代码的,Acegi也提供了现成的类。那么大家又奇怪了:又是现成的,那怎么和我的数据库关联起来呢?别着急,其实这两个管理器自己也不做事,认证管理器把任务交给了Provider,而决策管理器则把任务交给了Voter,如下图:

现在我要告诉你们,这里的Provider和Voter也是不需要我们写代码的。不要崩溃,快到目标了。Acegi提供了多个Provider的实现类,如果我们想用数据库来储存用户的认证数据,那么我们就选择DaoAuthenticationProvider。对于Voter,我们一般选择RoleVoter就够用了,它会根据我们配置文件中的设置来决定是否允许某一个用户访问制定的Web资源。
而DaoAuthenticationProvider也是不直接操作数据库的,它把任务委托给了UserDetailService,如下图:

而我们要做的,就是实现这个UserDetailService。图画得不好,大家不要见笑,但是说了这么多总算是引出了我们开发中的关键,那就是我们要实现自己的UserDetailService,它就是连接我们的数据库和Acegi的桥梁。UserDetailService的要求也很简单,只需要一个返回org.springframework.security.userdetails.User对象的loadUserByUsername(String userName)方法。因此,怎么设计数据库都可以,不管我们是用一个表还是两个表还是三个表,也不管我们是用户-授权,还是用户-角色-授权,还是用户-用户组-角色-授权,这些具体的东西Acegi统统不关心,它只关心返回的那个User对象,至于怎么从数据库中读取数据,那就是我们自己的事了。
反过来再看看上面的过程,我们发现,即使我们要做的只是实现自己的UserDetailService类,但是我们不得不在Spring中配置那一大堆的Bean,包括几个Filter,几个Manager,几个Provider和Voter,而这些配置往往都是重复的无谓的。好在Acegi 2.0也认识到了这个问题,所以,它设计了一个<http>标签,让Acegi的配置得到了简化。下面是SpringSide 3中的配置的截图,大家可以看看:


下图是官方文章中的传统Filter设置和<http>元素之间的对应关系:


下面的代码是SpringSide 3中实现UserDetailService的范例,在SpringSide 3的范例中,白衣使用了三个表User、Role、Authority。但是Acegi不关心你用了几个表,它只关心UserDetails对象。而决定用户能否访问指定Web资源的,是RoleVoter类,无需任何修改它可以工作得很好,唯一的缺点是它只认ROLE_前缀,所以搞得白衣的Authority看起来都象角色,不伦不类。

package personal.youxia.service.security;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.DataAccessException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import personal.youxia.entity.user.Authority;
import personal.youxia.entity.user.Role;
import personal.youxia.entity.user.User;
import personal.youxia.service.user.UserManager;
/**
 * 实现SpringSecurity的UserDetailsService接口,获取用户Detail信息.
 * 
 * @author calvin
 */
public class UserDetailServiceImpl implements UserDetailsService {
    private UserManager userManager;
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
        User user = userManager.getUserByLoginName(userName);
        if (user == null)
            throw new UsernameNotFoundException(userName + " 不存在");
        List<GrantedAuthority> authsList = new ArrayList<GrantedAuthority>();
        for (Role role : user.getRoles()) {
            for (Authority authority : role.getAuths()) {
                authsList.add(new GrantedAuthorityImpl(authority.getName()));
            }
        }
        // 目前在MultiDatabaseExample的User类中没有enabled, accountNonExpired,credentialsNonExpired, accountNonLocked等属性
        // 暂时全部设为true,在需要时才添加这些属性.
        org.springframework.security.userdetails.User userdetail = new org.springframework.security.userdetails.User(
                user.getLoginName(), user.getPassword(), true, true, true, true, authsList
                        .toArray(new GrantedAuthority[authsList.size()]));
        return userdetail;
    }
    @Required
    public void setUserManager(UserManager userManager) {
        this.userManager = userManager;
    }
}

最后再来说说这个命名的问题,我对Authentication和Authority这两个单词比较反感,两个原因,一是因为它们太生僻了,二是因为它们长得太像了,明明一个是认证,一个是授权,意思相差很远,外貌却如此相似,确实很烦人。如果让我来选择,我喜欢Privilege这个单词,在我刚使用MySQL的时候就跟它很熟了,所以在我的项目中,我可能会用Privilege来代替Authority。如果我们只使用User-Role两级关系,使用RoleVoter默认的ROLE_前缀当然没有关系,如果是像白衣这样是用三层关系,最好还是把这个前缀改一改,以免混淆。

转自: http://www.blogjava.net/youxia/archive/2008/12/07/244883.html