spring启动过程☆☆☆☆☆☆
Format's Notes《深入理解Spring Cloud与实战》正式开始售卖啦!http://fangjian0423.github.io/
springboot使用自定义的注解增强处理
spring aop(MethodInterceptor), springmvc (HandlerInterceptor), servlet Filter有什么区别?_const伐伐的博客-CSDN博客 了解spring的增强处理和拦截处理
Spring AOP全面详解(超级详细)_springaop_鸨哥学JAVA的博客-CSDN博客
https://www.cnblogs.com/toby-xu/p/11444288.html Spring AOP源码解析
代码如下
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CommonLogger {
}
--------------------------------------
@Component
@Aspect
public class CommonLoggerAdvice implements Ordered {
@Pointcut(value="@annotation(com.example.spring.api.interceptor.CommonLogger)")
private void pointcut(){
}
@Around(value="pointcut()&& @annotation(logging)")
public void doArount(ProceedingJoinPoint joinPoint, CommonLogger logging) throws Throwable {
System.out.println("CommonLogger do around before.................");
joinPoint.proceed();
System.out.println("CommonLogger do around after.................");
}
@Override
public int getOrder() {
return 10;
}
}
----------------------------------
这样就可以使用@CommonLogger注解了
-----------------------------------------------------------
还有一种方式是通过MethodInterceptor方式
在spring boot下有两种方式设置AOP(实现织入weave):
1. 使用@Aspect注解
2. 使用DefaultPointcutAdvisor
以实现TracingInterceptor为例
----------------------------------------------
// 方法1:使用aspectj execution(切点) + interceptor(增强Advice)构成织入(DefaultPointcutAdvisor)
class TracingInterceptor implements MethodInterceptor {
Object invoke(MethodInvocation i) throws Throwable {
System.out.println("method "+i.getMethod()+" is called on "+
i.getThis()+" with args "+i.getArguments());
Object ret=i.proceed();
System.out.println("method "+i.getMethod()+" returns "+ret);
return ret;
}
}
//织入配置类
@Configuration
public class InterceptorConfig {
public static final String traceExecution = "execution(* com.hfi.aop..*.*(..))";
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor2() {
TracingInterceptor interceptor = new TracingInterceptor();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(traceExecution);
// 配置增强类advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(interceptor);
return advisor;
}
}
-----------------------------------------------
方法2:使用自定义注解(切点)+interceptor(增强Advice)构成织入(DefaultPointcutAdvisor)
//自定义注解HfiTrace
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HfiTrace {
}
//interceptor
public class TracingInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
HfiTrace annotation = getAnnotation(method);
if (annotation == null) {
return invocation.proceed();
}
// 为什么调用http://127.0.0.1:8089/jpademo/perform时有两次输出呢?
// 因为在Audience里面用的是@Around,会拦截到两次
System.out.println("method " + invocation.getMethod() + " is called on " + invocation.getThis() + " with args" +
" " + invocation.getArguments());
Object proceed = invocation.proceed();
System.out.println("method " + invocation.getMethod() + " returns " + proceed);
return proceed;
}
private HfiTrace getAnnotation(Method method) {
// 如果有多个annotation 似乎就不好用了 如放在controller上 由于已经有了@RequestMapping注解了 所以...
if (method.isAnnotationPresent(HfiTrace.class)) {
return method.getAnnotation(HfiTrace.class);
}
return null;
}
}
//织入配置类
@Configuration
public class InterceptorAnnotationConfig {
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor3() {
TracingInterceptor interceptor = new TracingInterceptor();
// AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(HfiTrace.class, true);
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPatterns("com.hfi.*");
// 配置增强类advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(interceptor);
return advisor;
}
}
//业务代码
@HfiTrace
@Override
public String perform() {
System.out.println("perform...");
return "perform";
}
---------------------------------------------------------------
//方法3:使用自定义注解(切点)+@Aspect(切面)构成织入
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HfiTrace {
String name() default "默认注解信息";
}
@Component
@Aspect
public class TracingAspect {
@Before("@annotation(test)")
public void beforeTest(JoinPoint point, HfiTrace test){
System.out.println("method " + point.getSignature().getName() + " is called on " + point.getThis() + " with " +
"args" +
" " + point.getArgs());
System.out.println("before invoke: "+ test.name());
}
@AfterReturning(value = "@annotation(test)", returning = "rvt")
public void afterTest(JoinPoint point, HfiTrace test, Object rvt) {
System.out.println("method "+point.getSignature().getName() + " returns " + rvt);
System.out.println("after invoke: " + test.name());
}
}
//业务代码
@HfiTrace
@GetMapping("/perform")
public String perform() {
String perform = performance.perform();
return perform;
}
@HfiTrace(name = "abc")
@GetMapping("/performEncore")
public String performEncore() {
// 强制转换
Encoreable encoreable = (Encoreable) performance;
return encoreable.performEncore();
}
------------------------------------------------------------
或者在配置类里面
Advisor需要配置pointcut和interceptor然后放入到spring容器中
@Configuration
public class CacheAutoConfiguration {
@Bean
public CommonLoggerAdvice commonLoggerAdvice(LoggerMethodInterceptor loggerMethodInterceptor) {
Pointcut pointcut = new AnnotationMatchingPointcut(null, CommonLogger.class);
return new CommonLoggerAdvice(pointcut, loggerMethodInterceptor);
}
}
public class CommonLoggerAdvice extends AbstractPointcutAdvisor {
private Pointcut pointcut;
private Advice advice;
public CommonLoggerAdvice(Pointcut pointcut,Advice advice){
this.pointcut=pointcut;
this.advice=advice;
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this.advice;
}
}
-------------------------------------------------------------------------------
spring有8中pointcut的实现类
1. org.springframework.aop.support.annotation.AnnotationMatchingPointcut 此实现在类或方法上查找自定义的java注解
2. org.springframework.aop.aspectj.AspectJExpressionPointcut 实现使用AspectJ织入器以AspectJ语法表达式匹配切入点;(需要添加aspectjrt和aspectjweaver两个库
3. org.springframework.aop.support.ComposablePointcut ComposablePointcut类使用诸如union()和intersection()等操作组合两个或更多切入点
4. org.springframework.aop.support.ControlFlowPointcut ControlFlowPointcut是一种特殊的切入点,它匹配另一个方法的控制流中的所有方法,即任何作为另一个方法的结果而直接或间接调用的方法;
5. org.springframework.aop.support.DynamicMethodMatcherPointcut 动态切入点的基类
6. org.springframework.aop.support.JdkRegexpMethodPointcut 使用正则表达式进行切入点匹配;(jdk1.4以上)
7. org.springframework.aop.support.NameMatchMethodPointcut 通过NameMatchMethodPointcut,创建切入点,对方法名称列表执行简单匹配;
8. org.springframework.aop.support.StaticMethodMatcherPointcutStaticMethodMatcherPointcut创建静态切入点的基础;
----------------------------------------------
spring的获取代理类
private static GrammyGuitarist getProxy(ComposablePointcut pointcut, GrammyGuitarist guitarist) {
final DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new SimpleAdvice());
final ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(advisor);
proxyFactory.setTarget(guitarist);
return (GrammyGuitarist) proxyFactory.getProxy();
}
核心就是ProxyFactory
https://blog.csdn.net/u014259503/article/details/90719930可做参考
spring切面@Transactional不起作用的几种原因
1,同一个类中的方法调用(如A方法无@Transactional注解,调用了一个有@Transactional注解的方法),这样增强interceptor也是不生效的。
2、检查你的方法是不是public的。@Transactional注解只能应用到public可见度的方法上,如果应用在protected、private或者package可见度的方法上,也不会报错,不过事务设置不会起作用。
3、检查你的异常类型是不是unchecked异常。默认情况下,Spring会对unchecked异常进行事务回滚,如果是checked异常则不回滚。如空指针异常、算术异常等,会被回滚;文件读写、网络出问题,spring就没法回滚了。如果你想check异常也回滚怎么办,注解上面写明异常类型即可:
@Transactional(rollbackFor = Exception.class)
类型的还有norollbackFor,自定义不回滚的异常
4是否在service中进行了try…catch的操作,由于已经被捕获异常,故事务也不会回滚。如果非要在service中try…catch异常,又想要事务回滚,可在catch块中抛出运行时异常:
5数据库引擎要支持事务,如果是mysql,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的。
6spring是否扫描到你这个包,如下是扫描到org.test下面的包
springboot @EnableAutoConfiguration
SpringBoot之@EnableAutoConfiguration注解 Spring 循环依赖及三级缓存_程序源程序的博客-CSDN博客Spring在启动过程中,使用到了三个map,称为三级缓存。Spring启动过程大致如下:1.加载配置文件2.解析配置文件转化beanDefination,获取到bean的所有属性、依赖及初始化用到的各类处理器等3.创建beanFactory并初始化所有单例bean4.注册所有的单例bean并返回可用的容器,一般为扩展的applicationContext一级缓存在第三步中,所有单例的bean初始化完成后会存放在一个Map(singletonObjects)中,beanName为key,单例https://blog.csdn.net/u012098021/article/details/107352463
spring循环依赖 以及三级缓存
springboot启动过程
SpringBoot源码分析之Spring容器的refresh过程:SpringBoot源码分析之Spring容器的refresh过程 - 简书
Springboot启动后,BeanDefinition的生成过程 Springboot启动后,BeanDefinition的生成过程_Oxye的博客-CSDN博客
spring源码系列(三)——beanDefinition(1) ☆☆☆ spring源码系列(三)——beanDefinition(1)_beandefinition集合_shadow?s的博客-CSDN博客
springboot2.x源码笔记-生成bean的实例以及初始化 springboot2.x源码笔记-生成bean的实例以及初始化_@bean 遍历生成_Eshin_Ye的博客-CSDN博客
下面是springboot服务启动过程中的重要详解
1 prepareContext阶段
2 refreshContext阶段
2.1 prepareRefresh
2.1.1 设置spring容器的启动时间,开启活跃状态以及撤销关闭状态
2.1.2 初始化属性源信息(Property)
2.1.3 验证环境信息里一些必须存在的属性
2.2 obtainFreshBeanFactory()
2.2.1 生成DefaultListableBeanFactory,并配置beanfactory的一些属性(对于GenericAppliclationContext),而且beanfacotry也是BeanDefinitionRegistry类
2.3 prepareBeanFactory
2.3.1 设置classLoader(beanClassLoader 一般都是Thread.currentThread().getContextClassLoader();),设置表达式解析器beanExpressionResolver【解析bean定义中的一些表达式】添加属性编辑注册器(注册属性编辑器propertyEditorRegistrars
2.3.2 往beanFactory中添加BeanPostProcessor【ApplicationContextAwareProcessor和ApplicationListenerDetector,后面有讲到ApplicationContextAwareProcessor,方法是addBeanPostProcessor(BeanPostProcessor beanPostProcessor)】,写入需要忽略的类【ignoreDependencyInterface(Class<?> ifc);全部都是Aware类】往beanFactory注入BeanFactory类型为自身,而ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器【方法是registerResolvableDependency(xx.class,xx实例)】,往beanfactory注入单例对象Environment等[registerSingleton(String beanName, Object singletonObject);]
2.4 postProcessBeanFactory(beanFactory)
2.4.1.往beanfactory中添加BeanPostProcessor [WebApplicationContextServletContextAwareProcessor],同时将ServletRequest ServletResponse HttpSession等属性设置为WebAppContextUtils.xxxObjectFactory[内部有getObejct()方法,该方法返回的是当前线程的xx]
2.5 invokeBeanFactoryPostProcessors(beanFactory)
2.5.1 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors 方法内部逻辑: 获取当前context的所有beanFactoryPostProcessor【可以通过prepareContext阶段通过设置各个实现接口ApplicationContextInitializer.initial方法来往context塞BeanFactoryPostProcessor】,
2.5.2 对实现了PriorityOrdered的BeanDefinitionRegistryPostProcessor排序,并且从BeanFactoryPostProcessor中选择类型为BeanDefinitionRegistryPostProcessor,并执行接口方法postProcessBeanDefinitionRegistry。然后再在beanfactory中找到其他的BeanDefinitionRegistryPostProcessor【这里面有一个重要的实现类ConfigurationClassPostProcessor】并执行postProcessBeanDefinitionRegistry 方法。注解:ConfigurationClassPostProcessor是一个BeanFactory的后置处理器,因此它的主要功能是参与BeanFactory的建造,在这个类中,会解析加了@Configuration的配置类,还会解析@ComponentScan、@ComponentScans注解扫描的包,以及解析@Import等注解,其内部ConfigurationClassParser也比较重要
相关代码 可参考https://www.cnblogs.com/hermanlife/p/10019473.html Springboot - @Import 详解
2.5.3 当执行完所有的BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry之后,然后执行所有BeanFactoryPostProcessor的postProcessBeanFactory方法【优先执行实例类型为BeanDefintiionRegistryPostProcessor的方法,然后再执行实际类型为BeanFactoryPostProcessor的方法】。之前这些beanFactoryPostProcessor都是context获取的,后面再从beanfactory中获取类型为BeanFactoryPostProcessor实例,再次调用其方法。
2.6 registerBeanPostProcessors(beanFactory); 获取所有的BeanPostProcessor,注意这些BeanPostProcessor都会进行BeanFactory.doGetBean()初始化【并且生成的都是代理类】
2.7 initApplicationEventMulticaster();
2.7.1 将事件广播器注入到beanFactory 名称是"applicationEventMulticaster"只需要拿出BeanFactory中的事件广播器然后设置到Spring容器的属性中即可。如果没有使用SpringBoot的话,Spring容器得需要自己初始化事件广播器。
2.8 onRefresh()
2.8.1 从BeanFactory中获取ServletWebServerFactory的beanName (默认是 tomcatServletWebServerFactory)并初始化webserver。
2.9 registerListeners() 将把Spring容器内的时间监听器和BeanFactory中的时间监听器都添加的事件广播器中。然后如果存在early event的话,广播出去
2.10 finishBeanFactoryInitialization(beanFactory)
实例化BeanFactory中已经被注册但是未实例化的所有实例(懒加载的不需要实例化)。比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化。实例化的过程各种BeanPostProcessor开始起作用。
2.11 finishRefresh() refresh做完之后需要做的其他事情。
2.11.1初始化生命周期处理器,并设置到Spring容器中(LifecycleProcessor)
2.11.2调用生命周期处理器的onRefresh方法,这个方法会找出Spring容器中实现了SmartLifecycle接口的类并进行start方法的调用
2.11.3发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
2.11.4调用LiveBeansView的registerApplicationContext方法:如果设置了JMX相关的属性,则就调用该方法
2.11.5发布EmbeddedServletContainerInitializedEvent事件告知对应的ApplicationListener进行响应的操作
3.afterRefresh() 空函数 未有任何实现
ApplicationContextInitializer 上下文初始化器
接口:void initialize(C var1);
作用:
- 用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法)
- 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
- springboot通常在prepareContext阶段的时候使用所有的ApplicationContext.initialize函数接口
注意点:
1.ApplicationContextInitializer实现类起作用的三种方式:
1)实现ApplicationContextInitializer接口的实现类需要需要放置在META-INF/spring.factories资源文件中,
2)或者在application.properties文件中配置context.initializer.classes=org.springframework.boot.demo.common.MyApplicationContextInitializer
3)ApplicationContext是在springApplication对象构造函数初始化的时候进行获取的,也是通过spring.factories文件获取的。
2.springframework中重要的实现类
ContextIdApplicationContextInitializer
SpringApplication
有几个重要的函数:
1.getSpringFactoriesInstances(Class<T> type),这个是通过加载META-INF/spring.factories中的所有实现类(不一定是ApplicationContextInitializer的实现类,其他接口的实现类只要使用这个了这个方法的都可以),其中SpringApplication使用这个方法加载了ApplicationContextInitializer以及ApplicationListener,所以ApplicationListener也可以放置在META-INF/spring.factories资源中
spring.factories中的资源文件格式类型其实就是properties文件类型格式如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration,\
org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration
org.springframework.context.ApplicationListener=\
org.apache.dubbo.spring.boot.context.event.OverrideDubboConfigApplicationListener,\
org.apache.dubbo.spring.boot.context.event.WelcomeLogoApplicationListener,\
org.apache.dubbo.spring.boot.context.event.AwaitingNonWebApplicationListener
org.springframework.boot.env.EnvironmentPostProcessor=\
org.apache.dubbo.spring.boot.env.DubboDefaultPropertiesEnvironmentPostProcessor
org.springframework.context.ApplicationContextInitializer=\
org.apache.dubbo.spring.boot.context.DubboApplicationContextInitializer
2.createSpringFactoriesInstances方法是实例化从spring.factories文件中的配置类,因为在实例化过程中使用的paramType(class对象)是new Class<?>[] {}这个,所以凡是通过getSpringFactoriesInstancesz这个方法实例化的类,即需要通过spring.factories中配置的实现类都需要有一个默认构造函数。
3.primarySource和mainApplicationClass都是同一个含有主方法的类。
ThemeSource
ApplicationListener和SpringApplicationRunListeners以及SpringApplicationRunListener
SpringApplicationRunListener是springboot的运行时监听器,而ApplicationListener是springboot的事件监听器,而SpringApplicationRunListeners包含了SpringApplicatonRunListener
SpringApplicationRunListener唯一一个springboot内建的实现类是EventPublishingRunListener,根据springFactoryLoader机制,可以发现EventPublishingRunListener内建于org.springframework.boot:spring-boot的jar包中
EventObject是事件,所有springboot发布的事件类都需要继承该类,这个是行业规则,其内部有一个source,标注该事件的来源
事件的发布过程:ConfiguableApplicationContext->SpringApplicationRunListeners->EventPublishingRunListener->SimpleApplicationEventMulticaster
而SimpleApplicationEventMulticaster的父类AbstractApplicationEventMulticaster有个内部类ListenerRetriever,ListenerRetriever和ApplicationListener是一对多关系,而SimpleApplicationEventMulticaster和ListenerRetriever也是一对多关系,ApplicationListener的实现类需要实现SmartApplicationListener这个类的方法supportsEventType,表明这个类可以监听哪些方法。
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
boolean supportsEventType(Class<? extends ApplicationEvent> var1);
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
default int getOrder() {
return 2147483647;
}
}
AbstractApplicationEventMulticaster获取事件对应ApplicatonListener的方法如下
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
//TODO 以EventType和sourceType为计算因子
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
ApplicationContext关系图
Spring IoC容器
<https://www.cnblogs.com/zhangfengxian/p/11086695.html#spring-ioc容器>
BeanFactory.ignoreDependencyInterface的主要功能是忽略给定接口的自动装配功能,也就是当有忽略的接口类,自动装配会忽略这部分类的初始化装配,因为某种情况下,此时的接口实现类不能初始化,列如BeanNameAware,要想装配这个接口的实现对象,可以实现这个接口,通过实现的set方法进行装配
ApplicationContext
springboot中容器对象是ApplcationContext,实际应该是AnnotationConfigServletWebServerApplicationContext,
继承关系如下图所示
常见的配置
spring.profile.active=PROD //设置的环境
获取ApplicationContext的三种方式
1 test测试时,自动注入(注入失败,暂时解决)
2 编写implements ApplicationContextAware接口实现类(主要方法是setApplicationContext(xx)),然后自动注入该实现类即可
注意,静态变量是不能被spring 容器所管理的
ApplicationContext可以获取容器管理对象
ApplicationContext.getBeanDefinitionNames();
Error creating bean with name 'springApplicationAdminRegistrar' defined in class path resource [org/springframework/boot/autoconfigure/admin/SpringApplicationAdminJmxAutoConfiguration.class]: Unsatisfied dependency expressed through method 'BeanFactory 。。。。。No querifier of org.springframeowork.core.env.Environment found, at least one ....
这种错误,多数由于配置格式错误,导致environment没有配置完成
springboot服务中起作用的是DefaultListableBeanFactory,其继承关系如下图所示:
CGLIB$CALLBACK六种callback
InitializingBean接口
InitializingBean接口为实例化bean提供了初始化方法,它只有一个方法afterPropertiesSet,凡是继承该接口的类,都会在实例化该类bean的时候调用该方法,即bean的所有属性都被注入后才会调用此方法,此方法适用于那些属性无法自动注入或者需要特殊设置的类bean.
BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor以及BeanPostProcessor
BeanDefinitionRegisterPostProcessor这个接口会在AbstractApplicationContext.invokeBeanFactoryPostProcessor中方法中执行,核心主要是ConfigClassPostProcessor,通过包名扫描到包名下的所有组件类,然后生成对应的BeanDefinition,放入到GenericBeanFactory中的BeanDefinitionMap中
BeanFactoryPostProcessor都是通过AbstractApplicationContext.addBeanFactoryPostProcessor这个接口添加的,所以在添加BeanFactoryPostProcessor的时候需要获取AbstractApplicationContext,而一般都是实现ApplicationContextInitializer这个接口
因为该接口有个方法是void initialize(C applicationContext);,这个方法可以获取到容器applicationContext,这个initialize方法的调用阶段是在prepareContext->applyInitializers阶段。
BeanFactoryPostProcessor的接口定义如下:
@FunctionalInterface
public interface BeanFactoryPostProcessor {
在初始化之后修改应用程序上下文的内部bean工厂。所有bean定义都已加载,实例化bean之前,可以覆盖或添加属性
//BeanFactoryPostProcessor:用来修改Spring容器中已经存在的bean的定义,使用ConfigurableListableBeanFactory对bean进行处理
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
官方文档对接口的说明如下:
- 允许自定义修改应用程序上下文(容器)的未实例化bean的定义,修改上下文的底层bean工厂的bean属性值。
- 应用程序上下文可以在其bean定义中自动检测BeanFactoryPostProcessor bean,并在创建任何其他bean之前应用它们。
- BeanFactoryPostProcessor可以与bean定义交互并修改bean定义,但从不与bean实例交互。这样做可能会导致过早的bean实例化,破坏容器并导致意外的副作用。如果需要bean实例交互,考虑实BeanPostProcessor。
BeanDefinitionPostProcessor有一个非常重要的实现类就是ConfigurationClassPostProcessor,也就是说这个接口的作用是在容器中的bean实例化之前,对加载进容器的bean进行一些属性的修改。
BeanDefinitionRegistryPostProcessor接口继承了BeanFactoryPostPorpcessor,接口定义如下:
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
//在应用程序上下文中的bean注册器实例化之后可以对其进行修改,这可以向bean注册器中添加更多的bean定义
//BeanDefinitionRegistryPostProcessor:继承BeanFactoryPostProcessor,作用跟BeanFactoryPostProcessor一样,只不过是使用BeanDefinitionRegistry对bean进行处理
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
官方文档对于这个接口的说明如下:
- 扩展了标准BeanFactoryPostProcessor SPI,允许在常规BeanFactoryPostProcessor postProcessBeanFactory方法执行之前向容器中注册更多的bean定义。特别是,BeanDefinitionRegistryPostProcessor可以注册更多的bean定义,这些定义反过来又定义了BeanFactoryPostProcessor实例。
BeanDefinitionRegistryPostProcessor作为BeanFactoryPostProcessor的子接口,其postProcessBeanDefinitionRegistry方法会在所有BeanFactoryPostProcessor post-processing前执行。因此 BeanDefinitionRegistryPostProcessor这个接口用于向容器中注册bean定义。BeanDefinitionRegistryPostProcessor可以支持听过代码实现动态注册bean到springIOC容器中,spring容器在初始化bean的时候,是通过资源读取相关bean的配置,然后保存到BeanDefinitionMap中,然后通过这些定义实例化bean,而在实例化之前,spring允许我们自定义扩展来改变bean的定义,定义一旦变了,后面的实例也会产生变化,而beanFactory的后置处理器BeanDefinitionPostProcessor就是用来改变配置属性定义的。
@Override public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//对beanFactory进行相关配置,例如context的ClassLoader和post-processors
prepareBeanFactory(beanFactory);
try {
// 允许context子类对beanFactory进行一些后置处理
postProcessBeanFactory(beanFactory);
// 调用容器中的BeanFactoryPostProcessor实现类的后置处理方法
invokeBeanFactoryPostProcessors(beanFactory);
// 向容器中注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
...
}
}
在AbstractApplicationContext的refresh中的invokeBeanFactoryPostProcessor来找到所有的BeanFactoryPostProcessor,并调用这些处理器来改变bean的定义。BeanFactoryPostProcessor通过其postProcessBeanFactory来改变bean的定义,而BeanDefinitionRegistryPostProcessor有两个需要实现的接口:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException:
该方法的实现中,主要用来对bean定义做一些改变。
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException:
该方法用来注册更多的bean到spring容器中,详细观察入参BeanDefinitionRegistry接口,看看这个参数能带给我们什么能力。
BeanPostProcessor 接口有两个默认方法
public interface BeanPostProcessor {
//上述第一个方法会在Spring实例化bean并且popluate属性之后,在调用各种init方法之前被调用,,如果这里第一个方法能够返回目标bean对象,那么这里就直接使用该对象,Spring不会继续生成目标bean对象,这种方式可以实现自定义的bean对象
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//而第二个方法会在Spring在调用各种init方法之后被执行,主要作用是对已经生成的bean进行一定的处理
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
- 首先 Spring 通过调用构造方法创建
User
对象; - User 对象创建好之后,先不直接进行初始化操作,通过
BeanPostProcessor
对刚创建好的 User 对象进行加工操作,其中postProcessBeforeInitialization
方法的第一个参数是 User 对象,第二个参数是在配置文件中指定的 id 值; - 加工好之后通过 return 将对象返回给 Spring 容器,然后 Spring 容器继续按照流程执行 初始化操作,先是
InitializingBean
的初始化操作; - 再是
init-method
的初始化; - 然后 Spring 容器再次将对象交给
BeanPostProcessor
,执行postProcessAfterInitialization
方法。
ConfigurationClassPostProcessor和ConfigurationClassParser
ConfigurationClassPostProcessor源码解析 : ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!_天堂2013的博客-CSDN博客
ConfigurationClassPostProcessor这个类是spring中最为重要的后置处理器,它实现了BeanDefinitionRegisterPostProcessor,而BeanDefinitionRegisterPostProcessor继承了BeanFactoryPostProcessor
从定义上看两个扩展接口的意义,BeanDefinitionRegisterPostProcessor主要用于增加新的BeanDefintiion,而BeanFactoryPostProcessor用于修改BeanDefintion的配置信息[或者增加动态代理],ConfigurationClassPostProcessor在执行过程中,会先执行postProcessBeanDefinitionRegistry(),然后执行postProcessBeanFactory()。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
Environment
ConfigurableEnvironment的实现类包括AbstractEnvironment,ConfigurableReactiveWebEnvironment等等子类
在开发中我们可以通过编写implements EnvironmentAware来获取Environment对象
Environment(StandardEnvironment)内部有propertySources属性(类型为MutablePropertySources,该类内部有propertySourceList),可以根据名称,可以获取到propertySource(内部可以根据getSource方法获取map对象,即originalSource)
Aware的执行阶段
通过实现该接口,setEnvironment 可以获取Environment对象,其中重写的方法setEnvironment方法会在系统工程启动的时候会被调用,@Service,@Component,@Controller等被spring管理的类都可以实现该接口
这些Aware的实现类的实现方法[注入setEnvironment,setContext等方法]都是通过ApplicationContextAwareProcessor.postProcessBeforeInitialization->ApplicationContextAwareProcessor.invokeAwareInterfaces调用的,而ApplicationContextAwareProcessor是在AbstractApplicationContext.refresh->AbstractApplicationContext.prepareBeanFactory阶段,加入到beanFactory中的。
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
大部分aware都是在context.refresh阶段的得到bean以后,但是在初始化之前获取的
EnvironmentPostProcessor
在服务启动的过程中,会寻找到所有EnvironmentPostProcessor,然后调用postProcessorEnvironment方法,这个过程在ConfigFileApplicationListener的onApplicationEnvironmentPreparedEvent接口中,每个EnvironmentPostProcessor可以根据@Priority(javax.annotation.Priority)的value设置。
SpringApplicationRunListener(ApplicationListener)
在SpringApplication对象构造函数获取所有ApplicationContextInitializer和SpringApplicationRunListener之后,在run阶段,EventPublishingRunListener->SimpleApplicationEventMulticaster,使其内部的所有SpringApplicationRunListener接收通知。
SpringApplicationRunListener一共可以接收到下面几个事件(通过SpringApplicationRunListeners对象统一)
ApplicationContextInitializer
这个可以通过META-INF/spring.factories文件中写入完整的类名,springboot在初始化阶段既可以在SpringFactoriesLoader.loadSpringFactories接口中完整的加载所有ApplicationContextInitializer
注意这个接口的实现类可排序,实现Ordered或者使用@Order注解
用途:ApplicationContextInitializer是在ConfigurableApplicationContext刷新之前初始化ConfigurableApplicationContext的回调接口。
目的:可以在context初始化阶段注册属性源或者激活配置
ImportBeanDefinitionRegistry接口、Import、ImportSelector以及ImportAware,ImportRegistry,ImportAwareBeanPostProcessor,InstantiationAwareBeanPostProcessor
这个是spring的一个扩展点之一,实现该接口的类必须通过@Import引入,作用是可以动态注入bean,如果想要该类起作用,需要将@Import引入到springboot的启动类中,或者使用在注解上使用,然后将注解注入到启动类中,但注意的是如果使用注解,必须在该注解上注入@Retention(RetentionPolicy.RUNTIME),否则不起作用.,类似的还有ImportSelector,所以Import可以
其中mybatis集成spring的原理即为使用该接口和注解实现。
mybatis源码讲解https://www.cnblogs.com/wt20/p/10990697.html
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
@Import(value={DynamicSpringConfiguration.class})
//可以添加ImportSelector,ImportBeanDefinitionRegistry以及含有@Configuration注解的配置类
public @interface ImportRegistry {
}
@Data
public class DynamicSpringConfiguration implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
System.out.println("setEnvironment ***********************************");
this.environment = environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
System.out.println("registerBeanDefinitions ***********************************");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Car.class);
builder.setScope(BeanDefinition.SCOPE_SINGLETON);
builder.addPropertyValue("name", "fancy car");
builder.addPropertyValue("wheelCount", 3);
AbstractBeanDefinition abstractBeanDefinition = builder.getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("pjwCar",abstractBeanDefinition);
}
}
外部化配置
https://www.cnblogs.com/loongk/p/12046582.html
@Value比较特殊,只能注入外部化配置所对应的信息(包括application.properties等配置文件),在处理静态变量时候,使用上面的@Value的用法是无法获取到配置文件中的数据
Spring 允许外部化你的配置,这样你就可以在不同的环境中使用相同的应用程序代码,你可以使用properties文件、YAML文件、环境变量和命令行参数来外部化配置,属性值可以通过使用@Value注解直接注入到你的bean中,通过@Configuration,@PropertySource绑定到结构化对象。外部化配置中的数据都是key value 格式
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.Resource;
/**
* 外部化配置作为依赖来源示例
*
*/
@Configuration
@PropertySource(value = "META-INF/default.properties",encoding="UTF-8")
public class ExternalConfigurationDependencySourceDemo {
@Value("${user.id:-1}")
private Long id;
@Value("${usr.name}")
private String name;
@Value("${user.resource:classpath://default.properties}")
private Resource resource;
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(ExternalConfigurationDependencySourceDemo.class);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 依赖查找 ExternalConfigurationDependencySourceDemo Bean
ExternalConfigurationDependencySourceDemo demo = applicationContext.getBean(ExternalConfigurationDependencySourceDemo.class);
System.out.println("demo.id = " + demo.id);
System.out.println("demo.name = " + demo.name);
System.out.println("demo.resource = " + demo.resource);
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
}
外部化配置可以参考apollo的外部化配置的代码:Apollo 源码解析 —— 客户端配置 Spring 集成(三)之外部化配置_weixin_42073629的博客-CSDN博客
@Resource和@Autowired的区别
共同点:两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
不同点:
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
@Resource装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byType方式进行装配;如果匹配到多个,则会抛出异常。
@Resource的作用相当于@Autowired,只不过resource还可以通过name进行装配
注意:
Spring对注解形式的bean的名字的默认处理就是将首字母小写,再拼接后面的字符,但今天看来不是这样的。(和AnnotationNameGenerator类相关)
回来翻了一下原码,原来还有另外的一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致[实践是这样的]
springAOP源码详解
参考文章
1.Spring Aop之Target Source详解_charming丶的博客-CSDN博客
aop比较重要的几个类包含了
spring cache
Springboot中的缓存Cache和CacheManager原理介绍 https://www.cnblogs.com/top-housekeeper/p/11865399.html
关键类 cacheable CacheProxyFactoryBean CacheIntrceptor CacheAspectSupport AbstractCacheResolver(里面含有唯一的属性CacheManager)
CacheAspectSupport在getCaches中使用CacheResolver来获取caches