Spring Boot 学习笔记(五)—— Web开发

我们说引入spring-boot-starter-web场景启动器后,就会给自动配置web,那到底默认配置了什么呢?

我们以WebMvcAutoConfiguration为例进行讲解

@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 {

    //主要就是看这个内部静态类,spring-mvc.xml需要配置的内容基本都写在这里面的,具体自己进去看
    @Configuration
    @Import(EnableWebMvcConfiguration.class)
    @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
    public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
    	
    	@Override
    	//静态资源配置
		public void addResourceHandlers(ResourceHandlerRegistry registry) {
    		
    	}
    	
    	//欢迎页配置
    	@Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(ResourceProperties resourceProperties) {

		}
    }
    
    //页面图标配置
    @Configuration
	@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
	public static class FaviconConfiguration {
	
	}
}

静态资源加载位置按优先级从高到低依次为:classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/;只要放在这里面的静态资源,都可以通过http://xxx:xxx/abc.png(前提没有@RequestMapping("abc"));可以通过在spring.resources=xxx,xxx修改静态资源的默认位置

如果在静态资源下创建index.html页面即为欢迎页,创建favicon.ico即为页面图标;关于什么是欢迎页、页面图标就不用我多说了吧!

更多springmvc的自动配置参考官网:https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration

如何修改SpringBoot的默认配置

1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(例如ViewResolver)将用户配置的和自己默认的组合起来;

2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置,例如增加拦截器

<!-- 这是spring-mvc.xml 的写法 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <mvc:exclude-mapping path="/person.do"/>
        <bean class="com.example.demo.DemoInterceptor"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/admin/**" />
        <mvc:exclude-mapping path="/admin/login.do"/>
        <bean class="com.example.demo.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>
package com.example.demo;

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

@Configuration //这里等价于spring-mvc.xml
public class WebConfig {
	
	/*
	 *  把DemoInterceptor注册到容器中后,添加拦截器就可以不用new DemoInterceptor()
	 *	registry.addInterceptor(demoInterceptor()).addPathPatterns("/**").excludePathPatterns("/person");
	@Bean //这里等价于<bean class="com.example.demo.DemoInterceptor"></bean>
	public DemoInterceptor demoInterceptor() {
		return new DemoInterceptor();
	}
	*/

    //也可以注册一个WebMvcConfigurerAdapter的bean,该类是抽象类,直接在这里匿名实现
    //该类implements WebMvcConfigurer,这就是我们说的xxxConfigurer;我们注册的这个bean会和自动配置的WebMvcConfigurer一起生效,形成互补
    //因为WebMvcConfigurer需要实现的方法太多了,所以用了WebMvcConfigurerAdapter
	@Bean
	public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
		
		WebMvcConfigurerAdapter webMvcConfigurerAdapter = new WebMvcConfigurerAdapter() {

			@Override //重写添加拦截器
			public void addInterceptors(InterceptorRegistry registry) {
				//这里我是new DemoInterceptor(),也可以把DemoInterceptor注册一个bean,就不用new了
				registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**").excludePathPatterns("/person");
				//可以添加多个拦截器
				registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/admin/**").excludePathPatterns("/admin/login");
			}
			
		};
		
		return webMvcConfigurerAdapter;
		
	}
	
	
}

或者这样配置也是可以的

@Configuration
public class SpringMvcConfig extends WebMvcConfigurerAdapter {
	
		@Override
		public void addInterceptors(InterceptorRegistry registry) {
			
		}
}

全面接管SpringMVC

SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了

@EnableWebMvc   //添加此注解全面接管springmvc配置
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

}

为什么加上@EnableWebMvc自动配置就失效了;

//进入@EnableWebMvc 在进入@Import(DelegatingWebMvcConfiguration.class)
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

}

回过头来看看自动配置生效条件

//当WebMvcConfigurationSupport不存在时,自动配置才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 
public class WebMvcAutoConfiguration {

}

建议不要全面接管springmvc,尽量使用自动配置+自己的配置,形成互补

3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置,这里我们以EmbeddedServletContainerCustomizer 为例来修改servlet容器的端口号

@Configuration
public class MainConfig {
	
	@Bean  //EmbeddedServletContainerCustomizer是一个接口
	public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
	    return new EmbeddedServletContainerCustomizer() {
	        @Override
	        public void customize(ConfigurableEmbeddedServletContainer container) {
	            container.setPort(8083); 
	        }
	    };
	}
}

在application.properties 中配置的server.port=8080 实质也是实现的EmbeddedServletContainerCustomizer,详见org.springframework.boot.autoconfigure.web.ServerProperties

SpringBoot默认的错误处理机制

当我们发生错误时,springboot会默认给我们显示一个空白页或json输出

这是因为ErrorMvcAutoConfiguration为我们做了自动配置

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
// Load before the main WebMvcAutoConfiguration so that the error View is available
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(ResourceProperties.class)
public class ErrorMvcAutoConfiguration {

	//主要看下面几个组件的逻辑
	
	@Bean
	@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
	public DefaultErrorAttributes errorAttributes() {
		
	}
	
	@Bean
	@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
	public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
		
	}
	
	@Bean
	public ErrorPageCustomizer errorPageCustomizer() {
		
	}
	
	@Configuration
	static class DefaultErrorViewResolverConfiguration {

		@Bean
		@ConditionalOnBean(DispatcherServlet.class)
		@ConditionalOnMissingBean
		public DefaultErrorViewResolver conventionErrorViewResolver() {
			
		}

	}

}

如何定制错误页面?

我们可以修改server.error.path属性,默认为/error;如果静态资源文件路径或模板引擎路径下午有error/状态码.html(例如404.html,精确匹配优先)或4xx.html(模糊匹配,5xx.html),就会使用对应的html进行输出显示,否则就会使用默认输出

如何定制json输出?

@ControllerAdvice
public class MyExceptionHandler {

    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)
    public Map<String,Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        return map;
    }
}

如何自适应是该输出页面还是json?

利用springboot的server.error.path=/error以及ErrorMvcAutoConfiguration自动配置原理实现

@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e,HttpServletRequest request){
    	//设置错误响应码,不设置这句话的话,就不会跳转到500.html或5xx.html,否则仍然是默认页输出
    	request.setAttribute("javax.servlet.error.status_code",500);  
    	request.setAttribute("customInfo","自定义信息")
        return "forward:/error";    
    }
}
//另外还要给容器中加入我们自己定义的ErrorAttributes,否则仅会输出默认值
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
        //request.setAttribute("customInfo","自定义信息")的值
        requestAttributes.getAttributes("customInfo",0) //第二个参数是从哪个scope中取,0表示request,具体可以点进方法看
        
        map.put("more","追加更多错误信息"); //还可以在这里追加更多信息
        
        return map;
    }
}

但是这种方式有个问题,虽然追加了我自定的内容,但是我不想要timestamp、status、error等这些默认信息该怎么办?

请参考我这篇文章:https://blog.csdn.net/q42368773/article/details/104036753

注册Servlet三大组件【Servlet、Filter、Listener】

//使用xxxRegistrationBean注册三大组件
@Configuration
public class MainConfig {
	
	@Bean
	public ServletRegistrationBean myServlet(){
	    ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
	    return registrationBean;
	}
	
	@Bean
	public FilterRegistrationBean myFilter(){
	    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
	    registrationBean.setFilter(new MyFilter());
	    registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
	    return registrationBean;
	}
	
	@Bean
	public ServletListenerRegistrationBean myListener(){
	    ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
	    return registrationBean;
	}
}

注意:如果想使用@WebServlet、@WebFilter、@WebListener配置三大组件,则需要将该类加入到容器中,否则不会生效。或者直接在启动类上加入@ServletComponentScan

@WebFilter("/*")
@Component //将组件加入到容器中
public class MyFilter implements Filter {

}
@SpringBootApplication
@ServletComponentScan  //或者在启动类中加入该注解
public class DemoApplication {

}

如何使用外置Tomcat容器启动SpringBoot应用程序

	<!-- 项目设置为war包 -->
	<packaging>war</packaging>
	
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!-- 重写spring-boot-starter-tomcat的scope属性 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
	</dependencies>
//创建一个类(名字可以随便取)继承SpringBootServletInitializer
public class ServletInitializer extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(DemoApplication.class); //参数为springboot的启动类
	}

}

 

最后使用外置的Tomcat启动程序,此时使用springboot启动类启动无效;如果需要使用jsp,则需要添加jstl的依赖和设置视图解析器

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>
spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp

 如果只是想使用JSP,不使用外置Tomcat,则可以不用创建ServletInitializer ;但是项目必须为war包,打包后启动命令:java -jar spring-boot-demo.war 运行程序

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值