spring

注释语法越来越多的被业界所使用,并且注释配置相对于 XML 配置具有很多的优势:它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作。注释和 Java 代码位于一个文件中,而 XML 配置采用独立的配置文件,大多数配置信息在程序开发完成后都不会调整,如果配置信息和 Java 代码放在一起,有助于增强程序的内聚性。而采用独立的 XML 配置文件,程序员在编写一个功能时,往往需要在程序文件和配置文件中不停切换,这种思维上的不连贯会降低开发效率。因此在很多情况下,注释配置比 XML 配置更受欢迎,注释配置有进一步流行的趋势。Spring 2.5 的一大增强就是引入了很多注释类,现在您已经可以使用注释配置完成大部分 XML 配置的功能。

在使用注释配置之前,先来回顾一下传统上是如何配置 Bean 并完成 Bean 之间依赖关系的建立。

代码清单1 Foo.java Foo对象有一个String类型的name属性.

package com.tony.test;

public class Foo {

private String name;

public String toStirng(){

return "Foo Name is :" + this.name;

}

Set和get方法

}

代码清单2 Bar.java Bar对象有一个String类型的add属性.

package com.tony.test;

public class Bar {

private String add;

public String toStirng(){

return "Bar Add is :" + this.add;

}

Set和get方法

}

代码清单3 Main.java Main对象有两个属性分别是Foo和Bar

package com.tony.test;

public class Main {

private Foo foo;

private Bar bar;

public String toString(){

return "Main : [" + this.foo.toStirng() +" "+ this.bar.toStirng() + "]";

}

Set和get方法

}

代码清单4 配置文件spring-config-beans.xml

<bean id="main" class="com.tony.test.Main">

<property name="foo" ref="foo"></property>

<property name="bar" ref="bar"></property>

</bean>

<bean id="foo" class="com.tony.test.Foo">

<property name="name" value="Foo"></property>

</bean>

<bean id="bar" class="com.tony.test.Bar">

<property name="add" value="Bar"></property>

</bean>

代码清单 5 Test.java Test类用于初始化Spring容器并获得main对象

package com.tony.test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.

ClassPathXmlApplicationContext;

public class Test {

public static void main(String[] args) {

String[] locations = {"spring-config-beans.xml"};

ApplicationContext ctx = new ClassPathXmlApplicationContext(locations);

Main main = (Main) ctx.getBean("main");

System.out.println(main);

}

}

运行Test类控制台输出以下信息:

Main : [Foo Name is :Foo Bar Add is :Bar]

这说明Spring已经完成了Bean的创建和装配工作。

1)使用 @Autowired 注释

Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。下面我们来看一下使用 @Autowired 进行成员变量自动注入的代码:

代码清单6使用 @Autowired 注释的 Main.java,此时可以将Main.java类中的set和get方法删除

package com.tony.test;

import org.springframework.beans.factory.annotation.Autowired;

public class Main {

@Autowired

private Foo foo;

@Autowired

private Bar bar;

public String toString(){

return "Main : [" + this.foo.toStirng() +" "+ this.bar.toStirng() + "]";

}

}

Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析,所以要让 @Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean

代码清单 7 修改配置文件

<!--该BeanPostProcessor将自动对标注@Autowired的Bean进行注入-->

<bean class="org.springframework.beans.factory.annotation.

AutowiredAnnotationBeanPostProcessor"/>

<!—此时移除mainBean的属性注入信息-->

<bean id="main" class="com.tony.test.Main"></bean>

<bean id="foo" class="com.tony.test.Foo">

<property name="name" value="Foo"></property>

</bean>

<bean id="bar" class="com.tony.test.Bar">

<property name="add" value="Bar"></property>

</bean>

当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认按类型匹配)的 Bean,并将其注入。

2)使用@Qualifier 注释

Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称,这样就不会产生注入错误了,请看下面代码清单:

代码清单8 修改Main.java类中的foo属性注释增加注释@Qualifier("foo1")

public class Main {

@Autowired

@Qualifier("foo1")

private Foo foo;

@Autowired

private Bar bar;

public String toString(){

return "Main : [" + this.foo.toStirng() +" "+ this.bar.toStirng() + "]";

}

}

代码清单9 在配置文件中增加id为foo2 Bean定义

<bean class="org.springframework.beans.factory.annotation.

AutowiredAnnotationBeanPostProcessor"/>

<bean id="main" class="com.tony.test.Main"></bean>

<bean id="foo1" class="com.tony.test.Foo">

<property name="name" value="Foo1"></property>

</bean>

<bean id="foo2" class="com.tony.test.Foo">

<property name="name" value="Foo2"></property>

</bean>

<bean id="bar" class="com.tony.test.Bar">

<property name="add" value="Bar"></property>

</bean>

运行Test.java控制台输出如下信息:

Main : [Foo Name is :Foo1 Bar Add is :Bar]

证明Spring容器成功将foo1注入进main类中

3)使用 <context:annotation-config/> 简化配置

Spring 2.1 添加了一个新的 context 的 Schema 命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。而我们前面所介绍的 AutowiredAnnotationBeanPostProcessor 就是处理这些注释元数据的处理器。但是直接在 Spring 配置文件中定义这些 Bean 显得比较笨拙。Spring 为我们提供了一种方便的注册这些 BeanPostProcessor 的方式,这就是 <context:annotation-config/>。请看下面的代码清单:

代码清单10

<context:annotation-config/>

<bean id="main" class="com.tony.test.Main"></bean>

<bean id="foo1" class="com.tony.test.Foo">

<property name="name" value="Foo1"></property>

</bean>

<bean id="foo2" class="com.tony.test.Foo">

<property name="name" value="Foo2"></property>

</bean>

<bean id="bar" class="com.tony.test.Bar">

<property name="add" value="Bar"></property>

</bean>

代码清单中将

<bean class="org.springframework.beans.factory.annotation.

AutowiredAnnotationBeanPostProcessor"/>

替换成为<context:annotation-config/>

<context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。

4) 使用 @Component

虽然我们可以通过 @Autowired在 Bean 类中使用自动注入功能,但是 Bean 还是在 XML 文件中通过 <bean> 进行定义,也就是说,在 XML 配置文件中定义 Bean,通过 @Autowired 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean,从 XML 配置文件中完全移除 Bean 定义的配置呢?答案是肯定的,我们通过 Spring 2.5 提供的 @Component 注释就可以达到这个目标。请看下面的代码清单:

代码清单11 Foo.java

@Component

public class Foo {

private String name = "Foo's name.";

public String toStirng(){

return "Foo Name is :" + this.name;

}

}

在类的开始位置使用@Component注释,标明此类是一个Bean

代码清单12 Main.java

@Component("main")

public class Main {

@Autowired

private Foo foo;

@Autowired

private Bar bar;

……

@Component 有一个可选的入参,用于指定 Bean 的名称,在 Main 中,我们就将 Bean 名称定义为“main”。在使用 @Component 注释后,Spring 容器必须启用类扫描机制以启用注释驱动 Bean 定义和注释驱动 Bean 的自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展,提供了这一功能。

代码清单13 Spring配置文件中只保留以下配置信息

<context:component-scan base-package="com.tony.test"/>

这里,所有通过 <bean> 元素定义 Bean 的配置内容已经被移除,仅需要添加一行 <context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。<context:component-scan/> 的 base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。

8.4.2 Spring2.5基于注解驱动的MVC

Spring 2.5 也为 Spring MVC 引入了注释驱动功能。现在我们无须让 Controller 继承任何接口,无需在 XML 配置文件中定义请求和 Controller 的映射关系,仅仅使用注释就可以让一个 POJO 具有 Controller 的绝大部分功能 —— Spring MVC 框架的易用性得到了进一步的增强。

1) 基于注解的Controller

由于 Spring MVC 的 Controller 必须事先是一个 Bean,所以 @Controller 注解是不可缺少的。请看下面的代码清单

代码清单1

@Controller //将这个类标注为Controller

public class FooController {

@Autowired

private FooService fooService;

@RequestMapping("/list.do") //URL请求映射

public String[] list() {

String[] list = fooService.getAll();

System.out.println(list);

return list;

}

@RequestMapping("/del.do") //URL请求映射

public void del(HttpServletRequest request, HttpServletResponse response) {

fooService.doDel(request.getParameter("id"));

}

}

在代码清单1中我们通过@Controller注释将FooController.java 标注为一个控制器,而不需继承或者实现任何类和接口,就使FooController.java拥有了控制器的功能。代码清单1中使用了两个链接分别访问了不同的方法,在实际应用中我们也许有另外一种需求一个控制器只接受一个URL请求,而控制器中不同的方法来处理URL请求中携带的不同的参数,请看下面的代码清单。

2) 一个 Controller 对应一个 URL,由请求参数决定请求处理方法

代码清单2

@Controller

@RequestMapping("/doFoo.do")// 指定控制器对应URL请求

public class FooController {

@Autowired

private FooService fooService;

//list方法对应URL /doFoo.do?mode=list

@RequestMapping(params = "mode=list")

public String[] list() {

String[] list = fooService.getAll();

System.out.println(list);

return list;

}

//del方法对应URL /doFoo.do?mode=del

@RequestMapping(params = "mode=del")

public void del(HttpServletRequest request,

HttpServletResponse response) {

fooService.doDel(request.getParameter("id"));

}

}

代码清单2中满足了针对不同粒度程序设计的需要。我们还可以让请求处理方法处理特定的 HTTP 请求如POST类型的,请看下面的代码清单。

3) 让请求处理方法处理特定的 HTTP 请求方法

代码清单3

@Controller

@RequestMapping("/doFoo.do")// 指定控制器对应URL请求

public class FooController {

//只针对POST请求

@RequestMapping(params = "mode=submit",

method = RequestMethod.POST)

public String submit(HttpServletRequest request,

HttpServletResponse response){

System.out.println("调用 submit 方法.");

return "success";

}

}

方法submit只处理类型为POST的URL请求

4) 处理方法入参绑定 URL 参数

代码清单4

@Controller

@RequestMapping("/doFoo.do")// 指定控制器对应URL请求

public class FooController {

@Autowired

private FooService fooService;

//del方法对应URL /doFoo.do?mode=del&id=10

@RequestMapping(params = "mode=del")

public String del(int id) {

fooService.doDel(id);

return "success";

}

}

当我们发送 /doFoo.do?mode=del&id=10 的 URL 请求时,

Spring 不但让 del() 方法处理这个请求,而且还将 id 请求参数在类型转换后绑定到 del() 方法的 id 入参上。而 del() 方法的返回类型是 String,它将被解析为逻辑视图的名称。也就是说 Spring 在如何给处理方法入参自动赋值以及如何将处理方法返回值转化为 ModelAndView 中的过程中存在一套潜在的规则,不熟悉这个规则就不可能很好地开发基于注解的请求处理方法,因此了解这个潜在规则无疑成为理解 Spring MVC 框架基于注解功能的核心问题。代码清单4还可以写成下面这种形式

代码清单5

@Controller

@RequestMapping("/doFoo.do")// 指定控制器对应URL请求

public class FooController {

@Autowired

private FooService fooService;

//del方法对应URL /doFoo.do?mode=del&id=10

@RequestMapping(params = "mode=del")

public String del(@RequestParam("id")int id) {

fooService.doDel(id);

return "success";

}

}

代码清单5中对 del() 请求处理方法的 id 入参标注了 @RequestParam("id") 注释,所以它将和 id 的 URL 参数绑定。

第一章 Spring的MVC框架

6.1 Spring MVC概要

Spring的web框架是围绕DispatcherServlet来进行设计的。DispatcherServlet的作用是将请求分发到不同的处理器。Spring的web框架包括可配置的处理器(handler)映射、视图(view)解析、本地化(local)解析、主题(theme)解析以及对上传文件解析。处理器是对Controller接口的实现,该接口仅仅定义了ModelAndView handleRequest(request, response)方法。你可以通过实现这个接口来生成自己的控制器(也可以称之为处理器),但是从Spring提供的一系列控制器继承会更省事,比如AbstractController、AbstractCommandController和SimpleFormController。注意,你需要选择正确的基类:如果你没有表单,你就不需要一个FormController。这是和Structs的一个主要区别。Spring的视图解析相当灵活。一个控制器甚至可以直接向response输出一个视图(此时控制器返回ModelAndView的值必须是null)。在一般的情况下,一个ModelAndView实例包含一个视图名字和一个类型为Map的model,一个model是一些以bean的名字为key,以bean对象(可以是命令或form,也可以是其他的JavaBean)为value的名值对。对视图名称的解析处理也是高度可配置的,可以通过bean的名字、属性文件或者自定义的ViewResolver实现来进行解析。实际上基于Map的model(也就是MVC中的M))是高度抽象的,适用于各种表现层技术。也就是说,任何表现层都可以直接和Spring集成,无论是JSP、Velocity还是其它表现层技术。Map model可以被转换成合适的格式,比如JSP request attribute或者Velocity template model。

Spring Web MVC框架提供了大量独特的功能,包括:

1) 清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command object)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、视图解析器(view resolver)等等。每一个角色都可以由一个专门的对象来实现。

2)强大而直接的配置方式:将框架类和应用类都作为JavaBean配置,支持在一个context中引用其他context的中JavaBean,例如,在web控制器中对业务对象和验证器(validator)的引用。

3) 可适配、非侵入的controller:你可以根据不同的应用场景,选择合适的控制器子类(simple型、command型、form型、wizard型、multi-action型或者自定义),而不是从单一控制器(比如Action/ActionForm)继承。

4)可重用的业务代码:你可以使用现有的业务对象作为命令或表单对象,而不需要在类似ActionForm的子类中重复它们的定义。

5)可定制的绑定(binding) 和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保存错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。

6) 可定制的handler mapping和view resolution:Spring提供从最简单的的URL映射,到复杂的、专用的定制策略。与某些MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。灵活。

7) 灵活的model转换: 在Springweb框架中,使用基于Map的名/值对来达到轻易地与各种视图技术的集成。

8) 可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。

9)Spring Bean的生命周期可以被限制在当前的HTTP Request或者HTTP Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Sping MVC使用的WebApplicationContext容器。

6.2 将请求映射到控制器

和其它web框架一样,Spring的web框架是一个请求驱动的web框架,其设计围绕一个中心的servlet进行,它能将请求分发给控制器,并提供其它功能帮助web应用开发。然而,Spring的DispatcherServlet所做的不仅仅是这些,它和Spring的IoC容器完全集成在一起,从而允许你使用Spring的其它功能。

代码清单1

<servlet>

<servlet-name>dispatcher</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring-config-mvc.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>dispatcher</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

代码清单1中我们定义了DispatcherServlet取名dispatcher并且将所有以.do结尾的请求都交由dispatcher处理,并且为他指定了名为spring-config-mvc.xml 的Spring配置文件。如果我们没有指定配置文件的名称Spring会在web应用的WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml的配置文件如dispatcher-servlet.xml

代码清单2

<bean id="viewResolver" class="org.springframework.web.servlet.view.

InternalResourceViewResolver">

<property name="prefix" value="/WEB-INF/jsp/" />

<property name="suffix" value=".jsp" />

</bean>

<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.

SimpleUrlHandlerMapping">

<property name="mappings">

<props>

<prop key="/helloworld.do"> helloworldController</prop>

</props>

</property>

</bean>

<bean id=" helloworldController "

class="com.tony.web.controller.HelloworldController"/>

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.mvc.Controller;

public class HelloworldController implements Controller {

@Override

public ModelAndView handleRequest(HttpServletRequest request,

HttpServletResponse response) throws Exception {

//向HttpServletRequest中加入一个名为message值为HelloWorld的对象

request.setAttribute("message", "HelloWorld");

//返回一个ModelAndView对象通过viewResolver的处理

//页面跳转至/WEB-INF/jsp/helloworld.jsp

return new ModelAndView("helloworld");

}

}

代码清单1中配置 InternalResourceViewResolver,它是 jsp 渲染模板的处理器。如果你告诉 InternalResourceViewResolver 处理一个名为 helloworld的模板时,它会渲染 /WEB-INF/jsp/helloworld.jsp 文件。把 jsp 文件放到 /WEB-INF/jsp/ 目录下是被鼓励的,这样可以防止用户不经过 Controller 直接访问 jsp 文件从而出错。配置 SimpleUrlHandlerMapping, 在上面的配置文件中,/ helloworld.do 的请求将被 helloworldController处理。 "/ helloworld.do"和"helloworldController" 是变量,你可以更改。但是你注意到了吗, hello.do 以 .do 作为后缀名。如果这里(本文的条件下)你不使用.do 作为后缀名, 就没有程序来处理这个请求了。 因为 DispatcherServlet 将收到的请求转交给 SimpleUrlHandlerMapping, DispatcherServlet 收不到的请求,SimpleUrlHandlerMapping 当然也收不到了。我们还定义了一个名为HelloworldController的控制器来处理/helloworld.do的请求。大多数Web应用都会遇到需要填写表单的页面,当表单提交成功后,表单的数据被传送到Web服务器中处理,遇到这种情况Spring为我们提供了一个简单的类SimpleFormController。

代码清单2

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.mvc.SimpleFormController;

public class HelloworldController extends SimpleFormController{

public HelloworldController(){

//设置表单对象类型

this.setCommandClass(User.class);

}

@Override

protected void doSubmitAction(Object command) throws Exception {

User user = (User)command;

//将User对象存入数据库

}

}

代码清单2中我们将HelloworldController类继承SimpleFormController并且实现了doSubmitAction方法,在构造方法中我们设置了表单对象的类型,当表单提交后Spring会自动调用doSubmitAction方法将表单对象装配好作为入参传递进来,我们将其类型转后就可以对其操作了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值