SpringBoot_web开发-扩展与全面接管SpringMVC

前面我们分析了SpringBoot对SpringMVC,自动配置的一些功能,但实际在开发过程中,仅靠Springboot的自动配置,

是不够用的,比如我来举一个例子,我们以前有SpringMVC配置文件的时候,大家可能写过这些标签

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

视图映射,我把hello的请求发到success,这样视图解析器也会帮我们配到success页面,包括大家也写过

<mvc:interceptors>

</mvc>

这是我们定义SpringMVC的拦截器

<!--拦截器 -->
<mvc:interceptors>
	<!--多个拦截器,顺序执行 -->
	<mvc:interceptor>
		<!-- 表示所有url包括子url路径 -->
		<mvc:mapping path="/**"/>
		<bean class="com.learn.interceptor.SecurityInterceptor"></bean>
	</mvc:interceptor>
</mvc:interceptors>

比如我们来拦截hello请求,然后是哪个拦截器我们用bean来标识上,这就是经常要做的配置,但没了这个配置文件,

这个功能能不能做,我们Springboot也提供了扩展,还是看我们之前的这段话,

If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration 

(interceptors, formatters, view controllers etc.) you can add your own @Configuration class of 

type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of

 RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver 
 
 you can declare a WebMvcRegistrationsAdapter instance providing such components.

如果我们想要保持Spring boot对SpringMVC配置的这些功能,而且只是想给里面添加一些功能,比如interceptors,

view controllers,我们可以添加一个自己的@Configuration,我们要做的就是,编写一个配置类,这个配置类就是用

@Configuration注解标注的,这个类才能成为配置类,来编写一个配置类,这个类的类型是WebMvcConfigurerAdapter,

是这个类型的,without @EnableWebMvc,我们还不能标注@EnableWebMvc注解,这个就是我们向来扩展的话就是这么来扩展,

我来举一个例子

想要实现刚才的这个功能,我们就来给他扩展一下,我们自己来写一个配置类,首先要用@Configuration来标注,

是一个配置类,而且还是WebMvcConfigurerAdapter这个类型,这是一个抽象类,我们继承他就行了

public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {

继承他有什么用呢,我们点进来看一下,里面有非常多的空方法,空方法其实是来源于这个接口的,

public interface WebMvcConfigurer {

这个接口就是SpringMVC的扩展配置,而我们实现接口太麻烦了,所以给我们适配器空方法,做什么功能就在这里

配置,比如要做异步支持的这里有异步的,

/**
 * {@inheritDoc}
 * <p>This implementation is empty.
 */
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}

/**
 * {@inheritDoc}
 * <p>This implementation is empty.
 */
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}

这个标签的功能

<mvc:default-servlet-handler>

就是我们写的这个功能,包括下边有添加Formatters的

/**
 * {@inheritDoc}
 * <p>This implementation is empty.
 */
@Override
public void addFormatters(FormatterRegistry registry) {
}

还有添加Interceptors

/**
 * {@inheritDoc}
 * <p>This implementation is empty.
 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
}

Interceptors不就是这个功能吗,

/**
 * {@inheritDoc}
 * <p>This implementation is empty.
 */
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}

给资源注册的,之前的静态资源映射规则,我们以后就来用它就行了,使用WebMvcConfigurerAdapter就可以扩展

SpringMVC的功能,那怎么扩展呢,要什么功能就重写什么方法就可以了,比如我们要添加视图映射,我们就

addViewControllers,剩下的都一样,我这里有一个registry,有一个addViewController,添加一个视图映射,

首先是urlPath,你要把什么请求映射到页面,浏览器发送/learn,去哪个页面呢,继续链式调用,setViewName,

然后去哪个页面,我们写一个index,也是由我们视图解析器来进行解析的,他的效果呢就是浏览器发送,/learn请求,

然后也来到success页面,我们直接来做视图映射,

http://localhost:8080/learn

确实来到成功页面了
package com.learn.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
	
	@ResponseBody
	@RequestMapping("/hello")
	public String hello() {
		return "hello";
	}
	
	@ResponseBody
	@RequestMapping("/success")
	public String success() {
		return "success";
	}
	
}
package com.learn.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @Configuration:指明当前类是配置类;就是来替代之前的Spring配置文件
 * 
 * 在配置文件中用<bean></bean>标签添加组件
 * 
 */
//@EnableWebMvc 不要接管SpringMVC
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
	

		@Override
		public void addViewControllers(ViewControllerRegistry registry) {
			// 浏览器发送/learn 请求来到success
			registry.addViewController("/learn").setViewName("success");
		}
	
}
package com.learn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @SpringBootApplication 来标注一个主程序类,说明这是一个Sprint Boot应用
 * @author Leon.Sun
 *
 */
//@ImportResource(locations= {"classpath:beans.xml"})
@SpringBootApplication
public class SpringBoot02ConfigApplication {

	public static void main(String[] args) {
		// Spring应用启动起来
		SpringApplication.run(SpringBoot02ConfigApplication.class,args);
	}
	
}
要扩展SpringMVC就这么来用,这个是扩展SpringMVC,他既保留了所有的自动配置,也能用我们自己扩展的,

那么这个原理是什么呢,我们也可以大概的来看一看,还是来到我们的WebMvc配置中,我们还是观察他,

WebMvcAutoConfiguration是SpringMVC的自动配置类,然后第二个我们来看,这里还有一个WebMvcAutoConfigurationAdapter,

他也是继承WebMvcConfigurerAdapter,他也是利用我们的Adapter机制,提供的方法,然后把组件添进去,我们之前的静态资源映射,

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
	if (!this.resourceProperties.isAddMappings()) {
		logger.debug("Default resource handling disabled");
		return;
	}
	Integer cachePeriod = this.resourceProperties.getCachePeriod();
	if (!registry.hasMappingForPattern("/webjars/**")) {
		customizeResourceHandlerRegistration(registry
				.addResourceHandler("/webjars/**")
				.addResourceLocations("classpath:/META-INF/resources/webjars/")
				.setCachePeriod(cachePeriod));
	}
	String staticPathPattern = this.mvcProperties.getStaticPathPattern();
	if (!registry.hasMappingForPattern(staticPathPattern)) {
		customizeResourceHandlerRegistration(
				registry.addResourceHandler(staticPathPattern)
						.addResourceLocations(
								this.resourceProperties.getStaticLocations())
						.setCachePeriod(cachePeriod));
	}
}

就是调用的addResourceHandlers方法,我们看这个类目的不是分析他,主要是他上面有一个

@Import(EnableWebMvcConfiguration.class),

@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {

我们在配置的时候呢,在做其他配置时,会导入什么呢,会导入EnableWebMvcConfiguration,那我说他的目的是干什么呢,

还在WebMvc自动配置类中,它是继承DelegatingWebMvcConfiguration

/**
 * Configuration equivalent to {@code @EnableWebMvc}.
 */
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

这个类的描述是这个样子的,而这段描述里面重要的是什么呢,重要的是他继承的父类,这个父类里面呢,有这么一段话

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


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

这段话的作用是什么,@Autowired叫自动装配,自动装配什么呢,自动装配一旦装在方法上,方法的参数就要从容器中

获取,相当于这句话的作用就是说,从容器中获取什么,你看这里是一个list,List<WebMvcConfigurer>,获取所有的

WebMvcConfigurer,然后把这些configure赋值到configurers里面,赋值到这里有啥用,我们注意,然后在这个里面,

有我们之前的配置,每一个都是调configure的方法,比如视图映射,

@Override
protected void addViewControllers(ViewControllerRegistry registry) {
	this.configurers.addViewControllers(registry);
}

他调的是configurers.addViewControllers(registry);我就以这个为例,我点进去看,

@Override
public void addViewControllers(ViewControllerRegistry registry) {
	for (WebMvcConfigurer delegate : this.delegates) {
		delegate.addViewControllers(registry);
	}
}

他其实是把所有的WebMvcConfigurer都拿来,

private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();

delegates保存类的信息,他的一个参考实现,配置它是这样子的,

将所有的WebMvcConfigurer相关的配置都来一起调用,相当于一起起作用,不只是Spring Boot给我们的配置起作用,

第二句话的核心目的,就在这,所有的容器中,所有的WebMvcConfigurer,都会一起来起作用,那就包括是不是我们

自己写的,自己写的这个配置类,继承了WebMvcConfigurerAdapter,Adapter就是我们的一个实现,我们的配置类,

也会被调用,实现的效果是什么,首先使我们的SpringMVC自个的配置在起作用的时候,把我们的也顺便召唤进来了,

通过这种方式,SpringMVC的自动配置,和我们的扩展配置,都会起作用,这个就是我们说的如何扩展SpringMVC,不能够标注

@EnableWebMvc,标了他有啥特点呢,又看这句话

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated 

with @EnableWebMvc.

如果我们想完全的掌控Spring MVC,我们需要给我们的注解添加一个@EnableWebMvc,就是我们这个注解的作用,

全面接管SpringMVC,所谓的全面接管SpringMVC,就是Spring Boot对SpringMVC的自动配置不要了,然后所有的都是

我们自己配置,这个就类似于什么呢,类似于我们以前来开发三大框架整合的时候,刚把SpringMVC配进来,就是那个时候

是什么样子,就是什么样子,我们在配置文件里面该要写什么,你就来继续自己来写就行了,所有都是我们自己配置,只需要

干什么呢,只需要加一个@EnableWebMvc,在配置类中添加@EnableWebMvc,那我们就来添一下,看一下他的效果是什么,我就来

添加一个@EnableWebMvc,我们把它一添上以后呢,我来重新启动,重新启动的效果是什么呢,我们现在全面接管了SpringMVC,

既然全面接管了,那相关于以前的SpringMVC自动配置,他是不是失效了,既然失效了,我就来测几个,如果自动配置生效,

控制台在启动的时候,我们来看这个打印,这里打印没有见到相应的filter,如果我把这个注解注掉,我再来启动,我们不全面

接管,我们控制台会打印hiddenHttpMethodFilter,所以我们这个filter写过来了,包括我们再来测一下,我把这个注解开启,

我们全面接管了,就是SpringMVC对SpringBoot的自动配置失效,最典型的我们来测一下,就是我们这些静态资源,webjars请求,

或者静态页面,不能访问了,如果我现在先不配他,我们的静态页面肯定能访问的,我就直接访问index页面

localhost:8080/

没问题,但是说如果我加上了这个@EnableWebMvc注解,全面接管,静态资源映射失效,我来启动一下,看能不鞥访问首页,

localhost:8080/

以及webjars请求呢,我们发现现在就404了
package com.learn.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @Configuration:指明当前类是配置类;就是来替代之前的Spring配置文件
 * 
 * 在配置文件中用<bean></bean>标签添加组件
 * 
 */
// 不要接管SpringMVC
@EnableWebMvc 
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
	

		@Override
		public void addViewControllers(ViewControllerRegistry registry) {
			// 浏览器发送/learn 请求来到success
			registry.addViewController("/learn").setViewName("success");
		}
	
}
SpringBoot对我们提供的所有配置,都失效了,这里的效果就是所有的自动配置都失效,严格来说是SpringMVC的

自动配置都失效了,连我们的静态资源,都没法用了,我们在类中加一个他就行了,全面接管我们就用这个样式,当然我们

后来开发不建议全面接管,如果说我们只是做一个简单的功能,我们可以用全面接管,可以节省空间,但是我们往往是需要

用非常多的功能的,原理是什么呢,为什么写了一个他自动配置就失效了,为什么加了一个@EnableWebMvc,自动配置就失效了,

非常简单,来看一下,@EnableWebMvch核心在这,这个注解的核心就是导入一个他

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

@EnableWebMvch核心就是导入DelegatingWebMvcConfiguration,这个东西我点进来,他就是我们看的组合逻辑

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


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

他的父类点进来,他给我们做基本的配置,RequestMappingHandlerMapping,这父类我们就不看了,那么看他有什么用呢,

他怎么把自动配置给失效了呢,那你回头再来看一下自动配置,

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
		WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

有一个非常重要的一句话,@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),啥叫OnMissingBean,

容器中没有这个组件的时候,接下来自动配置类里面的所有功能,才起效,这个自动配置类才生效,既然容器中没有

WebMvcConfigurationSupport,而刚才我们@EnableWebMvc,给我们容器中导了这么一个组件,这个组件正是

WebMvcConfigurationSupport,也就是说一句话,这个@EnableWebMvc帮我们把这个组件导进来了,所以自动配置类

判断失效了,接下来就由我们配置了,将WebMvcConfigurationSupport组件导入进来了,导入的这个组件只是SpringMVC

的基本功能,像那些视图解析器,包括拦截器,都需要自己再来配置一下,这就是我们使用@EnableWebMvc可以全面接管,

用不到SpringBoot的任何自动配置,当然这样希望大家更能吸取这种模式,我们如何改SpringBoot的配置呢,在SpringBoot

里边,又会有很多很多的类似的Configure,有异步的Configure支持,Configure就是帮我们进行配置扩展的,在SpringBoot中,

会有非常多的xxxxConfigurer,帮助我们进行扩展配置,只要遇见他们了,大家多留心

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值