美文网首页
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

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