Spring Boot注解的原理(含有自动配置原理)
@SpringBootApplication注解
由以下几个组件组成:
@SpringBootConfiguration(和configuration的作用一样并会将当前类内声明的一个或多个以
)
@EnableAutoConfiguration开启自动配置--该注解中有一个@Import(AutoConfigurationImportSelector.class),而其中发挥自动配置作用就是AutoConfigurationImportSelector类。
观察AutoConfigurationImportSelector类
List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//SpringFactoriesLoader是用来加载自动配置的类的 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; }
关于SpringBoot自动配置的原理:
在使用一个公用的 starter 的时候,只需要将相应的依赖添加到Maven 的配置文件当中即可,免去了自己需要引用很多依赖类,并且 SpringBoot 会自动进行类的自动配置
SpringBoot在启动的时候就会到AutoConfigurationImportSelector这个类里面
SpringFactoiesLoader会扫描依赖的jar包中的META-INF/spring.factoies文件,该文件的格式都是key=value的格式。
key是一个接口的名字,value是实现类的名字,如果value有多个值,则用逗号分隔。
而我们上面提到的@EnableAutoConfiguration也是利用这个原理去扫描所有的自动配置类,以该注解的全限定类名作为key,所有的默认配置类为值。
SpringFactoiesLoader内部有一个静态的双层ConcurrentMap,用来存储这些key-value,第一层map的key是classloader,springboot默认的classloader是appClassLoader,value则是一个MultiValueMap,是spring自己实现的一个hashmap。而这个MultiValueMap的key就是spring.factoies文件中的key,value是一个list,即spring.factoies文件中的value。SpringFactoiesLoader利用缓存机制,只在第一次扫描所有的META-INF/spring.factoies文件时,就把所有的key-value都加载到这个map中,后面再进从中获取值时,直接从map中取就可以了,就不需要再重新扫描了。
@Conditional注解的条件,进行自动配置并将 Bean 注入 Spring Context 上下文当中。
@ImportAutoConfiguration({MyServiceAutoConfiguration.class}) 指定自动配置哪些类。指定配置自己的类。
springboot初始化过程中主要是扫描两个spring.factoies文件,其中定义的key-value中的类,是整个启动过程中要用到的。一个是spring-boot-2.0.3.RELEASE中的,一个是spring-boot-autoconfigure包中的。这两个jar包中的文件定义整个启动流程中要执行的所有默认配置好的类。
@ComponentScan
默认扫描当前包及其子包下面被@component,@Controller,@Service,@Repository标记标注的类然后纳入spring管理容器中去
@ResponseBody
表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,用于构建RESTful的api。在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@Responsebody后,会直接返回json数据。该注解一般会配合@RequestMapping一起使用。示例代码:
@RestController:
是@Controller和@ResponseBody的合集,表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器。
@RequestMapping:
提供路由信息,负责URL到Controller中的具体函数的映射。
关于RequestMapping:
其中的一些属性:
value:请求的实际地址
method:get,post请求等
consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces:指定返回类型
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。 映射请求的标头,缩小主映射。
params:
@RequestMapping(value = "/pets", method = RequestMethod.GET, headers="Referer=http://www.ifeng.com/") 表示:仅处理request的header中包含了指定“Refer”请求头和对应值为“http://www.ifeng.com/”的请求;
@RequestMapping加到接口上也会生效:
启动时在向容器注入Controller的Bean(HandlerAdapter)时做了处理。controller类通过反射获取到接口的注解信息,并组装到Controller的Bean中。
https://blog.csdn.net/forezp/article/details/80069961
关于RequestMapping的整个过程:
在springBoot项目启东时候,扫描controller会把@RequestMapping的方法也加入到一个集合,放入到上下文环境中。
将类层次的RequestMapping和方法级别的RequestMapping结合 (createRequestMappingInfo)
当请求到达时,去urlMap中需找匹配的url,以及获取对应mapping实例,然后去handlerMethods中获取匹配HandlerMethod实例。
发起请求后的执行流程是:检查request类型-->获取匹配的Handlemethod-->查找拦截器-->组成HandlerExecutionChain执行链-->获取方法执行链对象的适配器(HandlerAdapter)-->然后反射执行业务方法。
HandlerExecutionChain getHandler(HttpServletRequest request);
请求来的时候进入doDispatch方法,只简述一下里面的几个方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
processedRequest = checkMultipart(request);
...
mappedHandler = getHandler(processedRequest);//获取匹配的执行链
...
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//根据匹配到的执行链对象,获取合适的适配器
}
HandlerExecutionChain getHandler(HttpServletRequest request);
HandlerMapping 是由 DispatcherServlet 调用,DispatcherServlet 会从容器中取出所有 HandlerMapping 实例并遍历,让 HandlerMapping 实例根据自己实现类的方式去尝试查找 Handler,而 HandlerMapping 具体有哪些实现类下面就会详细分析。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 这些 HandlerMapping 在容器初始化时创建,在 initHandlerMappings 时放入集合中 for (HandlerMapping hm : this.handlerMappings) { HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
https://www.cnblogs.com/tengyunhao/p/7658952.html 还需要经常看
https://www.cnblogs.com/javammc/p/8600509.html
@Qualifier:
当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。比如加service注解,在serviceImp需要加名字@service("userService")
与@Autowired配合使用,在接口有多个实现类的时候通过这个指定实现类。
@Autowired和@Resource(这两个功能是一样的)
都是注入Bean用的,Autowired默认按类型装配,如果需要按名称装配,需要配合Qualifier,这个是spring框架提供的
Resource默认按名称进行装配,通过name属性进行指定,这个是jdk提供的注解,如果@Resource不指定name就会退化成@Autowired
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10673019.html