springboot 启动排除某些bean 的注入

springboot 启动排除某些bean 的注入,第1张

问题:

最近做项目的时候,需要引入其他的jar。然后还需要扫描这些jar里的某些bean。于是使用注解:@ComponentScan

这个注解直接指定包名就可以,它会去扫描这个包下所有的class,然后判断是否解析:

@ComponentScan(basePackages = {"yourpkg", "otherpkg"})

public class Application {

} 

其他的jar中定义了 redissonConfig 这个bean。然后我自己的项目也定义了redissonConfig 这个bean。导致项目启动报错。所以使用如下方式,排除jar 中的RedissonConfigclass。

@ComponentScan(basePackages = {"comxxxx"}, excludeFilters = @ComponentScanFilter(type = FilterTypeASSIGNABLE_TYPE, classes = {RedissonConfigclass}))

@ComponentScan注解。扫描或解析的bean只能是Spring内部所定义的,比如@Component、@Service、@Controller或@Repository。如果有一些自定义的注解,比如@Consumer、这个注解修饰的类是不会被扫描到的。这个时候我们就得自定义扫描器完成这个 *** 作。

配置文件中使用的:component-scan标签底层使用ClassPathBeanDefinitionScanner这个类完成扫描工作的。@ComponentScan注解配合@Configuration注解使用,底层使用ComponentScanAnnotationParser解析器完成解析工作。

通过AbstractApplicationContext#refresh()提供了在加载完配置文件后的启动过程。以SpringBoot的Web环境来看启动过程(文末有spring容器的启动过程及区别)

1prepareRefresh():由于在此时还没有加载servlet容器,所以servletContext=null,servletConfig=null,还没有进行初始化propertySource,添加了几个ApplicationListeners。

2obtainFreshBeanFactory():创建BeanFactory,即DefaultListableBeanFactory

3prepareBeanFactory():对DefaultListableBeanFactory进行属性填充。如:添加类加载器,添加BeanPostProcessor等等。

4postProcessBeanFactory():在上面prepareBeanFactory()公共的之后,提供子类的特殊处理,注册特殊的后处理器,此处为ServletWebServerApplicationContext的具体实现。

5invokeBeanFactoryPostProcessors():主要是扫描包,注册成BeanDefinition,对类配置的信息解析成BeanDefinition的属性。然后执行实现了BeanFactoryPostProcessor接口的bean的postProcessBeanFactory()方法。

对于BeanDefinitionRegistryPostProcessor类型的BeanFactoryPostProcessor,执行postProcessBeanDefinitionRegistry

ConfigurationWarningsApplicationContextInitializer类型的processor,检查包名是否异常

获取类型为BeanDefinitionRegistryPostProcessor并且是PriorityOrdered优先排序类型的beanDefinition

上面获取postProcessorNames的具体实现如下

调用刚获取到的currentRegistryProcessor列表中的bean的postProcessBeanDefinitionRegistry()方法

获取到启动类

对启动类开始进行解析,包装成SourceClass,开始解析

获取到需要扫描的包信息,开始进行解析

解析是否有includeFilters,excludeFilters,lazyInit等属性,解析basePackage,basePackageClasses配置,如果没有,则根据启动类的包名获取,最终获得需要扫描的包。

解析包下所有beanDefinition,遍历并逐个解析。  如果实现了AnnotatedBeanDefinition接口,解析是否有lazy,Primary,DependsOn,Role,Description等注解并对beanDefinition进行相应属性设置。   检查是否在beanDefinitionMap中,如果不存在则将beanDefinition注册到beanDefinitionMap中。

对获取到的包下的beanDefinition进行遍历处理。处理是否import,ImportResource,Bean注解,处理实现的接口。

处理完BeanDefinitionRegistryPostProcessor类型的bean之后,处理之前注册的处理器,前两个处理器没做任何处理,第三个处理器ConfigurationClassPostProcessor,对目前加载的所有的beanDefinition进行处理。如果是启动类,生成代理类并替换原来的beanClass

获取实现了BeanFactoryPostProcessor接口的类,去除了上面已经解析过的internalConfigurationAnnotationProcessor,根据是否排序进行分类,然后按照PriorityOrdered,Ordered,nonOrdered以此执行。

6registerBeanPostProcessors():获取实现了BeanPostProcessor的bean,并根据是否实现order接口进行排序,并按顺序注册到beanPostProcessors中。

7initMessageSource():

8initApplicationEventMulticaster():注册事件广播器

9onRefresh():获取ServletWebServerFactory(tomcatServletWebServerFactory),创建TomcatWebServer,然后initServletPropertySources

创建TomcatWebServer之后获取需要初始化的servlet,filter

将servlet添加到servletContext中

将filter添加到servletContext中

10registerListeners():在事件广播器中注册监听器,注册监听器name,如果earlyApplicationEvents存在,则广播事件。

11finishBeanFactoryInitialization():实例化bean

先进行实例化之前的 *** 作,再实例化bean。

如果有实现InstantiationAwareBeanPostProcessor, 则执行postProcessBeforeInstantiation

根据实例化策略进行实例化(这里是cglib)包装成BeanWrapper

实例化之后执行后处理

执行postProcessProperties

属性设置,在AutowiredAnnotationBeanPostProcessor中进行依赖注入

是否实现BeanNameAware,BeanClassLoaderAware,BeanFactoryAware等接口

init之前

调用自定义的init方法,在InitDestroyAnnotationBeanPostProcessor中处理

如果实现了InitializingBean,执行afterPropertiesSet()方法

然后执行init之后的方法

12finishRefresh():主要做的事情是启动tomcat,并发布事件

逐个listener去匹配事件

以下为spring环境启动与web环境启动的相同和不同:

1prepareRefresh():区别在于initPropertySources()的实现不同,spring上下文为AnnotationConfigApplicationContext,web上下文AnnotationConfigServletWebServerApplicationContext

2obtainFreshBeanFactory():相同

3prepareBeanFactory():相同

4postProcessBeanFactory():spring使用的AbstractApplicationContext的默认处理,没有提供实现,web提供了实现注册了特殊的处理器

5invokeBeanFactoryPostProcessors():相同

6registerBeanPostProcessors():相同

7initMessageSource():相同

8initApplicationEventMulticaster():相同

9onRefresh():spring什么都没做,使用的AbstractApplicationContext的默认实现,web提供了创建web容器的过程

10registerListeners():相同

11finishBeanFactoryInitialization():相同

12finishRefresh():spring主要做的事情是发布事件,web容器多了一步启动web容器

综上:web环境和spring环境启动容器流程基本相同,spring默认使用AbstractApplicationContext的实现,web多了properties解析,注册特殊处理器,创建webServer和启动webServer的过程。

初看@SpringBootApplication有很多的注解组成,其实归纳就是一个"三体"结构,重要的只有三个Annotation:

(1)@Configuration注解

(2)@ComponentScan

(3)@EnableAutoConfiguration

从源码中可以知道,最关键的要属@Import(EnableAutoConfigurationImportSelectorclass),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。同时借助于Spring框架原有的一个工具类:SpringFactoriesLoader,@EnableAutoConfiguration就可以实现智能的自动配置。

总结 :@EnableAutoConfiguration作用就是从classpath中搜寻所有的META-INF/springfactories配置文件,并将其中orgspringframeworkbootautoconfigureEnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。这些功能配置类要生效的话,会去classpath中找是否有该类的依赖类(也就是pomxml必须有对应功能的jar包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。

1、从spring-boot-autoconfigurejar/META-INF/springfactories中获取redis的相关配置类全限定名(有120多个的配置类)RedisAutoConfiguration,一般一个功能配置类围绕该功能,负责管理创建多个相关的功能类,比如RedisAutoConfiguration负责:JedisConnectionFactory、RedisTemplate、StringRedisTemplate这3个功能类的创建

2、RedisAutoConfiguration配置类生效的一个条件是在classpath路径下有RedisOperations类存在,因此springboot的自动装配机制会会去classpath下去查找对应的class文件。

3如果pomxml有对应的jar包,就能匹配到对应依赖class,

4、匹配成功,这个功能配置类才会生效,同时会注入默认的属性配置类@EnableConfigurationProperties(RedisPropertiesclass)

5Redis功能配置里面会根据条件生成最终的JedisConnectionFactory、RedisTemplate,并提供了默认的配置形式@ConditionalOnMissingBean(name = "redisTemplate")

6最终创建好的默认装配类,会通过功能配置类里面的 @Bean注解,注入到IOC当中

7用户使用,当用户在配置文件中自定义时候就会覆盖默认的配置@ConditionalOnMissingBean(name = "redisTemplate")

1通过各种注解实现了类与类之间的依赖关系,容器在启动的时候Applicationrun,会调用EnableAutoConfigurationImportSelectorclass的selectImports方法(其实是其父类的方法)-- 这里需要注意,调用这个方法之前发生了什么和是在哪里调用这个方法需要进一步的探讨

2selectImports方法最终会调用SpringFactoriesLoaderloadFactoryNames方法来获取一个全面的常用BeanConfiguration列表

3loadFactoryNames方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigurejar 下面的springfactories),获取到所有的Spring相关的Bean的全限定名ClassName,大概120多个

4selectImports方法继续调用filter(configurations, autoConfigurationMetadata);这个时候会根据这些BeanConfiguration里面的条件,来一一筛选,最关键的是

@ConditionalOnClass,这个条件注解会去classpath下查找,jar包里面是否有这个条件依赖类,所以必须有了相应的jar包,才有这些依赖类,才会生成IOC环境需要的一些默认配置Bean

5最后把符合条件的BeanConfiguration注入默认的EnableConfigurationPropertie类里面的属性值,并且注入到IOC环境当中

1、singleton作用域

当一个bean的作用域设置为singleton,那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时候,spring的IOC容器中只会存在一个该bean。

配置实例:

<bean id="role" class="springchapter2maryGameRole" scope="singleton"/>

或者

<bean id="role" class="springchapter2maryGameRole" singleton="true"/>

2、prototype

prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当于一个new的 *** 作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个 prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)

配置实例:

<bean id="role" class="springchapter2maryGameRole" scope="prototype"/>

或者

<beanid="role" class="springchapter2maryGameRole" singleton="false"/>

3、request

request表示该针对每一次>

(1)过滤器:

依赖于servlet容器,是JavaEE标准,是在请求进入容器之后,还未进入Servlet之前进行预处理,并且在请求结束返回给前端这之间进行后期处理。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤 *** 作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改>

关于过滤器的一些用法可以参考我写过的这些文章:

继承>

在SpringMVC中使用过滤器(Filter)过滤容易引发XSS的危险字符:

(2)拦截器:

拦截器不依赖与servlet容器,依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用spring的依赖注入(DI)获取IOC容器中的各个bean,进行一些业务 *** 作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理,拦截器功在对请求权限鉴定方面确实很有用处

在实际的web应用程序中,经常需要在请求(request)外面增加包装用于:记录调用日志、排除有XSS威胁的字符、执行权限验证等等。除了上述提到的之外,Spring Boot自动添加了OrderedCharacterEncodingFilter和Hidden>

JAVA && Spring && SpringBoot2x — 学习目录

SpringBoot 关于Filter、Servlet、Listener配置—官网

在使用嵌入式容器中(内置Tomcat),类上含有 @WebServlet 、 @WebFilter 和 @WebListener 注解时,可以通过启动类上的 @ServletComponentScan 注解进行扫描。

需要注意的是:@ServletComponentScan 在独立容器中没有任何效果,而是使用容器的内置发现机制。

任何的 Servlet 、 Filter 或者 Listener 实例都是在容器中注册的,可以使用 @Component 或者 @Bean

默认情况下,如果上下文只包含一个Servlet,则将其映射为 / ,在多个Servlet bean的情况下,bean名称用作路径前缀,过滤器映射到 / 。

自定义Filter通过@Bean注解后,被SpringBoot自动注册到容器的Filter chain中,并且拦截路径为 / ,这样导致的结果是:所有的URL都会被自定义的Filter过滤。

可以使用 ServletRegistrationBean 、 FilterRegistrationBean 以及 ServletListenerRegistrationBean 类进行完全的控制。

Spring Boot对Filter、Servlet提供了相应的注册类,来进行精细化的配置,我们可以使用注册类来取消Filter的自动注册。

以上就是关于springboot 启动排除某些bean 的注入全部的内容,包括:springboot 启动排除某些bean 的注入、Spring容器启动流程、SpringBoot自动装配原理等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9278506.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-26
下一篇2023-04-26

发表评论

登录后才能评论

评论列表(0条)

    保存