spring mvc 2

接上篇:

上篇的末尾提到了contextConfigLocation,DispatchServlet初始化的参数配置,这个是spring启动时加载文件时所配置的参数使用的,比如我们的applicationContext.xml ,[servlet-name]-servlet.xml这些文件如果不是放在spring要求的默认的位置的时候,用contextConfigLocation来指定文件的位置,如:

	<context-param>
	  	<param-name>contextConfigLocation</param-name>
	  	<param-value>classpath*:applicationContext-*.xml</param-value>
	</context-param>

这些字符串的参数代表的配置文件,被解析成为spring的上下文的实例,如果这些参数定义多次的话,最新定义的位置则会被使用。

还有另外两个参数:contextClass,namespace,我按照自己的方式把它们从官方文档上翻译过来:

contextClass实现了WebApplicationContext接口,作为核心servlet的一个上下文使用,默认情况下是:xmlWebApplicationContext,

namespace则是指WebApplicationContext的上下文,默认情况下是[servlet-name]-servlet.,这正是代表了[servlet-name]-servlet.xml 的前缀。


注解Controller

直接给出一个例子的写法 :
@Controller
public class HelloWorldController {

    @RequestMapping("/helloWorld")
    public String helloWorld(Model model) {
        model.addAttribute("message", "Hello World!");
        return "helloWorld";
    }
}

上面的代码中:@Controller是属于类层级别的注解,spring中所有的组件都可以使用@Component来进行注解,代表属于spring中的一个bean组件,而@Controller你可以认为是spring中@Component的一种特殊形式,你无需实现接口和继承一些基类。

spring所有的注解都可以被自动的探测到它们所在的具体位置,然后将它们实例化为spring的组件之一,对于spring mvc中的注解如:@Controller,@RequestMapping等这些注解,都可以自动检测得到,只需要在[servlet-name]-servlet.xml中加上:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.easy.web"/>
    <mvc:annotation-driven/>
    <!-- ... -->

</beans>

这样,所有被注解的类和方法都可以被找到并在spring中将bean进行注册和定义,其中的<mvc:annotation-driven/>必须要加上,因为我们在使用springmvc的时候它初始化了好多事情。


@RequestMapping

先上文档上的一个例子:
@Controller
@RequestMapping("/appointments")
public class AppointmentsController {

    private final AppointmentBook appointmentBook;

    @Autowired
    public AppointmentsController(AppointmentBook appointmentBook) {
        this.appointmentBook = appointmentBook;
    }

    @RequestMapping(method = RequestMethod.GET)
    public Map<String, Appointment> get() {
        return appointmentBook.getAppointmentsForToday();
    }

    @RequestMapping(value="/{day}", method = RequestMethod.GET)
    public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
        return appointmentBook.getAppointmentsForDay(day);
    }

    @RequestMapping(value="/new", method = RequestMethod.GET)
    public AppointmentForm getNewForm() {
        return new AppointmentForm();
    }

    @RequestMapping(method = RequestMethod.POST)
    public String add(@Valid AppointmentForm appointment, BindingResult result) {
        if (result.hasErrors()) {
            return "appointments/new";
        }
        appointmentBook.addAppointment(appointment);
        return "redirect:/appointments";
    }
}

从上边的代码中,我们可以看到,@RequestMapping在好多个地方出现,大类可以分为:类层级和方法级别,首先用@Controller来标识这是一个控制器,然后在类层级给出了一个@RequestMapping("/appointments")代表,所有的对这个控制器内所有方法请求都必须带有"/appoinments"的前缀。

方法内部的则对@RequestMapping进行了细化,分别给出了用于接收HTTP中的GET和POST请求的两种方式,对get()方法,我们可以看到,所有的/appointment的GET请求都会请求到这个方法上来,add()方法有类似的细化,getNewForm()的访问路径为:"/appointment/new",方式为:GET,而对于getForDay()这个方法而言,有些不同之处,这里用到了URI 的模版。

事实上在类层级别的@RequestMappin 是没有必要的,给出来,可以清晰的表明各个请求都是属于同一个模块,有点命名空间的意思,如果没有的话,所有的路径都是使用绝对路径,也没有什么影响。

URI-Template

URI模版可以方便的使用在@RequestMapping,在@RequestMapping注解的路径里加上一些变量,类似于struts2中的*.*通配符的配置,这里给出一个测试的例子:
@Controller
public class TreeGridController {

	@RequestMapping("/bug")
	public void treegrid(Bug bug) {
		System.out.println(bug.getDesc());
	}
	
	@RequestMapping("/find/{userId}")
	public void findUser(@PathVariable String userId)  {
		System.out.println(userId);
	}
}

上面代码中第二个方法就是就是URI模版的方法,这里需要指出@ReqeustMapping中的变量一定要和findUser()中方法参数的名称一样,如果不一样的话,这里如果你用@PathVariable会报错,在前端我们用find/kitty.do进行调用的时候,后边打印出的结果就是:

kitty

经过测试,@PathVariable查可以省略不写的,不过为了能够显示的看出我们是在用URI-模版,最好还是加上,而且一个方法里面可以含有多个变量,如:

@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
  Owner owner = ownerService.findOwner(ownerId);
  Pet pet = owner.getPet(petId);
  model.addAttribute("pet", pet);
  return "displayPet";
}

上边的是docs里的,可以看出带有两个变量的URI,事实上道理清楚,不管带几个变量,写法都是一成不变的。

可是形如:/find/spring-web-4.0.jar这种形式的的URI该怎么写呢 ? 其实官方也提供了写法,就昨正则表达式进行结合,如下:
@RequestMapping("/find/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
  public void handle(@PathVariable String version, @PathVariable String extension) {
    // ...
  }
}
这样就将要映射的方法进行完美的注解了,其中symbolicName部分是指有小写字母组成的,"+"号代表字母出现一次或者多次,连接一个“-”,再连接数字,这样就会映射所有这种情况的Controller。

我们当然也可以用*号来匹配Controller,比如:
/find/*.do

这样就匹配find下所有的Controller,spring当然也支持${}点位符来解决路径映射的问题,通常用PropertyPlaceholderConfigurer这个类,这个类可以对当前本地的变量及系统环境变量进行提取使用。

定义@ReqeustMapping的处理方法

在Controller中的方法我们可以随便定义,但是方法的参数支持有:
HttpServletReqeust、HttpServeltResponse、HttpSession,等等,有一些注解是将参数进行转换成方法的参数的,如@RequestParam,这个注解默认是必须的,在spring mvc中,如果方法的参数不是字符串的时候,则转换调协的调用是自动进行的。


spring 拦截器

spring中也有类似于struts2中的拦截器,供我们对请求未到达控制指定控制器之前,对request进行必要的处理, 一般spring mvc中的拦截器需要实现一个接口:HandlerInterceptor,实现这个接口有三个方法:preHandle(),postHandle(),afterCompletion(),从单词的表面意义我们就可以已经知道这几个方法执行的时间,其中preHandle()的返回值是一个boolean类型的,false表示不会继承执行请求链,true才会继续。


我们要对所有的请求加上这个拦截器时,需要进行如下配置:
<beans>
    <bean id="handlerMapping"
          class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="interceptors">
            <list>
                <ref bean="officeHoursInterceptor"/>
            </list>
        </property>
    </bean>

    <bean id="myInterceptor"
          class="com.easyui.MyInterCeptor">        
    </bean>
<beans>
java类:spring给出一个简单实现的HandlerInterceptorAdapter,用它写出我们的实现类:
package com.easyui.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class MyInterceptor extends HandlerInterceptorAdapter {

	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		String username = request.getParameter("username");
		if(username == null || username.equals("")) {
			response.sendRedirect("error.jsp");
			return false;
		}
		return super.preHandle(request, response, handler);
	}
}

这里如果用户名为空则跳转页面,不去继续执行请求链,如果用上边的配置,则会对所有的请求都会进行拦截,如果你想要缩小一下拦截器应用的范围,这个时候需要用到MVC namespace或者MVC java config。

关于@enableWebMvc还不是特别清楚,用它来完全实现java方式来管理bean的方式,和用xml来管理是两种情况回头找例子再补上,这里只给出用于xml的时候配置的拦截器,在xml中的配置应该是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

	<context:component-scan base-package="com.easyui.web" />
	<mvc:annotation-driven />
	<mvc:interceptors>
	    <mvc:interceptor>
	        <mvc:mapping path="/find/*"/>
	        <bean class="com.easyui.interceptor.MyInterceptor" />
	    </mvc:interceptor>
	</mvc:interceptors>
</beans>

xml 配置文件中说明,我们对系统只配置了一个拦截器,就是自己实现的那一个,而且我们拦截Controller的格式是:/find/*,对于其它格式的我们并没有进行拦截。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值