Spring Web MVC(三)之注解

spring web mvc 基于注解的优化

  我写的注解是按照spring web的部件分类写的,这样的话比较方便查看,大家感觉有用的话可以分享个别人,希望对对更多的人有帮助。毕竟零基础开始学这块是感觉是比较乱,这里写的都比较简单,关于这几个部件的详细介绍在我写的spring web mvc 中有介绍,不懂得可以去看下。

注解

DispatcherServlet

  •   DispatcherServlet 应用的其实就是一个“前端控制器”的设计模式

  •   DispatcherServlet其实就是个 Servlet (它继承自 HttpServlet 基类) ,同样也需要在你web应用的** web.xml 配置文件下声明**。你需要在 web.xml 文件中把你希
    望 DispatcherServlet 处理的请求映射到对应的URL上去。

  配置DispatcherServlet就是标准的Java EE Servlet配置;下面的代码就展示了对 DispatcherServlet 和路径映射的声明:

<web-app>
	<servlet>
		<servlet-name>example</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>example</servlet-name>
		<url-pattern>/example/*</url-pattern>
	</servlet-mapping>
</web-app>

  在上面的例子中,所有路径以 /example 开头的请求都会被名字为 example 的 DispatcherServlet 处理。

  下面是一段这种基于代码配置的例子,它与上面定义的 web.xml 配置文件是等效的。

public class MyWebApplicationInitializer implements 	WebApplicationInitializer {
		@Override
		public void onStartup(ServletContext container) {
			ServletRegistration.Dynamic registration = container.addServlet("dispatcher",new DispatcherServlet());
			registration.setLoadOnStartup(1);
			registration.addMapping("/example/*");
	}
}

  WebApplicationInitializer 是Spring MVC提供的一个接口,它会查找你所有基于代码的配置,并应用它们来初始化Servlet 3版本以上的web容器。它有一个抽象的实现 AbstractDispatcherServletInitializer ,用以简化 DispatcherServlet 的注册工作:你只需要指定其servlet映射(mapping) 即可。

控制器(Controller)

  控制器作为应用程序逻辑的处理入口,它会负责去调用你已经实现的一些服务。通常,一个控制器会接收并解析用户的请求,然后把它转换成一个模型交给视图,由视图渲染出页面最终呈现给用户。

  你可以在你的控制器实现上添加 **@RequestMapping **、 @RequestParam@ModelAttribute 等注解。注解特性既支持基于Servlet的MVC,也可支持基于Portlet的MVC。通过此种方式实现的控制器既无需继承某个特定的基类,也无需实现某些特定的接口。

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

  @Controller 注解和 @RequestMapping 注解支持多样的方法名和方法签名。在上面这个例子中,方法接受一个 Model 类型的参数并返回一个字符串 String 类型的视图名。但事实上,方法所支持的参数和返回值有非常多的选择。

使用@Controller注解定义一个控制器

  @Controller 注解表明了一个类是作为控制器的角色而存在的。Spring不要求你去继承任何控制器基类,也不要求你去实现Servlet的那套API。

  @Controller 注解可以认为是被标注类的原型(stereotype) ,表明了这个类所承担的角色。分派器(DispatcherServlet ) 会扫描所有注解了 @Controller 的类,检测其中通过 @RequestMapping 注解配置的方法 。

  你也可以不使用 @Controller 注解而显式地去定义被注解的bean,这点通过标准的Spring bean的定义方式,在dispather的上下文属性下配置即可做到。但是 @Controller 原型是可以被框架自动检测的,Spring支持classpath路径下组件类的自动检测,以及对已定义bean的自动注册

 在配置中加入组件扫描的配置代码来开启框架对注解控制器的自动检测

<?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="org.web"/>
	<!-- base-package的值为所在包的名称  -->
</beans>

@RequestMapping注解映射请求路径

  使用 @RequestMapping 注解来将请求URL,如 /appointments 等,映射到整个类上或某个特定的处理器方法上。一般来说,类级别的注解负责将一个特定(或符合某种模式) 的请求路径映射到一个控制器上,同时通过方法级别的注解来细化映射,即根据特定的HTTP请求方法(“GET”“POST”方法等) 、HTTP请求中是否携带特定参数等条件,将请求映射到匹配的方法上。

用 @RequestMapping 注解:

  • @RequestMapping(path = "/new", method = RequestMethod.GET)

  • @RequestMapping(path = "/new", method = RequestMethod.POST)

注解的表单控制器的例子
  注解@RequestMapping被用于映射如“/editPet.do”这样的URL到一个完整的类或者一个特定的处理方法。 典型的,顶层的注解映射一个特定的请求路径(或者路径模式)到一个表单控制器,另外的方法一级的注解可以缩小这个主要映射的范围,包括对于一个特定的HTTP请求方法(“GET/POST”)或者特定的HTTP请求参数。

@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {

	private final Clinic clinic;

	@Autowired
	public EditPetForm(Clinic clinic) {
		this.clinic = clinic;
	}

	@ModelAttribute("types")
	public Collection<PetType> populatePetTypes() {
		return this.clinic.getPetTypes();
	}

	@RequestMapping(method = RequestMethod.GET)
	public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
		Pet pet = this.clinic.loadPet(petId);
		model.addAttribute("pet", pet);
		return "petForm";
	}

	@RequestMapping(method = RequestMethod.POST)
	public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, 
			SessionStatus status) {
		new PetValidator().validate(pet, result);
		if (result.hasErrors()) {
			return "petForm";
		}
		else {
			this.clinic.storePet(pet);
			status.setComplete();
			return "redirect:owner.do?ownerId=" + pet.getOwner().getId();
		}
	}

}


  使用@RequestMapping注解的处理器方法允许具有非常灵活的外观。 它们可以拥有很多类型的参数,比如
  请求和/或响应对象(Servlet API或者Portlet API)。
  会话对象(Servlet API或者Portlet API):不管是HttpSession还是PortletSession。

支持的方法返回类型

  • ModelAndView
  • 对象其中model隐含填充了命令对象,以及注解了 @ModelAttribute 字段
    的存取器被调用所返回的值。

向页面传值的几种方法

  • 向页面传值的第一种方式:
  • 通过ModelAndView。
    将处理方法的返回值类型设置为ModelAndView。
@RequestMapping("/login4.do")

	public ModelAndView login4(User user){
		System.out.println("HelloController的"+ "login4方法...");
		System.out.println(user.getUsername() + " " + user.getPwd());
		/*
		 * ModelAndView(String viewName,Map data);
		 * viewName:视图名。
		 * data:处理结果。
		 */
		//将处理结果封装成一个Map对象。
		Map<String,Object> data = new HashMap<String,Object>();
		//相当于执行了request.setAttribute
		data.put("username", user.getUsername());
		data.put("user", user);
		ModelAndView mav = new ModelAndView("index",data);
		return mav;
	}
  • 向页面传值的第二种方式:
  • 通过ModelMap对象。
    要将ModelMap对象作为方法的入参。
	@RequestMapping("/login5.do")
	public String login5(User user,ModelMap mm){
		System.out.println("HelloController的"+ "login5方法...");
		System.out.println(user.getUsername() + " " + user.getPwd());
		//将处理结果添加到ModelMap。
		//相当于request.setAttribute
		mm.addAttribute("user",	user);
		return "index";
	}
  • 向页面传值的第三种方式:
  • 使用request。
	@RequestMapping("/login6.do")
	public String login6(HttpServletRequest request){
		System.out.println("login6方法...");
		String username = request.getParameter("username");
		System.out.println(username);
		request.setAttribute("username", username);
		return "index";
	}
  • 向页面传值的第四种方式:
  • 使用session。
	@RequestMapping("/login7.do")
	
	public String login7(User user,HttpSession session){
		System.out.println("login7方法...");
		System.out.println(user.getUsername());
		session.setAttribute("user", user);
		return "index";
	}

  • String 对象,其值会被解析成一个逻辑视图名。其中,model将默认填充了命令对象以及注解了 @ModelAttribute 字段的存取器被调用所返回的值。handler方法也可以增加一个 Model 类型的方法参数来增强model

下面为读取请求参数值的几种方式

  • 读取请求参数值的第一种方式:
  • 在处理方法里面,添加request作为 方法的入参即可。
    前端控制器会将request对象作为参数传进来。
	@RequestMapping("/login.do")
	public String login(HttpServletRequest request){
		System.out.println("HelloController的"+ "login方法...");
		String username = request.getParameter("username");
		String pwd = request.getParameter("pwd");
		System.out.println("username:" + username + " pwd:" + pwd);
		return "index";
	}
	
  • 读取请求参数值的第二种方式:
  • 将处理方法的入参与请求参数名保持一致。
    注:如果不一致,可以使用@RequestParam(请求参数名)。
	@RequestMapping("/login2.do")
	public String login2(String username,@RequestParam("pwd") String pwd1){
		System.out.println("HelloController的"+ "login2方法...");
		System.out.println("username:" + username + " pwd:" + pwd1);
		return "index";
	}
  • 读取请求参数值的第三种方式:
  • 将请求参数封装成一个javabean。
	@RequestMapping("/login3.do")
	
	public String login3(User user){
		System.out.println("HelloController的"+ "login3方法...");
		System.out.println("username:"+ user.getUsername() + " pwd:" + user.getPwd());
		return "index";
	}

重定向

  • 重定向的第一种方式:
  • 如果处理方法的返回值是String,
    则在重定向地址前添加"redirect:"作为前缀。
@RequestMapping("/login8.do")

	public String login8(User user){
		System.out.println("login8方法...");
		System.out.println(user.getUsername());
		return "redirect:toIndex.do";
	}
  • 重定向的第二种方式:
  • 如果处理方法的返回值是ModelAndView。
	@RequestMapping("/login9.do")
	
	public ModelAndView login9(User user){
		RedirectView rv = 
				new RedirectView("toIndex.do");
		ModelAndView mav = 
				new ModelAndView(rv);
		return mav;
	}
  • Model 对象
    其中视图名称默认由 RequestToViewNameTranslator 决定,model隐含填充了命令对象以及注解了 @ModelAttribute 字段的存取器被调用所返回的值

  • Map 对象
    用于暴露model,其中视图名称默认由 RequestToViewNameTranslator 决定,model隐含填充了命令对象以及注解了 @ModelAttribute 字段的存取器被调用所返回的值

  • View 对象

  • 其中model隐含填充了命令对象,以及注解了 @ModelAttribute 字段的存取器被调用所返回的值。handler方法也可以增加一个 Model 类型的方法参数来增强model

其他就不说了主要讲的就这几种。

使用@RequestParam绑定请求参数到方法参数

@RequestParam注解是用于在控制器中绑定请求参数到方法参数

@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {

    // ...

	@RequestMapping(method = RequestMethod.GET)
	public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
		Pet pet = this.clinic.loadPet(petId);
		model.addAttribute("pet", pet);
		return "petForm";
	}

    // ...

  使用这个注解的参数默认是必需的,但是可以把@RequestParam的required属性置为false从而让这个参数可选(例如,@RequestParam(value="id", required="false"))。

使用@ModelAttribute提供一个从模型到数据的链接

当作为一个方法参数时,@ModelAttribute用于映射一个模型属性到特定的注解的方法参数
这是控制器获得持有表单数据的对象引用的方法。另外,这个参数也可以被声明为特定类型的表单支持对象,而不是一般的java.lang.Object,这就增加了类型安全性。

使用@SessionAttributes指定存储在会话中的属性

类型级别的@SessionAttributes注解使用一个特定的句柄声明会话属性。 这通常会列出模型属性的名称,这些属性应被透明的保存在会话或者对话存储中,用于在后续的请求之间作为表单支持beans。

@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
	public class EditPetForm {
    // ...
	}

使用@RequestBody注解映射请求体
  方法参数中的 @RequestBody 注解暗示了方法参数应该被绑定了HTTP请求体的值

@RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer)throws IOException {
	writer.write(body);
}

  请求体到方法参数的转换是由 HttpMessageConverter 完成的。 HttpMessageConverter 负责将HTTP请求信息转换成对象,以及将对象转换回一个HTTP响应体。

定义@RequestMapping注解的处理方法

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
	<property name="messageConverters">
		<util:list id="beanList">
			<ref bean="stringHttpMessageConverter"/>
			<ref bean="marshallingHttpMessageConverter"/>
		</util:list>
	</property
</bean>
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>

<bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
	<property name="marshaller" ref="castorMarshaller"/>
	<property name="unmarshaller" ref="castorMarshaller"/>
</bean>

<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>

  注解了 @RequestBody 的方法参数还可以被 @Valid 注解,这样框架会使用已配置的 Validator 实例来对该参数进行验证。

使用@ResponseBody注解映射响应体

  @ResponseBody 注解与 @RequestBody 注解类似。 @ResponseBody 注解可被应用于方法上,标志该方法的返回值(更正,原文是return type,看起来应该是返回值) 应该被直接写回到HTTP响应体中去(而不会被被放置到Model中或被解释为一个视图名) 。
  @ResponseBody可以用来返回json对象的

@RequestMapping(path = "/something", method = RequestMethod.PUT)
@ResponseBody
public String helloWorld() {
	return "Hello World"
}

使用@RestController注解创建REST控制器

  当今让控制器实现一个REST API是非常常见的,这种场景下控制器只需要提供JSON、XML或其他自定义的媒体类型内容即可。你不需要在每个 @RequestMapping 方法上都增加一个 @ResponseBody 注解,更简明的做法是,给你的控制器加上一@RestController 的注解。

  @RestController 是一个原生内置的注解,它结合了 @ResponseBody @Controller 注解的功能。不仅如此,它也让你的控制器更表义,而且在框架未来的发布版本中,它也可能承载更多的意义。

处理器映射(Handler Mappings)

  用户需要在web应用的上下文中定义一个或多个的 HandlerMapping bean,用以将进入容器的web请求映射到合适的处理器方法上。允许在控制器上添加注解后,通常你就不必这么做了,因为RequestMappingHandlerMapping 类会自动查找所有注解了 @RequestMapping 的 @Controller 控制器bean。

配置一个拦截器的方法:

<beans>
	<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
		<property name="interceptors">
			<bean class="example.MyInterceptor"/>
		</property>
	</bean>
<beans>

使用HandlerInterceptor拦截请求

  Spring的处理器映射机制包含了处理器拦截器。拦截器在你需要为特定类型的请求应用一些功能时可能很有用,比如,检查用户身份等
处理器映射处理过程配置的拦截器,必须实现 org.springframework.web.servlet 包下的HandlerInterceptor 接口。这个接口定义了三个方法:

  •    preHandle(..) ,它在处理器实际执行之前会被执行;
       
  •    postHandle(..) ,它在处理器执行 完毕 以后被执行;
       
  •    afterCompletion(..) ,它在 整个请求处理完成 之后被执行。
       
      这三个方法为各种类型的前处理和后处理需求提供了足够的灵活性。

  preHandle(..) 方法返回一个boolean值。你可以通过这个方法来决定是否继续执行处理链中的部件。当方法返回 true 时,处理器链会继续执行;若方法返回 false ,DispatcherServlet 即认为拦截器自身已经完成了对请求的处理(比如说,已经渲染了一个合适的视图) ,那么其余的拦截器以及执行链中的其他处理器就不会再被执行了。

  拦截器可以通过 interceptors 属性来配置,该选项在所有继承了 AbstractHandlerMapping 的处理器映射类 HandlerMapping 都提供了配置的接口。

<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="officeHoursInterceptor" class="samples.TimeBasedAccessInterceptor">
		<property name="openingTime" value="9"/>
		<property name="closingTime" value="18"/>
	</bean>
<beans>

具体怎么实现拦截器在这里就不写了

视图解析

  Spring提供了一些视图解析器,它们让你能够在浏览器中渲染模型,并支持你自由选用适合的视图技术而不必与框架绑定到一起。Spring原生支持JSP视图技术、Velocity模板技术和XSLT视图等。

  有两个接口在Spring处理视图相关事宜时至关重要,分别是视图解析器接口 ViewResolver 和视图接口本身 View 。

  • 视图解析器 ViewResolver 负责处理视图名与实际视图之间的映射关系。

  • 视图接口 View 负责准备请求,并将请求的渲染交给某种具体的视图技术实现。

使用ViewResolver接口解析视图

  Spring MVC中所有控制器的处理器方法都必须返回一个逻辑视图的名字,无论是显式返回(比如返回一个 String 、 View 或者 ModelAndView ) 还是隐式返回(比如基于约定的返回) 。Spring中的视图由一个视图名标识,并由视图解析器来渲染。

假设这里使用的是JSP视图技术

<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
	<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
	<property name="prefix" value="/WEB-INF/jsp/"/>
	<property name="suffix" value=".jsp"/>
</bean>

  若返回一个 test 逻辑视图名,那么该视图解析器会将请求转发到 RequestDispatcher ,后者会将请求交给 /WEB-INF/jsp/test.jsp 视图去渲染。

  如果需要在应用中使用多种不同的视图技术,你可以使用ResourceBundleViewResolver :

<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
	<property name="basename" value="views"/>
	<property name="defaultParentView" value="parentView"/>
</bean>

  ResourceBundleViewResolver 会检索由bundle根路径下所配置的 ResourceBundle ,对于每个视图而言,其视图类由 [viewname].(class) 属性的值指定,其视图url由 [viewname].url 属性的值指定。

  关于注解主要的就是这些大家可以参考参考

原文地址:https://www.cnblogs.com/yangliguo/p/7470685.html