美文网首页
4.SpringMVC

4.SpringMVC

作者: 9156523 | 来源:发表于2019-05-02 18:06 被阅读0次

SpringMVC

SpringMVC的核心是DispatcherServlet,所有请求都要通过它转发,当一个用户发起一个请求,DispatcherServlet先找到处理器映射,根据映射找到类中对应的控制器,然后调用控制器中对应的方法,返回数据模型(即各种需要被前端用到的数据,比如实体类)以及视图名称,然后DispatcherServlet在根据控制器返回的视图名称找到视图解析器解析视图,最后输出到前端响应

  1. DispatcherServlet找到处理器映射
  2. 根据映射结果找到控制器中对应的方法
  3. 根据方法返回的模型和视图名找到视图解析器解析
  4. 然后根据视图解析器找到视图使用模型渲染结果

在这里模型指的是返回给用户并在浏览器显示的数据

1.通过java配置取代web.xml配置

servlet3.0规范定义了一个ServletContainerInitializer接口来初始化类,用于替代web.xml,在servlet容器启动时,会加载该接口的实现类用于加载相关配置,SpringMVC的AbstractAnnotationConfigDispatcherServletInitializer实现了这个接口,继承这个接口即可实现不通过web.xml配置文件,


//当web程序启动时,tomcat会加载实现了ServletContainerInitializer,而下面这个就实现了
//所以。这个就是一个配置类,用于代替web.xml,在该类被初始化时,会同时初始化Dispatcher核心类和ConTextLoaderListener
//后面ContextLoaderListener个类用于getServletConfigClasses方法
public class SpitterWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

   //根配置,配置数据库等 其他要用到的组件信息
   @Override
   protected Class<?>[] getRootConfigClasses() {
       return new Class<?>[]{RootConfig.class};
   }

   //Dispatcher配置
   //使用conTextLoaderListener加载前端控制器的上下文
   @Override
   protected Class<?>[] getServletConfigClasses() {
       return new Class<?>[]{WebConfig.class};
   }


   //实现ServletContainerInitializer是用于替代web.xml,而Abstra....是SpringMVC 在替代web.xml的基础上加入MVC特有的组件
//而这个ServletContainer是servlet3.0以后的产物,servlet容器tomcat7或者更高的版本才能这么使用
   //拦截
   @Override
   protected String[] getServletMappings() {
       return new String[]{"/"};
   }
}

RootConfig

package spitter.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;

@Configuration
@ComponentScan(basePackages = {"spitter"},excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = EnableWebMvc.class)})
public class RootConfig {
}

WebConfig

package spitter.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.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("spitter.web")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver();

        resourceViewResolver.setPrefix("/WEB-INF/view/");
        resourceViewResolver.setSuffix(".jsp");
        resourceViewResolver.setExposeContextBeansAsAttributes(true);

        return resourceViewResolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

其中getServletConfigClass用于获取配置Dispatcher相关属性,而rootConfig用于配置其他类,例如mybatis redies等

最后在创建一个控制器

package spitter.web;

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

@Controller
public class HomeController {
    static {
        System.out.println("初始化成功");
    }
    @RequestMapping(value = "/",method = RequestMethod.GET)
    public String home(){
        return "home";
    }
}

然后在对应设置的目录创建一个jsp,最后测试就能成功跳转到页面

传递模型数据到视图中

在控制器的方法中,可以指定模型参数,就可以把模型数据带到前端显示,实际这个参数就是一个map,具体如下,在这里传递了一个对象集合

    /**
     * 给定一个模型参数,用于传递 需要显示在前端的数据,实际上model是一个map,
     * 当不给其传递key时,他会自己推断,在这里名字为spittles,如果你不希望使用Model 作为参数
     * 你也可以使用Map model作为参数,效果已有
     */
    public String home(Model model){
        model.addAttribute(createSpittleList(20));
        return "home";
    }

接收请求的输入

SpringMVC自然也需要支持前端传参数到后台,SpringMVC中可以在方法参数前面添加一个@RequestParam表示要接受的参数的name,代码如下

   /**
     * 在这里@RequestParam代表必须要传的参数,也可以指定一个默认值,在没有传入参数的时候
     * 将赋值为默认值,默认值都必须是String类型的,不过在赋值给max的时候会做对应的转换
     * @param max
     * @param count
     * @return
     */
    public List<Spittle> spittles(@RequestParam(value = "max",defaultValue = "3") long max,@RequestParam int count){
        System.out.println(max +" " +count);
        return findSpittle(max,count);
    }

如果在一个页面的form表单上不写action,那么这个表单提交时,就会默认提交到 跳转到这个页面上的路径,意思就是 如果你访问 /index,而Controller跳转到index.jsp,那么这个jsp就会提交到该控制器中去,即可指定一个POST同名方法,参数不一致即可(重载)

如下,其中redirect代表重定向,直接输入controller的url路径

    @RequestMapping(value = "view",method = RequestMethod.POST)
    public String spittles(@RequestParam(value = "max",defaultValue = "3") long max,@RequestParam int count){
        System.out.println(max +" " +count);
        return "redirect:/view";
    }
    @RequestMapping(value = "view",method = RequestMethod.GET)
    public String spittles(){

        return "form";
    }

校验表单

普通的后台校验表单是获取到前端的值,然后做很多逻辑判断,这样既写了很多重复代码,而且如果字段比较多,写起来也比较麻烦,从Spring3.0开始,Spring提供了对java校验api,又称之为JSR-303,实际上是一个接口,而其实现类就是Hibernate Validator,下面是相关注解

  • @AssertFalse:所注解的属性必须是false
  • @AssertTrue:所注解的属性必须是true
  • @DecimalMax:所注解的属性必须是数字,且小于给定的值
  • @DecimalMin:所注解的属性必须是数字,且大于给定的值
  • @Digits:必须是数字,且必须有指定的数字
  • @Future:必须是将来的日期
  • @Max:必须是数字,且值要小于或者等于给定的值
  • @Min:同上,大于给定的值
  • @NotNULL:不能为空
  • @NUll:必须为null
  • @Past:必须是过去的日期
  • @Pattern:必须匹配正则表达式
  • @Size:值必须是String、集合、数组,且长度符合要求

用法如下

package spitter;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;


public class Spittle {
    private  Long id;
    @NotNull(message = "不能为空")
    @Size(min=5,max = 16,message = "不能超过5-16")
    private  String message;
    @NotNull
    @Size(min=5,max=25,message = "不能超过5-25")
    private  String time;
    @NotNull
    @Size(min=2,max=30,message = "不能超过2-30")
    private String latitude;
    @NotNull
    @Size(min=2,max = 30,message ="不能超过2-30")
    private String longitude;

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

    public Spittle() {
    }

    public Spittle(String message, String time) {
        this.message = message;
        this.time = time;
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }

    @Override
    public String toString() {
        return "Spittle{" +
                "id=" + id +
                ", message='" + message + '\'' +
                ", time='" + time + '\'' +
                ", latitude='" + latitude + '\'' +
                ", longitude='" + longitude + '\'' +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

    public String getLatitude() {
        return latitude;
    }

    public void setLatitude(String latitude) {
        this.latitude = latitude;
    }

    public String getLongitude() {
        return longitude;
    }

    public void setLongitude(String longitude) {
        this.longitude = longitude;
    }
}

    @RequestMapping(value = "registry",method = RequestMethod.POST)
    public String processRegistration( @Valid Spittle spittle, Errors error){
        System.out.println(spittle);
        if(error.hasErrors()){
            System.out.println(error.getAllErrors());
            return "form";
        }

        return "home";
    }

建议不要使用高版本,我使用6.0.9报错,版本兼容报错,显示初始化失败,换成5.3版本以后正常执行

使用Apache Tiles视图定义布局

如果需要对每一个布局添加头部和尾部,常规做法是为每一个jsp模版设置头部和尾部,这样明显扩展性不好且增加日后维护成本,所以就需要布局引擎Apache Tiles

没什么卵用,多了些莫名其妙的配置,实际也就和一般的头部引用jsp,没什么差别

文件上传

Spring的文件上传比较简单,只要在Spring 中注册一个文件上传的解析器就行了,Spring中有两个解析器

  • ComonsMultipartResolver:通用
  • StandardServletMultipartResolver:适用于servlet3.0以后

基本用法

  1. 在Spring配置文件中实例化一个上传解析器
  2. 接收用MultipartFile

使用java配置的一般例子

在主配置类中重写方法


    /**
     * 设置上传文件的临时文件
     * @param registration
     */
    @Override
    protected void customizeRegistration(ServletRegistration.Dynamic registration) {
        //设置零时文件夹,以及其他相关设置
        registration.setMultipartConfig(new MultipartConfigElement("/Users/f7689386/PycharmProjects",2097152,4194304,0));
    }

Controller写法

    public String processRegistration(@RequestPart("profilePicture")MultipartFile profilePicture){
        //获取名字
        System.out.println(profilePicture.getOriginalFilename());


        return "home";
    }
    

MultipartFile接口相关方法

public abstract interface MultipartFile {
  String getName();
  String getOriginalFilename();
  String getContentType();
  boolean isEmpty();
  long getSize();
  byte[] getBytes() throws java.io.IOException;
  InputStream getInputStream() throws java.io.IOException;
  void transferTo(File arg0) throws IOException,IllegalStateException;
}
 

定义异常通知切面

如果有多个控制器抛出同样的异常,要对每一个方法都要进行处理跳转到error页面,这样会有大量存在代码,可以抽取成一个切面,对每一个抛出该异常的方法 作为一个切点

在SpringMVC中,可以给类添加@ControllerAdvice代表这个类是一个通知类,@ExceptionHandler代表这个方法是一个异常通知方法

java

@ControllerAdvice
public class AppWideExceptionHandler{
   @ExceptionHandler(DuplicateSprittleException.class)
   public String duplicateSplittleHandler(){
       return "error";
   }
}

上面这个类代表如果有某个类抛出DulicateSprittleException异常,则执行上面那个方法,其中ControllerAdvice里面包含的@Component,所以在启动的时候它也会被扫描进来被Spring管理

重定向传递数据

当用户提交post请求以后,如果不使用重定向跳转而使用请求转发,那么在刷新页面或者后退可能会产生多次提交数据等危险操作,但是在SpringMVC中Model的生命周期都是一次请求,在重定向model存储的数据都会消失

有个方案是把需要访问的数据存储在会话Session中,Spring也认为这是一个不错的方式,但是Spring认为我们不需要管理这些数据,Spring提供了RedirectAttributes设置属性,在跳转到页面以后数据消失,类似一次请求存储的数据

    @RequestMapping(value = "redirect",method = RequestMethod.GET)
    public String redirect(RedirectAttributes model){
        model.addFlashAttribute("a","a");
        return "redirect:/spitter/(username)";
    }

如果传递的是一个变量名,还可以不设置key,他会默认以变量名作为key

相关文章

  • 4.SpringMVC

    SpringMVC SpringMVC的核心是DispatcherServlet,所有请求都要通过它转发,当一个用...

网友评论

      本文标题:4.SpringMVC

      本文链接:https://www.haomeiwen.com/subject/imannqtx.html