如何用springboot进行web开发?
springboot给我们做了哪些自动配置呢?下面就来依次展开。
关于springboot给我们做了哪些自动配置,我们可以去看jar包中的:xxxAutoConfiguration、和xxxProperties。
使用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默认配置是/**
通过继续查看springboot关于springmvc的自动配置源码WebMvcAutoConfiguration
发现,springboot也为我们进行了欢迎页的配置:
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext),
applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
}
//获得欢迎页面
private Optional 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 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
}
}
通过分析欢迎页面的源代码可以发现:
classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/
找index.html同样,我们通过查看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 resolveFaviconLocations() {
String[] staticLocations = getResourceLocations(
this.resourceProperties.getStaticLocations());
List locations = new ArrayList<>(staticLocations.length + 1);
Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
.forEach(locations::add);
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
}
}
通过上面的代码,我们可以发现:
上面说了关于SpringBoot整合WEB应用给静态资源做了一些默认配置,那么除了静态资源,SpringBoot还给我们做了那些默认配置呢?下面继续讲解
通过继续阅读SpringBoot的WebMvcAutoConfiguration
源代码,可以发现,SpringBoot给我们默认配置了三种视图解析器:
InternalResourceViewResolver
试图解析器的使用,可以参考:https://blog.csdn.net/sinat_35821285/article/details/79094925 @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>
BeanNameViewResolver
试图解析器的使用,可以参考:https://blog.csdn.net/j080624/article/details/56485939 @Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
关于BeanNameViewResolver
的配置和springmvc配置文件中配置一样:
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="10">property>
bean>
ContentNegotiatingViewResolver
试图解析器的使用,可以参考:https://blog.csdn.net/z69183787/article/details/41654603 @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.Uservalue>
list>
property>
bean>
constructor-arg>
bean>
list>
property>
bean>
上面通过分析源码发现SpringBoot给我们做了许多的配置,当然也只分析了下静态资源和视图解析器的配置。当然SpringBoot还给我们做了许多的配置,例如:转换器、拦截器、格式化等其他配置。关于这些配置,我们就不再继续分析下去啦!
通过上面的源码分析了一部分,我们发现SpringBoot给我做了所有的配置,如果我们觉得SpringBoot做的配置与我们的需求不合,那么我们怎么去修改Springboot的默认配置呢?
关于修改SpringBoot的默认配置有两种方式:方式一:增加自己的配置,并然自己的配置和SpringBoot提供的默认配置一同起作用。方式二:通过自己的配置来完全接管SpringBoot关于Web的默认配置。
自己定义一个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 configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
我们发现setConfigurers方法就是从容器中加载所有的WebMvcConfigurer类的类进来。这样就可以让自定义的配置类和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容器中,这样就可以自定义一些配置。