0
点赞
收藏
分享

微信扫一扫

SpringBoot中对SpringMvc的特殊定制

SpringBoot中对SpringMvc的特殊定制

1、自定义拦截器

package cool.ale.config;

import cool.ale.interceptor.TimeInterceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Component
public class MyWebMvcConfiguration implements WebMvcConfigurer {

    /**
     * 添加拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TimeInterceptor())      // 添加一个拦截器
            .addPathPatterns("/**");     // 拦截映射规则,拦截所有请求
                //.excludePathPatterns("")  // 设置排除的映射规则
    }
}
package cool.ale.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.LocalDateTime;

public class TimeInterceptor implements HandlerInterceptor {

    Logger logger = LoggerFactory.getLogger(TimeInterceptor.class);

    LocalDateTime start;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 开始时间
        start = LocalDateTime.now();
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 结束时间
        LocalDateTime end = LocalDateTime.now();

        // 计算两个时间差
        Duration between = Duration.between(start, end);

        // 获得相差毫秒
        long millis = between.toMillis();

        logger.info("当前请求:" + request.getRequestURI() + ":执行时间:" + millis + "毫秒。");
    }
}

2、CORS跨域请求

2.1、全局配置

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>rest首页</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.0/jquery.min.js"></script>
</head>
<body>
    <button id="restButton">请求跨域请求</button>
</body>

    <script type="text/javascript">
        $("#restButton").click(function () {
            $.ajax({
                url:'http://localhost:8080/user/1',
                type:'GET',
                success:function (result) {
                    alert(result)
                }
            });
        })
    </script>
</html>

在这里插入图片描述

/**
 * 跨域请求处理(全局的配置)
 * @param registry
 */
@Override
public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/user/*")            // 映射本服务器中哪些http接口可以进行跨域请求
            .allowedOrigins("http://localhost:8101")           // 配置哪些来源有权限跨域
            .allowedMethods("GET","POST","DELETE","PUT");      // 配置允许跨域请求的方法
    }

2.2、单个配置

/**
 * 查询
 * @param id
 * @return
 */
@GetMapping("/{id}")
@ApiOperation("根据用户id查询相应的用户信息")
@CrossOrigin
public Result getUser(@PathVariable Integer id){
    User user = userService.getUser(id);
    return new Result<>(200,"查询成功!",user);
}

3、Json

3.1、Jackson的使用

注解含义
@JsonIgnore排除json序列化,将它标注在属性上将不会进行json格式化
@JsonFormat(pattern = “yyyy-MM-dd hh:mm:ss”, locale = “zh”)进行日期格式化
@JsonInclude(JsonInclude.Include.NON_NULL)当这个字段不为null时,给它进行json序列化
@JsonProperty(“ha”)可以通过这个属性给字段设置别名

3.2、自定义json的序列化与反序列化

package cool.ale.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.*;
import cool.ale.entity.User;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.jackson.JsonObjectDeserializer;
import org.springframework.boot.jackson.JsonObjectSerializer;

import java.io.IOException;

@JsonComponent
public class UserJsonCustom {
    public static class Serializer extends JsonObjectSerializer<User> {

        @Override
        protected void serializeObject(User user, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeObjectField("userId",user.getId());
            jgen.writeObjectField("addR",user.getAddress());
        }
    }

    public static class deserializer extends JsonObjectDeserializer<User>{

        @Override
        protected User deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec, JsonNode tree) throws IOException {
            User user = new User();
            // 这里的反序列化相当于是请求进来的id会进行json封装
            user.setId(tree.findValue("id").asInt());
            return user;
        }
    }
}

4、国际化

4.1、通过浏览器设置的语言参数,在请求头中获取实现国际化

4.1.1、添加国际化资源文件

在这里插入图片描述

4.1.2、配置messageResource 设置国际化文本

在这里插入图片描述

@Configuration(
    proxyBeanMethods = false
)
// 如果自己配置了@Bean名字叫messageSource的bean,就会用自定义的
@ConditionalOnMissingBean(
    name = {"messageSource"},
    search = SearchStrategy.CURRENT
)
@AutoConfigureOrder(-2147483648)
// @Conditional自定义条件匹配,会传入一个实现了Condition接口的类ResourceBundleCondition
// ResourceBundleCondition 会重写matches方法,自定义匹配规则,如果该方法返回true,则代表匹配成功
@Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class})
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    String classOrMethodName = getClassOrMethodName(metadata);

    try {
    	// 具体的匹配规则在 getMatchOutcome 方法里面
        ConditionOutcome outcome = this.getMatchOutcome(context, metadata);
        this.logOutcome(classOrMethodName, outcome);
        this.recordEvaluation(context, classOrMethodName, outcome);
        return outcome.isMatch();
    } catch (NoClassDefFoundError var5) {
        throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);
    } catch (RuntimeException var6) {
        throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
    }
}
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
	// 获取配置文件中 spring.messages.basename 属性的值,默认值为 messages
    String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
    ConditionOutcome outcome = (ConditionOutcome)cache.get(basename);
    if (outcome == null) {
        outcome = this.getMatchOutcomeForBasename(context, basename);
        cache.put(basename, outcome);
    }

    return outcome;
}
private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) {
    Builder message = ConditionMessage.forCondition("ResourceBundle", new Object[0]);
    String[] var4 = StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename));
    int var5 = var4.length;

    for(int var6 = 0; var6 < var5; ++var6) {
        String name = var4[var6];
        Resource[] var8 = this.getResources(context.getClassLoader(), name);
        int var9 = var8.length;
		// 根据 message 获取 该类路径下的所有 properties 的资源文件
		// 当如果这个路径下有属性资源文件时,匹配结果将会是true
        for(int var10 = 0; var10 < var9; ++var10) {
            Resource resource = var8[var10];
            if (resource.exists()) {
                return ConditionOutcome.match(message.found("bundle").items(new Object[]{resource}));
            }
        }
    }

    return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll());
}

在这里插入图片描述

4.1.3、通过去解析请求头中的accept-language 或者 解析url参数中?local=

@Bean
@ConditionalOnMissingBean(
    name = {"localeResolver"}
)
public LocaleResolver localeResolver() {
	// 当配置文件中有 spring.mvc.locale-resolver=fixed
	// 就会去配置文件里面找 spring.mvc.locale,这里相当于设置了一个固定值
    if (this.webProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.WebProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.webProperties.getLocale());
    } else if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
    } else {
    	// 如果上面没有设置fixed,则会以AcceptHeaderLocaleResolver 类去解析
    	// 将配置文件里面的这个参数当成一个默认值,如果请求头没有的话,则会去取这个默认值
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
        Locale locale = this.webProperties.getLocale() != null ? this.webProperties.getLocale() : this.mvcProperties.getLocale();
        localeResolver.setDefaultLocale(locale);
        return localeResolver;
    }
}
public Locale resolveLocale(HttpServletRequest request) {
    Locale defaultLocale = this.getDefaultLocale();
    // 先去请求头的Accept-Language参数去找,如果没有,则会找配置文件里面默认的
    if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
        return defaultLocale;
    } else {
        Locale requestLocale = request.getLocale();
        List<Locale> supportedLocales = this.getSupportedLocales();
        if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
            Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
            if (supportedLocale != null) {
                return supportedLocale;
            } else {
                return defaultLocale != null ? defaultLocale : requestLocale;
            }
        } else {
            return requestLocale;
        }
    }
}

4.1.4、直接通过MessageSource类调用

package cool.ale.controller;

import cool.ale.entity.Result;
import cool.ale.entity.User;
import cool.ale.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/user")
@Api("用户控制类")
public class UserController {

    @Autowired
    UserService userService;

    @Autowired
    MessageSource messageSource;

    /**
     * 查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据用户id查询相应的用户信息")
    public Result getUser(@PathVariable Integer id){
        String message = messageSource.getMessage("user.query.success",null, LocaleContextHolder.getLocale());
        User user = userService.getUser(id);
        return new Result<>(200,message,user);
    }
}

4.2、根据传递参数的形式实现国际化

4.2.1、添加国际化拦截器(在MyWebMvcConfiguration类中,自定义的)

/**
 * 添加拦截器
 * @param registry
 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
    // 添加国际化拦截器
    registry.addInterceptor(new LocaleChangeInterceptor())
            .addPathPatterns("/**");    // 拦截映射规则
}

4.2.2、覆盖掉之前的获取国际化标识的方法(在MyWebMvcConfiguration类中,自定义的)

/**
 * 重写localeResolver方法,让其在 url 中取参数设置国际化文本
 * @return
 */
@Bean
public LocaleResolver localeResolver() {
    CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
    // 设置过期时间,设计一个月
    cookieLocaleResolver.setCookieMaxAge(60*60*24*30);
    cookieLocaleResolver.setCookieName("locale");
    return cookieLocaleResolver;
}

4.2.3、测试

10、其它

10.1、WebMvcConfigurer原理

@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

public DelegatingWebMvcConfiguration() {
}

@Autowired(
    required = false
)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
        this.configurers.addWebMvcConfigurers(configurers);
    }

}
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    Iterator var2 = this.delegates.iterator();

    while(var2.hasNext()) {
        WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
        delegate.configureDefaultServletHandling(configurer);
    }

}

注意:我们在自定义的SpringMvc类上千万不能加上@EnableWebMvc注解,因为当我们加上这个注解之后,WebMvcAutoConfiguration类中的扩展方法将会失效。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
举报

相关推荐

0 条评论