springframework中IOC容器源码详解

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循环依赖 以及三级缓存

spring解决循环依赖原理探索  spring解决循环依赖原理探索_CrazySnail_x的博客-CSDN博客1.什么是循环依赖所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。这样依赖,就不知道该先实例化谁了。1.1spring中循环依赖的场景spring中,可能引起循环依赖的场景大致有三种:1.构造器互相依赖2.构造器依赖 + field依赖或者setter依赖(A的构造器依赖B,B的field或者sett...https://blog.csdn.net/weixin_40910372/article/details/105918296

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

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;
	}
  1. 首先 Spring 通过调用构造方法创建 User 对象;
  2. User 对象创建好之后,先不直接进行初始化操作,通过 BeanPostProcessor 对刚创建好的 User 对象进行加工操作,其中 postProcessBeforeInitialization 方法的第一个参数是 User 对象,第二个参数是在配置文件中指定的 id 值;
  3. 加工好之后通过 return 将对象返回给 Spring 容器,然后 Spring 容器继续按照流程执行 初始化操作,先是 InitializingBean 的初始化操作;
  4. 再是 init-method 的初始化;
  5. 然后 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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值