springboot原理
未使用springboot时,怎样构建web项目(ssm架构的)
导入web及其配置,spring,springmvc
在web.xml中配置dispatchServlet(前端控制器,作为分发器的作用)
配置包扫描路径,让我们的service层,controller层等里面的对象被spring容器进行管理
编写controller类
部署Tomcat
使用了springboot搭建web项目时
- 编写controller类
- 在项目根目录下新建一个类(如:App),作为配置类,在其上面贴上@SpringBootApplication注解
- 在App类里面启动方法
SpringApplication.run(..)的作用:启动springBoot应用;加载自定义的配置类,完成自定义的功能;把当前项目部署到嵌入的Tomcat;启动Tomcatpublic static void main(String[] args) { SpringApplication.run(App.class, args); }
- springboot方式不需要我们配置web,dispatchServlet,Tomcat,以及包扫描路径等
springboot对比传统方式简化的原理是什么?
- 因为在App类上面贴了@SpringBootApplication注解,该注解本身又被贴了以下三个核心注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
- 因为被贴了@SpringBootConfiguration这个注解,所以该App类就成为了配置类,
一般springBoot项目只有一个启动类,启动类都是配置类
- 因为贴了@ComponentScan这个注解,所有具有了扫描包的功能,它的功能就是扫描当前类所在包及其子包所有贴了版型注解的类,并创建出对象交给spring管理,所以我们贴@controller,@service等注解的类并没有做额外配置,就可以直接从spring中拿,就是因为有该@ComponentScan注解,
所以启动动类(贴了@SpringBootApplication注解的类)建议都是放在项目根包下
- @EnableAutoConfiguration:启用自动配置,这个注解的意思是根据你加载的依赖去猜测你需要怎样配置spring,(springBoot收集了很多第三方jar依赖,根据jar依赖的特点,springBoot对这些jar依赖作了进一步的分类,整合,再封装形成一个新的依赖工具集,每个工具集都是用来解决特定领域问题的,springBoot就把这些工具集称为启动器,所以spring-boot-starter-web这个也就是一个启动器(web项目的启动器),里面装了很多和web项目相关的依赖比如spring-webmvc,starter-tomcat等),因为我们配置了
spring-boot-starter-web
这个启动器,所以springBoot就知道我们是一个web项目,其底层就帮我们配置好了dispatchServlet, - 那@EnableAutoConfiguration注解的具体实现原理是什么呢?
- 在该注解里面又有
@Import(AutoConfigurationImportSelector.class)
这么一个注解(自动配置导入选择器),该类(AutoConfigurationImportSelector)里面有一个方法List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
这个方法是批量加载配置类的 - 点击getCandidateConfigurations方法进入这里
SpringFactoriesLoader就是去加载protected List<String> getCandidateConfigurations( AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META- INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
maven:org.springframework.boot:spring-boot-autoconfigure下面的META- INF/spring.factories
而Assert.notEmpty断言表示当configurations为空时就证明没有自动加载META- INF/spring.factories
里的自动配置类,注意:不管是spring定制的启动器,还是第三方定制的启动器,都需要编写META�INF/spring.factories,里面指定启动器的自动配置类
. - 在
META- INF/spring.factories
里面写了很多自动加载类,难道每一个我们都会加载到spring容器中吗?只有满足条件的配置类才会被加载到spring容器中 - 拿
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
举例,点击进入WebMvcAutoConfiguration,会发现它上面贴了如下注解@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
- @Configuration表示这也是一个配置类
- @ConditionalOnWebApplication(type = Type.SERVLET)这个注解表示在type为servlet这种条件下当前类的配置才生效(0n表示在什么条件下),因为我们一开始就配置了web启动器,所以我们是一个web项目,是满足当前这个条件的
- ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })表示必须有Servlet,DispatcherServlet,WebMvcConfigurer类当前配置才生效,这里就是判断你是否引入了SpringMVC相关的依赖
- @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)表示当我们没有主动配置WebMvcConfigurationSupport这个bean时,当前类的配置才会生效,意思就是说如果我们主动配置了WebMvcConfigurationSupport这个类型的bean,那么当前类的配置就会不生效,我们点击WebMvcConfigurationSupport进去看下,如果我们自己没有配置符合当前条件,springBoot会在WebMvcConfigurationSupport里面帮我们做什么事情呢?
主要是配置了视图解析器@Bean @ConditionalOnMissingBean public InternalResourceViewResolver defaultViewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix(this.mvcProperties.getView().getPrefix()); resolver.setSuffix(this.mvcProperties.getView().getSuffix()); return resolver; } @Bean @ConditionalOnBean(View.class) @ConditionalOnMissingBean public BeanNameViewResolver beanNameViewResolver({ BeanNameViewResolver resolver = new BeanNameViewResolver(); resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10); return resolver; }
- 方法 @AutoConfigureAfter这个注解表示在指定的类加载完了后,再加载本类(即先加载DispatcherServletAutoConfiguration类,再加载当前类WebMvcAutoConfiguration),而DispatcherServletAutoConfiguration这个类里面又配置了很多东西,包括前端控制器,代码如下
所以我们便得到了前端控制器,此时只差一个Tomcat我们便能启动项目了@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails()); return dispatcherServlet; }
- Tomcat:springboot使用嵌入式tomcat,编程实现,默认端口是8080,后续可以通过 application.properties文件进行修改
- 在该注解里面又有
springBoot优缺点
- 优点:
- 创建独立运行的spring应用程序
- 内嵌的猫,不用再打war包(如果不是用的springBoot那么用到Tomcat要打war包,不然Tomcat用不了)
- 不用搞一堆的spring配置,自动进行spring配置(如视图解析器,aop等等)
- 不用xml配置
- 很方便集成第三方jar依赖
- 简化maven(springBoot都重新整合了一个个的工具集,例如web项目直接导入一个web启动器,相应的依赖就都帮我们导入了)
- 提供了日志,健康检查,外部配置等生产就绪功能
- 缺点:
- 版本更新快,可能会出现较大变化
- 因为约定大于配置,所以可能出现一些不好解决的问题
application.properties优先级
-
一个项目中可以有多个application.properties文件存放在不同目录中,此时他们会遵循固定的优先级来处理有冲突的属性配置, 优先级由高到底,高优先级的配置会覆盖低优先级的配置
- 项目/config/application.properties
- 项目/application.properties
- classpath:config/application.properties1
- classpath:application.properties2(calsspath就是指Resources)
一般都在classpath:application.properties做配置,其他方式不使用
WEB集成相关
- Resources下面的static就相当于以前web-app的根目录用于存放静态资源( 默认情况下,Springboot会从classpath下的 /static , /public , /resources , /META-INF/resources下加载静态资源)
- templates用于存放模板文件,如ftl或jsp
- 可以在application.properties中配置spring.resources.staticLocations属性来修改静态资源加载地址,但一般不建议修改
- 因为应用是打成jar包,所以之前的src/main/webapp就作废了,如果有文件上传,那么就的必须去配置图片所在的路径
- 因为springBoot通过WebMvcAutoConfiguration自动配置类配置了dispatchServlet,其默认路径为/(<
url-pattern >是 /),所以如果我们还想使用localhost/list.do这种.do的形式,那么就需要在application.properties中配置spring.mvc.pathmatch.use-suffix-pattern=true
(这个是表示在匹配模式时是否使用后缀模式匹配) - 配置freemarker时,除了导入依赖外,以下三个配置我们要配上
#暴露session对象的属性
spring.freemarker.expose-session-attributes=true
#配置为传统模式,空值自动处理 spring.freemarker.settings.classic_compatible=true
#重新指定模板文件后缀 springboot 2.2.x 后 默认后缀为 .ftlh
spring.freemarker.suffix=.ftl
- springBoot自带了异常处理,SpringBoot默认情况下,会把所有错误都交给BasicErrorController类完成处理,错误的视图导向到classpath:/static/error/ 和 classpath:/templates/error/ 路径上,http状态码就是默认视图的名称,注意这里一定要是在error文件夹下,且必须是404.html(必须是html才有用),出现5xx类错误 ->
classpath:/static/error/5xx.html(名字必须是5xx.html) - 针对不同的需要我们还可以自定义异常
//控制器增强器 @ControllerAdvice public class ExceptionControllerAdvice { @ExceptionHandler(RuntimeException.class)//处理什么类型的异常 public String handleException(Exception e, Model model, HandlerMethod method){ /*这里就是和普通的 @RequestMapping("/list")注解下的方法一样用,即可以返回json类型的数据 还可以直接返回到指定的ftl页面,通过method可以拿到检测的控制器方法上面是否贴了json类型的注解@ResponseBody 从而进行json返回数据和返回页面的不同处理 */ return "errorView"; } }
- 我们可以自定义拦截器,实现下HandlerInterceptor接口即可,如下:
以前我们自定义拦截器后还需要在< mvc:interceptors >标签中去注册我们的拦截器,用了springBoot后,则只需让启动类实现WebMvcConfigurer接口,并调用addInterceptors方法即可;@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); if (uri.contains("list")) { response.sendRedirect("/login.html"); return false; } return true; } }
@SpringBootApplication @MapperScan("cn.sushen.mapper") //实现WebMvcConfigurer接口,调用addInterceptors方法配置拦截器 public class App implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { /* 1.第一个参数表示注册哪一个拦截器 2.addPathPatterns表示要拦截的路径是哪些 3.excludePathPatterns不用拦截的是哪些 */ registry.addInterceptor(loginInterceptor). addPathPatterns("/**"). excludePathPatterns("/static/**","/*/list"); } public static void main(String[] args) { SpringApplication.run(App.class,args); } }