SpringBoot学习笔记——(三)整合WEB项目

1. 静态资源

如何用springboot进行web开发?

  • 1.创建SpringBoot应用,选中我们需要的模块.
  • 2.SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来.
  • 3.自己编写业务代码。

springboot给我们做了哪些自动配置呢?下面就来依次展开。

关于springboot给我们做了哪些自动配置,我们可以去看jar包中的:xxxAutoConfiguration、和xxxProperties

1.1 springboot对静态资源的映射

使用springboot开发web项目,我们可以直接创建一个可以打包成jar包的mavne应用。

关于springboot对静态资源的映射规则我们可以去看WebMvcAutoConfiguration配置类给我们配置了哪些静态资源映射。我们在该类中发现如下方法就是对静态资源映射的处理:

        @Override
		public void addResourceHandlers(ResourceHandlerRegistry registry) {
		    //这里主要判断是否启动默认的资源处理===》通过查看resourceProperties类中的addMappings属性默认值为true,因此是默认使用springboot提供的资源配置处理
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
			CacheControl cacheControl = this.resourceProperties.getCache()
					.getCachecontrol().toHttpCacheControl();
			if (!registry.hasMappingForPattern("/webjars/**")) {
				customizeResourceHandlerRegistration(registry
				        //设置/webjars/**请求
						.addResourceHandler("/webjars/**")
						//设置所有有关/webjars/**的请求都从/META-INF/resources/webjars/中去找
						.addResourceLocations("classpath:/META-INF/resources/webjars/")
						//设置有关/webjars/**请求有关的缓存:缓存时间等
						.setCachePeriod(getSeconds(cachePeriod))
						.setCacheControl(cacheControl));
			}
			//获取静态资源的请求方式===》通过查看发现staticPathPattern = "/**";
			String staticPathPattern = this.mvcProperties.getStaticPathPattern();
			if (!registry.hasMappingForPattern(staticPathPattern)) {
				customizeResourceHandlerRegistration(
				        //设置请求/**请求
						registry.addResourceHandler(staticPathPattern)
						        //设置有关/**请求都从"classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/"中去映射
								.addResourceLocations(getResourceLocations(
										this.resourceProperties.getStaticLocations()))
								//设置有关/**的静态资源请求的缓存信息
								.setCachePeriod(getSeconds(cachePeriod))
								.setCacheControl(cacheControl));
			}
		}

通过上面的代码分析:我们可以发现,springboot给我们进行的默认配置有:

  • 1.如果用户没有配置resourceProperties属性类中的addMapping属性,springboot默认配置为true:表示默认使用springboot的配置的静态资源映射方式。

  • 2.如果没有映射器处理关于webjars请求,springboot就默认从:类路径下的/META-INF/resources/webjars/目录 路径下去寻找该请求的静态资源

  • 3.如果没有映射器处理/**静态资源的请求,springboot就默认从classpath:/META-INF/resources/", “classpath:/resources/”,“classpath:/static/”, "classpath:/public/ 路径下去寻找响应的静态资源。

  • 4.用户可以修改把哪种映射规则的请求路径映射到classpath:/META-INF/resources/", “classpath:/resources/”,“classpath:/static/”, "classpath:/public/

    /**
	 * Path pattern used for static resources.
	 */
	private String staticPathPattern = "/**"; #springboot默认配置是/**

1.2 springboot对欢迎页的配置

通过继续查看springboot关于springmvc的自动配置源码WebMvcAutoConfiguration发现,springboot也为我们进行了欢迎页的配置:

        @Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(
				ApplicationContext applicationContext) {
			return new WelcomePageHandlerMapping(
					new TemplateAvailabilityProviders(applicationContext),
					applicationContext, getWelcomePage(),
					this.mvcProperties.getStaticPathPattern());
		}
		
		//获得欢迎页面
		private Optional<Resource> getWelcomePage() {
			String[] locations = getResourceLocations(
					this.resourceProperties.getStaticLocations());
			return Arrays.stream(locations).map(this::getIndexHtml)
					.filter(this::isReadable).findFirst();
		}
		
		//发现获取欢迎页面的名字就是index.html,路径是从location中去获取,location恰好又是resourceProperties中的staticLocations属性的值
		//通过继续查看发现locations的值就是:"classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/" 
		private Resource getIndexHtml(String location) {
			return this.resourceLoader.getResource(location + "index.html");
		}

继续看WelcomPageHandlerMapping构造器:

    WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
			ApplicationContext applicationContext, Optional<Resource> welcomePage,
			String staticPathPattern) {
		if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
			logger.info("Adding welcome page: " + welcomePage.get());
			setRootViewName("forward:index.html");//跳转到index.html
		}
		else if (welcomeTemplateExists(templateAvailabilityProviders,
				applicationContext)) {
			logger.info("Adding welcome page template: index");
			setRootViewName("index");//设置逻辑视图为index
		}
	}

通过分析欢迎页面的源代码可以发现:

  • springboot设置的欢迎页面请求路径也是以下目录去 classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/ 找index.html

1.3 SpringBoot对图标ico资源的映射

同样,我们通过查看WelcomPageHandlerMapping源码发现,在该类中有一个嵌套内部类:

        @Configuration
		@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
		public static class FaviconConfiguration implements ResourceLoaderAware {

			private final ResourceProperties resourceProperties;

			private ResourceLoader resourceLoader;

			public FaviconConfiguration(ResourceProperties resourceProperties) {
				this.resourceProperties = resourceProperties;
			}

			@Override
			public void setResourceLoader(ResourceLoader resourceLoader) {
				this.resourceLoader = resourceLoader;
			}
            
            //给容器中SimpleUrlHandlerMapping映射器,并给该映射器设置ico的映设规则
			@Bean
			public SimpleUrlHandlerMapping faviconHandlerMapping() {
				SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
				mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
				mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
						faviconRequestHandler()));
				return mapping;
			}
            
            //首先给容器中添加ResourceHttpRequestHandler对象,并给该资源请求对象设置一个请求路径为下一个方法获取的路径
			@Bean
			public ResourceHttpRequestHandler faviconRequestHandler() {
				ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
				requestHandler.setLocations(resolveFaviconLocations());
				return requestHandler;
			}
            //获取ico图标的解析路径
            //阅读该方法,发现该方法的解析路径也是从staticLocations中获取的
			private List<Resource> resolveFaviconLocations() {
				String[] staticLocations = getResourceLocations(
						this.resourceProperties.getStaticLocations());
				List<Resource> locations = new ArrayList<>(staticLocations.length + 1);
				Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
						.forEach(locations::add);
				locations.add(new ClassPathResource("/"));
				return Collections.unmodifiableList(locations);
			}

		}

	}

通过上面的代码,我们可以发现:

  • 只要在staticLocations也就是静态资源路径中有favicon.ico的图标都会被springboot解析为网页title中的图标。

上面说了关于SpringBoot整合WEB应用给静态资源做了一些默认配置,那么除了静态资源,SpringBoot还给我们做了那些默认配置呢?下面继续讲解

2. 视图解析器

通过继续阅读SpringBoot的WebMvcAutoConfiguration源代码,可以发现,SpringBoot给我们默认配置了三种视图解析器:

        @Bean
		@ConditionalOnMissingBean
		public InternalResourceViewResolver defaultViewResolver() {
			InternalResourceViewResolver resolver = new InternalResourceViewResolver();
			resolver.setPrefix(this.mvcProperties.getView().getPrefix());
			resolver.setSuffix(this.mvcProperties.getView().getSuffix());
			return resolver;
		}

上面的代码就和springmvc配置InternalResourceViewResolver视图去一样:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 路径前缀 -->
    <property name="prefix" value="/jsp/"></property>
    <!-- 路径后缀 -->
    <property name="suffix" value=".jsp"></property>
</bean>
        @Bean
		@ConditionalOnBean(View.class)
		@ConditionalOnMissingBean
		public BeanNameViewResolver beanNameViewResolver() {
			BeanNameViewResolver resolver = new BeanNameViewResolver();
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
			return resolver;
		}

关于BeanNameViewResolver的配置和springmvc配置文件中配置一样:

<!-- ****** 配置视图 BeanNameViewResolver 解析器: 使用视图的名字来解析视图 ************ --> <!-- 通过 order 属性来定义视图解析器的优先级, order 值越小优先级越高 --> 
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"> 
    <property name="order" value="10"></property> 
</bean>
        @Bean
		@ConditionalOnBean(ViewResolver.class)
		@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
		public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
			ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
			resolver.setContentNegotiationManager(
					beanFactory.getBean(ContentNegotiationManager.class));
			// ContentNegotiatingViewResolver uses all the other view resolvers to locate
			// a view so it should have a high precedence
			resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
			return resolver;
		}

关于ContentNegotiatingViewResolver的配置和springmvc配置文件中配置一样:

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolve">
        <property name="order" value="1" />
        <property name="favorParameter" value="false" />
        <property name="ignoreAcceptHeader" value="true" />
        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
                <entry key="xml" value="application/xml" />        
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"></bean>
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                    <constructor-arg>
                        <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
                             <property name="classesToBeBound">
                                <list>
                                   <value>com.model.User</value>
                                </list>
                             </property>
                        </bean>
                    </constructor-arg>
                </bean>
            </list>
        </property>
    </bean>

3.自己配置

上面通过分析源码发现SpringBoot给我们做了许多的配置,当然也只分析了下静态资源和视图解析器的配置。当然SpringBoot还给我们做了许多的配置,例如:转换器、拦截器、格式化等其他配置。关于这些配置,我们就不再继续分析下去啦!

通过上面的源码分析了一部分,我们发现SpringBoot给我做了所有的配置,如果我们觉得SpringBoot做的配置与我们的需求不合,那么我们怎么去修改Springboot的默认配置呢?

关于修改SpringBoot的默认配置有两种方式:方式一:增加自己的配置,并然自己的配置和SpringBoot提供的默认配置一同起作用。方式二:通过自己的配置来完全接管SpringBoot关于Web的默认配置。

3.1 增加自己的配置

自己定义一个WebMvcConfigurer类型的的配置类

例子:如果我们想给项目中添加一个拦截器:

@Configuration
public class MyConfig implements WebMvcConfigurer {

    //重写WebMvcConfigurer中的方法
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/","/index.html","/login");
    }
}

上面这种方式就是采用自定义一个配置类和SpringBoot提供的默认配置共存的方式。那么为什么自己定义的配置类能够和Springboot提供的默认配置共同存在呢?

我们继续分析WebMvcAutoConfiguration,发现其中有一个内部类:

    @Configuration
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter
			implements WebMvcConfigurer, ResourceLoaderAware {

通过这个内部类可以发现WebMvcAutoConfigurationAdapter是SpringBoot提供的一个默认实现了WebMvcConfigurer的配置类,并且我们发现该配置类需要通过EnableWebMvcConfiguration导入一些组件。我们继续分析EnableWebMvcConfiguration发现该类也是WebMvcAutoConfigurationAdapter的内部类:

@Configuration
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

发现该类继承了一个DelegatingWebMvcConfiguration类,进一步发现该类有一个方法:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    public DelegatingWebMvcConfiguration() {
    }

    @Autowired(
        required = false
    )
    //从容器中加入所有WebMvcConfigurer类型的配置类
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }

    }

我们发现setConfigurers方法就是从容器中加载所有的WebMvcConfigurer类的类进来。这样就可以让自定义的配置类和SpringBoot默认提供的默认配置同时都生效啦

3.2 覆盖SpringBoot提供的默认配置

如果用户需要自定义的配置类覆盖SpringBoot提供的默认配置,可以在自定义的配置类中添加一个注解:@EnableWebMvc

@EnableWebMvc //如果添加了该注解,该配置类就会完全接管SpringBoot对WebMVC的配置,
//那么,当启动该的时候,我们就再也访问不到静态资源,
//因为,改配置类并没有给访问静态资源做任何配置
@Configuration
public class MyConfig implements WebMvcConfigurer {

    //重写WebMvcConfigurer中的方法
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/","/index.html","/login");
    }
}

那么为什么加了@EnableWebMvc注解就会让默认的配置类不生效呢?我们查看该注解的源码:

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

通过该源码,我们发现该注解给容器中导入了一个DelegatingWebMvcConfiguration的主件。,该主件又继承了一个WebMvcConfigurationSupport

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

然而在SpringBoot提供的默认配置类中,明确限定了如果容器中有该主件,默认配置类就不生效。

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

总结

通过上面的分析,我们发现SpringBoot给我们做了许多的默认配置,如果我们需要修改SpringBoot提供的一些默认配置或者增加一些配置,可以自己创建一个XXXConfigurer类型的配置类加入到Spring容器中,这样就可以自定义一些配置。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值