SpringMVC_learn01

跟随SpringMVC的请求

SpringMVC !!!等有时间了自己画一个符合描述的图,图来源于网络

  1. DispatcherServlet(前端控制器)会通过查询处理器映射将请求交给指定的SpringMVC控制器
  2. 处理器映射通过请求所携带的URL信息进行决策,确定请求的下一站
  3. 确定了控制器之后,DispatcherServlet会将请求交给指定的控制器,请求卸下负载等待控制器的处理,控制器会将具体的业务逻辑交给M层去实现
  4. 在控制器处理完成之后,会产生一些用于响应的信息,这些信息被称为模型,不能只将原始信息数据返回给用户,所以还会发送一个视图(视图名称),将模型视图打包发给DispatcherServlet,在这里使用视图名可以保证控制器不与具体的视图相耦合
  5. 因为从控制器传来的只是一个视图名,DispatcherServlet会使用视图解析器匹配为一个特定的视图
  6. DispatcherServlet交付模型数据,实现视图
  7. 视图使用模型视图输出,输出通过响应对象传递给客户端

1. 搭建SpringMVC

用javaConfig初始化DispatcherServlet,并加载Spring的应用上下文,去扩展抽象类AbstractAnnotionConfigDispatcherServletInitialiaer,实现抽象类的三个方法,前两个方法用来加载上下文配置文件,第三个方法配置DispatcherServlet的映射路径

package config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpittrWebAppInitializer extends 
AbstractAnnotationConfigDispatcherServletInitializer {//扩展该抽象类将自动的配置DispatcherServlet和Spring上下文

	protected Class<?>[] getRootConfigClasses() {
	//指定由ContextLoaderListen加载的上下文的配置类,这些bean通常是驱动应用后端的中间层和数据层组件(除了springWeb组件的bean)
		return new Class<?>[] {RootConfig.class};
	}

	protected Class<?>[] getServletConfigClasses() {
	//指定由DispatcherServlet加载的上下文的配置类,DispatcherServlet上下文加载包含web组件的bean,包括控制器、视图解析器、处理器映射
		return new Class<?>[]{WebConfig.class};
	}

	protected String[] getServletMappings() {
	//配置DispatcherServlet的映射路径
		return new String[]{"/"};
	}

}

2. 启用SpringMVC

  • 通过配置WebConfig来启动SpringMVC
    • 通过注解@EnableWebMvc启动SpringMVC的注解驱动
    • 启用组件扫描
    • 创建视图解析器bean
    • 配置静态资源的处理???还没有
package config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc//注解启动SpringMVC的注解驱动
@ComponentScan("spitter.web")//启动组件扫描,怎样去控制仅仅扫描web组件????????
public class WebConfig {
	@Bean
	public ViewResolver viewResolver() {//配置jsp视图解析器
		InternalResourceViewResolver resolver=new InternalResourceViewResolver();
		resolver.setPrefix("/WEB-INF/views/");//设置视图名称前缀
		resolver.setSuffix(".jsp");//设置视图名称后缀
		resolver.setExposeContextBeansAsAttributes(true);//
		return resolver;
	}
}
  • 配置RootConfig
    • s
package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.context.annotation.ComponentScan.Filter;

@Configuration
@ComponentScan(basePackages= {"spitter.web"},
			   excludeFilters= {
					   @Filter(type=FilterType.ANNOTATION,value=EnableWebMvc.class)
			   })//启动组件扫描,新加的内容的意思??????
public class RootConfig {

}

3. 编写基本的控制器

  • 配置
    • 使用@Controller声明组件为控制器
    • 使用@RequestMapping声明所要处理的请求(通过范围,请求方式等确定)
package spitter_web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;//method的GET取值

@Controller
public class HomeController {

	@RequestMapping(value="/",method=RequestMethod.GET)//配置要控制的请求,通过定义value和method
	public String home() {
		return "home";
	}
}

4. 进行测试

package chapter_05;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import spitter_web.HomeController;

public class HomeControllerTest {

  @Test
  public void testHomePage() throws Exception {
    HomeController controller=new HomeController();//实例化控制器
    MockMvc mockMvc = standaloneSetup(controller).build();//创建MockMvc实例
    mockMvc.perform(get("/")).andExpect(view().name("home"));//发起对"/"的get请求,并断言结果视图的名称为home
  }

}

5. 传递模型数据到视图中(通过Model)

  1. 定义数据访问的Repository,为了实现解耦以及避免陷入数据库的实现细节上,这里利用接口实现
package spittr_data;

import java.util.List;

import entity.Spittle;

public interface SpittleRepository {
	public List<Spittle> findSpittles();
}
  1. 实现实体类Spittle
package entity;

import java.util.Date;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Spittle {

  private final Long id;
  private final String message;
  private final Date time;
  private Double latitude;
  private Double longitude;

  public Spittle(String message, Date time) {
    this(null, message, time, null, null);
  }
  
  public Spittle(Long id, String message, Date time, Double longitude, Double latitude) {
    this.id = id;
    this.message = message;
    this.time = time;
    this.longitude = longitude;
    this.latitude = latitude;
  }

  public long getId() {
    return id;
  }

  public String getMessage() {
    return message;
  }

  public Date getTime() {
    return time;
  }
  
  public Double getLongitude() {
    return longitude;
  }
  
  public Double getLatitude() {
    return latitude;
  }
  
  @Override
  public boolean equals(Object that) {
    return EqualsBuilder.reflectionEquals(this, that, "id", "time");
  }
  
  @Override
  public int hashCode() {
    return HashCodeBuilder.reflectionHashCode(this, "id", "time");
  }
  
}
  1. 书写控制器
package spitter_web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import spittr_data.SpittleRepository;

@Controller
@RequestMapping("/spittles")
public class SpittleController {

	
	private SpittleRepository repository;
	@Autowired//注入数据仓库类
	public SpittleController(SpittleRepository repository) {
		this.repository=repository;
	}
	@RequestMapping(method=RequestMethod.GET)
	public String Spittles(Model model) {
		model.addAttribute("spittleList",repository.findSpittles(Long.MAX_VALUE,20));//将查询到的数据加入模型中
		return "spittlesR";
	}
}
  1. 书写测试类
package chapter_05;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.web.servlet.view.InternalResourceView;

import entity.Spittle;
import spitter_web.SpittleController;
import spittr_data.SpittleRepository;

public class SpittleControllerTest {

  @Test
  public void houldShowRecentSpittles() throws Exception {
    List<Spittle> expectedSpittles = createSpittleList(20);
    SpittleRepository mockRepository = mock(SpittleRepository.class);
    when(mockRepository.findSpittles(Long.MAX_VALUE, 20))
        .thenReturn(expectedSpittles);

    SpittleController controller = new SpittleController(mockRepository);
    MockMvc mockMvc = standaloneSetup(controller)
       /* .setSingleView(new InternalResourceView("/WEB-INF/views/spittlesR.jsp"))//当请求名称与视图返回名称一样时,在这里我将视图名改了
*/        .build();

    mockMvc.perform(get("/spittles"))
       .andExpect(view().name("spittlesR"))
       .andExpect(model().attributeExists("spittleList"))
       .andDo(MockMvcResultHandlers.print());
/*       .andExpect(model().attribute("spittleList", 
                  hasItems(expectedSpittles.toArray())));*/
  }

  private List<Spittle> createSpittleList(int count) {
    List<Spittle> spittles = new ArrayList<Spittle>();
    for (int i=0; i < count; i++) {
      spittles.add(new Spittle("Spittle " + i, new Date()));
    }
    return spittles;
  }
}

6. 接受请求的输入

  • SpringMVC允许以多种方式将客户端的数据传到控制器的处理器方法中
    • 查询参数
    • 表单参数
    • 路径变量
  1. 处理查询参数
  • 通过路径名+参数值将参数传递到控制器
  • 在控制器的方法中通过注解@RequestParam来定义从前端传来的参数,还可以设置默认值
package spitter_web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import spittr_data.SpittleRepository;

@Controller
@RequestMapping("/spittles")
public class SpittleController {

	private SpittleRepository repository;
	
	@Autowired//注入数据仓库类
	public SpittleController(SpittleRepository repository) {
		this.repository=repository;
	}
	
	@RequestMapping(method=RequestMethod.GET)
	public String Spittles(@RequestParam(value="max",defaultValue=(Long.MAX_VALUE+"")) Long max,
			@RequestParam(value="count",defaultValue="20") int count,Model model) {
		model.addAttribute("spittleList",repository.findSpittles(max,count));//将查询到的数据加入模型中
		return "spittlesR";
	}
}
  1. 通过路径参数决定输入
  • 从面向资源的角度来看,要识别的资源应该通过URL路径进行标识,而不是通过查询参数(路径+请求参数)
    • 例如/spittles/show?spittle_id=12345应写成/spittles/show/12345
  • 之前实现的控制器方法都被@RequestMapping映射到静态的地址上,在这里如果要实现设计好的路径参数需要编写@RequestMapping的变量部分
//路径参数实现数据传递
	//之前实现的控制器方法都被@RequestMapping映射到静态的地址上,在这里如果要实现设计好的路径参数需要编写@RequestMapping的变量部分
	@RequestMapping(value="/{spittleId}",method=RequestMethod.GET)//通过占位符{}匹配任意值,设置变量spittleId
	public String spille(@PathVariable("spittleId") Long spittleId,Model model) {//配置注解@PathVariable使用@RequestMapping的变量
		model.addAttribute(repository.findById(spittleId));//key值默认为函数的返回这类型,在这里是spittle
		return "spittle";
	}

代码扩展在SpittleController类中,该类同查询参数

  1. 处理表单
  • 当请求中传递较少数据时,查询参数和路径参数可以胜任,但我们还要处理表单等产生数据比较多的时候要使用处理表单功能
  • 使用表单体现在两个方面
    • 展现表单
    • 处理用户通过表单提交的数据

3.1 展现表单
去配置控制器方法,返回视图名,通过视图解析器去查找相应的jsp文件

package spitter_web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/spittle")
public class SpitterController {

	//展现表单
	@RequestMapping(value="register",method=RequestMethod.GET)
	public String showRegisterForm() {
		return "registerForm";
	}
}

3.2 处理表单

  • 将实体类对象作为控制器方法的参数接收表单数据
  • 注册完成之后,为了防止重复提交表单数据(浏览器刷新页面),可以重定向到一个页面(在return中添加redirect)

处理注册提交的表单,并在处理之后重定向到显示每个注册用户注册信息的页面

package spitter_web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import entity.Spitter;
import spittr_data.SpitterRepository;

@Controller
@RequestMapping("/spittle")
public class SpitterController {
	
	private SpitterRepository repository;
	@Autowired//通过构造器自动注入依赖
	public SpitterController(SpitterRepository repository) {
		this.repository=repository;
	}

	//展现表单
	@RequestMapping(value="register",method=RequestMethod.GET)
	public String showRegisterForm() {
		return "registerForm";
	}
	
	//处理表单
	@RequestMapping(value="register",method=RequestMethod.POST)
	public String handlerRegister(Spitter spitter) {//通过实体类对象接收表单数据
		repository.save(spitter);
		return "redirect:/spitter/"+spitter.getUsername();
		//在将注册信息存储完成之后,为了防止因为舒心浏览器而导致的表单重复提交问题,这里要重定向到每个注册用户的注册信息页面(profile.jsp)
	}
	
	//显示不同注册用户的注册信息
	@RequestMapping(value="{/userName}",method=RequestMethod.GET)
	public String showRegisterInfo(@PathVariable String username,Model model) {
		Spitter spitter = repository.findByUsername(username);
		model.addAttribute(spitter);
		return "profile";
	}
}

6. 校验表单

  • 通过java校验API(java validation API),来对表单数据进行校验,从而限制这行属性值
  • 该方式校验表单并不能阻止表单你提交
  • 通过注解实现
    • @AssertFalse:所注解的元素必须是Bollean类型,并且值为false
    • @AssertTrue:所注解的元素必须是Bollean类型,并且值为true
    • @DecimalMax:所注解的元素必须是数字,并且值小于或等于给定的BigDecimalSting的值
    • @DecimalMin:所注解的元素必须是数字,并且值大于或等于给定的BigDecimalSting的值
    • @Digits:所注解的元素必须是数字类型,并且它的值有指定的位数
    • @Future:所注解的元素必须是一个将来的日期
    • @Max:所注解的元素必须是数字,并且它的值要小于或等于给定的值
    • @Min:所注解的元素必须是数字,并且它的值要大于或等于给定的值
    • @NotNull:所注解的元素不能为空
    • @Null:所注解的元素为空
    • @Past:所注解的元素必须是一个已经过去的日期
    • @Pattern:所注解的元素必须匹配给定的正则表达式
    • @Size:所注解的元素必须是String、集合或数组,并且它的长度要符合给定范围
  • java校验API的实现可能还会提供额外的校验注解,同时我们也可以定义自己的限制条件
  • 设置步骤
    • 给表单数据的实体类属性通过注解设置限定
    • 在控制器的方法中指定要校验的参数(@Valid标注要校验的参数,Errors对象检查是否出错)
  1. 给属性值加入限定
package entity;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Spitter {

  private Long id;
  
  @NotNull//限制属性非空
  @Size(min=5, max=16)//限制字符串长度大于等于5,小于等于16
  private String username;

  @NotNull//限制属性非空
  @Size(min=5, max=25)//限制字符串长度大于等于5,小于等于25
  private String password;
  
  @NotNull//限制属性非空
  @Size(min=2, max=30)//限制字符串长度大于等于2,小于等于30
  private String firstName;

  @NotNull//限制属性非空
  @Size(min=2, max=30)//限制字符串长度大于等于2,小于等于30
  private String lastName;
  
  @NotNull//限制属性非空
/*  @Email*///???????????,还没学到
  private String email;

  public Spitter() {}
  
  public Spitter(String username, String password, String firstName, String lastName, String email) {
    this(null, username, password, firstName, lastName, email);//this在这里的作用??????????
  }

  public Spitter(Long id, String username, String password, String firstName, String lastName, String email) {
    this.id = id;
    this.username = username;
    this.password = password;
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
  }
  1. 在控制器的方法中指定要校验的参数
	//处理表单,校验表单
	@RequestMapping(value="register",method=RequestMethod.POST)
	public String handlerRegister(@Valid Spitter spitter,Errors errors) {
	//通过实体类对象参数接收表单数据,对参数加注解,指定对它进行校验,后跟Errors对象,用来判断校验结果
		if(errors.hasErrors()) {
			return "registerForm";//判断表单数据是否有错,有错则让浏览器转到注册页面
		}
		repository.save(spitter);
		return "redirect:/spitter/"+spitter.getUsername();
		//在将注册信息存储完成之后,为了防止因为舒心浏览器而导致的表单重复提交问题,这里要重定向到每个注册用户的注册信息页面(profile.jsp)
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值