?? ? 传统的 JAVA 企业级应用开发中, Servlet 的配置一直是一件令开发人员头痛的事情。文章使用 Annotation 技术实现的 @Servlet 注解,可以让 Servlet 的配置不再繁杂,只需要简单的标注就可以实现 Servlet 的配置功能,能极大地简化开发人员的工作,也让配置文件更加干净简洁。
??? JAVA EE 规范中规定,在使用 Servlet 开发应用系统时,需要在 web.xml 文件中为每一个 Servlet 配置相关信息。一个大型应用系统中, Servlet 的数量是很庞大的,如果要为每一个 Servlet 都配置上述信息,这无疑是开发人员的噩梦。现在主流的 MVC 框架(如 Struts 、 WebWork ),它们在一定程度上解决了这样繁琐的配置,但这些框架本身就引入了大量的 XML 文件配置。 JDK1. 5 版本之后, JAVA 语言提供了一种叫做 Annotation(JSR250) 的新数据类型,中文译为注解或标注,它的出现为铺天盖地的 XML 配置文件提供了一个完美的解决方案,让 JAVA EE 开发更加方便快速,也更加干净了。
?? ? ?@Servlet 注解的实现
?????? 上面简要地介绍了注解的定义声明与使用方式,了解到了注解在后台需要一个处理器才能起作用。下面我们定义一个 @Servlet 注解,将 Servlet 在 web.xml 中的配置信息使用注解来表示,使用注解后,只需要在相应 Servlet 类的前面使用类似 @Servlet(“/servlet/Login”) 注解就可以达到和上述 web.xml 文件中配置信息一样的目的。注解 @Servlet 中的属性值 ” /servlet/Login” 表示了 web.xml 配置文件中 <servlet-mapping> 元素的子元素 <url-pattern> 里的值。通过这样的注解能简化在 XML 文件中配置 Servlet 信息,整个配置文件将会非常简洁干净,开发人员的工作也将大大减少。
1 注解的定义
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Servlet { String value(); }
?
@Target 注解的属性值表明了 @Servlet 注解只能用于类或接口定义声明的前面, @Servlet 注解有一个必填的属性 value 。调用方式为: @Servlet(value=“/xxxx”) ,因语法规定如果属性名为 value 且只填 value 属性值时,可以省略 value 属性名,即也可以写作:@Servlet(“/xxxx”) 。
2 使用注解后程序流程
在 web.xml 文件中配置:
<listener> <listener-class>edu.xju.AnnotationListener</listener-class> </listener> <context-param> <!-- 扫描包及其子包, 如果有多个参数,以逗号分隔 --> <param-name>basePackage</param-name> <param-value>edu.xju.web</param-value> </context-param> <servlet> <servlet-name>ActionServlet</servlet-name> <servlet-class>edu.xju.action.ActionServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ActionServlet</servlet-name> <url-pattern>*.svt</url-pattern> </servlet-mapping>
?
?观察 web.xml 文件,会发现系统实现了一个名为 AnnotationListener 的监听器和一个名为 ActionServlet 的 Servlet ,二者为 @Servlet 注解提供了后台处理器 。应用服务器(如 Tomcat )启动时加载 web.xml 文件,并会立即执行 AnnotationListener 监听器的 contextInitialized 方法,在此方法中,根据 <context-param> 元素指定的 basepackage 参数,读取这些包 (package) 及其子包下的所有类,使用 JAVA 中的反射 API 对这些类进行分析。如果类前使用了 @Servlet 注解,如 @Servlet(“/servlet/LoginServlet”) ,则以 Key 为 ”/servlet/LoginServlet” , Value 为此 Servlet 类的一个实例对象,放入一个 Map 结构中。解析这些包下的所有的类之后,将得到的此 Map 结构对象置入 ServletContext 对象中。到此,完成了注解的解析,服务器成功运行起来了。
3 服务器将以下图的方式与客户端进行交互 ?
?? ? 根据 web.xml 文件的配置,所有后缀名为 .svt 请求,都交由 ActionServlet 处理。 ActionServlet 本身也是一个 Servlet ,它也可以使用 @Servlet 注解方法进行配置,但将其配置在 web.xml 文件中具有更好的可读性与可维护性。 ActionServlet 继承于 HttpServlet ,重写了 HttpServlet 类中 protected 修饰的 service 方法。
在此 service 方法的实现代码中,从 HttpServletRequest 请求对象中得到请求的方式类型( GET/POST )和请求的 URI 。如有请求, http://localhost/testProject/servlet/loginServlet.svt ,此时请求方法类型为 POST , URI 值为 /testProject/servlet/loginServlet.svt 。从 ServletConext 对象中获取到在监听器中保存的 Map 结构,根据 URI 获得一个 Key=” /servlet/loginServlet” ,从 Map 结构中根据此 Key 得到 Value ,此时 Value 就是要请求调用的那个 Servlet 对象实例。再根据前面得到的请求方法类型,能决定调用此 Servlet 对象实例的 doGet 或 doPost 方法。最终客户端发生的后缀为 . svt 请求,经由 ActionServlet 对请求对象( HttpServletRequest )的分析,从而调用相应某 Servlet 的 doGet 或 doPost 方法,完成了一次客户端请求到服务器响应的过程。
4 注解的扩展
?????? 通过 @Servlet 注解, Servlet 不用再在 web.xml 文件中进行繁冗的注册。考虑性能因素,使用单例设计模式,让每个 Servlet 类在内存中只有一个实例,这些类的实例对象在服务器启动时在监听器中就已经产生了。
通过 @Servlet 注解,我们还可以减少 Servlet 类文件的数量,并且此时不要求类继承或实现 Servlet 任何的类或接口,一个普通的类即可。不过此时注解 @Servlet 不应放在类前面,而应该让它能放在方法前面,需要将 @Servlet 定义时的 @Target(ElementType.TYPE) 改成 @Target(ElementType.METHOD) 。加 @Servlet 注解的方法,名称可以是任意的,但参数必须有 HttpServletRequest 类型及 HttpServletResponse 类型。这是因为我们的方法要对请求和响应做相应的处理。
JAVA 提供的注解,为简化开发人员的工作带来了无限的可能。在 JAVA EE 5 版本中,提供了一些注解的使用来解化开发,而像 Struts2.x 、 Hibernate 、 Spring 等框架中,也开始加了大量的注解,使用这些注解可以极大地简化了配置文件的编写,基本上实现了“零配置”。注解还在面向切面编程( AOP )等领域都有很好的应用。在 JAVA 企业级应用开发中,注解技术将越来越广泛地被使用。
?