给SpringBoot增加SpringMVC的扩展功能

一、SpringBoot直接跳转

SpringMVC可以写下面的标签,来进行跳转

<mvc:view-controller path="/luzelong" view-name="success"/>

而springboot 想实现该功能,其实也蛮容易的:
1.编写springMVC 功能扩展类:

//该类是springmvc  的功能扩展类   要求书写类上@Configuration 和 并实现WebMvcConfigurer接口
//不标注@EnableWebMvc,则下面的是springmvc的扩展   标注就是完全自定义 摒弃一切的配置
@Configuration
//public class MyMvcConfig extends WebMvcConfigurerAdapter {//1.0的时候是继承该类
public class MyMvcConfig implements WebMvcConfigurer {

//相当于配置了在xml中配置了 <mvc:view-controller path="/luzelong" view-name="success"/>
    public void addViewControllers( ViewControllerRegistry registry){
        //浏览器发送 /luze 请求来到success
        registry.addViewController("/luze").setViewName("success");
//        registry.addViewController("/list.html").setViewName("list");
    }
}

springBoot 1.0需要继承的是WebMvcConfigurerAdapter 类,而到了2.0版本,该类基本已经被遗弃,只需要实现WebMvcConfigurer 接口即可。
除了上述的方式,还可以写下面的:

@Configuration
//public class MyMvcConfig extends WebMvcConfigurerAdapter {//1.0的时候是继承该类
public class MyMvcConfig implements WebMvcConfigurer {

    @Bean
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
            @Override//下面的东西完全可以写在头顶的addViewControllers方法中
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
                registry.addViewController("/main.html").setViewName("dashboard");
            }
        };
        return adapter;
    }

}

那么该SpringMVC扩展类中还可以写什么呢??

二、配置拦截器

无论是springmvc 还是 springboot
第一步都是编写拦截器类:

package org.lzl.bootproject.component;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//拦截器 实现登录校验
public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override//拦截请求
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("loginUser");
        if (user == null){
            //未登录,返回登录页面
            request.setAttribute("msg","没有权限,请先登录");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else {
            //已登录
            return true;
        }

    }

    @Override//拦截响应
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override//渲染完毕
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

第二步:将拦截器配进去
首先看看之前springmvc是怎么配置的:

    <mvc:interceptors>
        <!--如果 不额外配置 :默认拦截一切请求-->
        <!--配置具体的拦截路径-->
        <mvc:interceptor>
            <!--指定拦截的路径-->
            <mvc:mapping path="/**"/>
            <!--指定不拦截的路径-->
            <mvc:exclude-mapping path="/handler/testUpload"/>
            <bean class="interceptor.MyInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

springboot的配置方法:

@Configuration
//public class MyMvcConfig extends WebMvcConfigurerAdapter {//1.0的时候是继承该类
public class MyMvcConfig implements WebMvcConfigurer {

    //注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {//"/", "/index", "/index.html", "/user/login", "/asserts/**"
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/asserts/**");
    }

}

三、获取参数问题

因为SpringBoot是对SSM的整合,所以对参数获取的方法完全可以参考我的这篇博客

本文只是写一下SpringBoot是怎么开启对矩阵变量的支持的:

1.给容器添加下面的Bean,该bean是配置springMVC的核心bean
 @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                //不移除;矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }
2.写完上面的bean,矩阵变量的写法就生效了,下面开始测试:

写个测试的controller:

    @ResponseBody
    @RequestMapping("/jj/{path}")
    public Map<?, ?> jj(@PathVariable("path") String path,
                        @MatrixVariable("name") String name,
                        @MatrixVariable("ages") List<Integer> ages){
        Map<String, Object> result = new HashMap<>();
        result.put("name", name);
        result.put("path", path);
        result.put("ages", ages);
        return result;
    }

然后去访问该页面:
在这里插入图片描述

四、SpingBoot自定义类型转化器

可以借鉴一下 SpringMVC的类型转化的逻辑这篇博客的写法
举例子(将字符串转化成学生)
学生类:

public class Student {
    private int id;
    private String name;
    private int age;
    
     public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
        public Student() {
    }
}


1.给容器添加下面的Bean,该bean是配置springMVC的核心bean
 @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String,Student>(){
                    @Override
                    public Student convert(String source) {
                        //source接受前端传来的String:2-zs-23
                        String[] studentStrArr = source.split("-");
                        Student student = new Student();
                        student.setId(Integer.parseInt(studentStrArr[0]));
                        student.setName(studentStrArr[1]);
                        student.setAge(Integer.parseInt(studentStrArr[2]));
                        return student;
                    }
                });
            }
        };
    }

测试

前端输入的时候需要安装 1-zs-23,这种格式输入

<form action="handler/testConverter">
    学生信息:<input name="studentInfo" type="text"/>
    <input type="submit" value="转换">
</form>

 @RequestMapping("testConverter")
    public String testConverter(@RequestParam("studentInfo") Student student){//因为自己写的转换器已经配置到springmvc中了,所以这里可以自动转换!
        System.out.println(student);
        return "success";
    }

五、Spring对内容协商的支持

1、判断当前响应头中是否已经有确定的媒体类型。MediaType
2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)

demo:

  • pojo
@Data
public class Person {

    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;

}
  • controller
    @ResponseBody  //利用返回值处理器里面的消息转换器进行处理
    @GetMapping(value = "/test/person")
    public Person getPerson(){
        Person person = new Person();
        person.setAge(28);
        person.setBirth(new Date());
        person.setUserName("zhangsan");
        return person;
    }

如果什么都不配置。直接在浏览器中访问 /test/person 则会返回下面的json数据:
在这里插入图片描述
现在我们在pom.xml中添加下面的依赖:(开启对xml格式的支持)

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

再次访问 /test/person ,发现响应的数据为xml格式:
在这里插入图片描述
为什么会这样?因为浏览器的消息头中的accept默认设置了访问的优先级,看下图:
在这里插入图片描述
xml的优先级是0.9,而json的优先级是0.8,所以引入了那个依赖后,springBoot开始支持xml格式,默认显示的就是xml格式了。

如果是postMan这种客户端工具,则只需要指定header的accept属性,即可随意更改响应的类型。

在这里插入图片描述
但我们如果想此时让浏览器依旧返回json格式的数据,则需下面的额外配置:

spring:
    contentnegotiation:
      favor-parameter: true  #开启请求参数内容协商模式

浏览器访问的地址后面 加上 ?format=json 或者 ?format=xml 来指定响应的格式类型:
在这里插入图片描述

自定义MessageConverter

你会发现上面的format只能写 json 或者 xml,比如我想加个x-guigu
就得完成下面的步骤 :
1.编写实现HttpMessageConverter接口的类:

package com.atguigu.boot.converter;

import com.atguigu.boot.bean.Person;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

/**
 * 自定义的Converter
 */
public class GuiguMessageConverter implements HttpMessageConverter<Person> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Person.class);
    }

    /**
     * 服务器要统计所有MessageConverter都能写出哪些内容类型
     *
     * application/x-guigu
     * @return
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-guigu");
    }

    @Override
    public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        //自定义协议数据的写出
        String data = person.getUserName()+";"+person.getAge()+";"+person.getBirth();


        //写出去
        OutputStream body = outputMessage.getBody();
        body.write(data.getBytes());
    }
}

然后在WebMvcConfigurer这个bean中配置刚刚写好的转化器:

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new GuiguMessageConverter());
            }
        };
    }

完成上面的配置,postman即可响应 x-guigu 了
在这里插入图片描述
但用浏览器的话,还需以下的配置:

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
             /**
             * 自定义内容协商策略
             * @param configurer
             */
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                //Map<String, MediaType> mediaTypes
                Map<String, MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("json",MediaType.APPLICATION_JSON);
                mediaTypes.put("xml",MediaType.APPLICATION_XML);
                mediaTypes.put("gg",MediaType.parseMediaType("application/x-guigu"));
                //指定支持解析哪些参数对应的哪些媒体类型
                ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes);
//                parameterStrategy.setParameterName("ff");

                HeaderContentNegotiationStrategy headeStrategy = new HeaderContentNegotiationStrategy();

                configurer.strategies(Arrays.asList(parameterStrategy,headeStrategy));
            }
        };
    }

接着浏览器访问 test/person?format=gg
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

键盘歌唱家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值