Spring容器:

  在基于Spring的应用中,你的应用对象生存于Spring容器(container)中,Spring容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期,从生存到死亡。(在这里,可能就是从new()到finalize())。

  容器是Spring框架的核心。Spring容器使用DI(依赖注入)管理构成应用的组件,它会创建相互协作的组件之间的关联。毫无疑问,这些对象更简单干净,更易于理解,更易于重用并且更易于进行单元测试。

1、使用应用上下文

  Spring自带了多种类型的应用上下文:

  AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。

  AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring Web应用上下文。

  ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义, 把应用上下文的定义文件作为类资源。

  FileSystemXmlapplicationcontext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。

  XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。

无论是从哪里装载应用上下文,将bean加载到bean工厂的过程都是类似的。

如果你想从Java配置中加载应用上下文, 那么可以使用AnnotationConfig-ApplicationContext:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContextConfig.class);

在这里没有指定加载Spring应用上下文所需的XML文件, AnnotationConfig-ApplicationContext通过一个配置类加载bean。

应用上下文准备就绪之后, 我们就可以调用上下文的getBean()方法从Spring容器中获取bean。

Spring注解驱动开发

使用Java配置类代替XML配置文件来装配bean:

package com.yang.config;

import com.yang.domain.Person;
import com.yang.factory.StudentFactoryBean;
import com.yang.service.TestService;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope; /**
* Spring 基于Java类注解形式的配置
*
* @Configuration 使Spring容器知道这是一个配置类
* @ComponentScan 启用组件扫描,默认扫描本文件所在的包及其子包下声明了@Component注解的类,并在Spring容器中为其创建一个bean
* value属性指定要扫描的包
* includeFilters 指定扫描的时候只需要包含哪些组件
* excludeFilters 指定扫描的时候按照什么规则排除那些组件
* 如:excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)排除Controller注解的类
* @PropertySource 使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量Environment中;加载完外部的配置文件以后使用${key}取出配
*  置文件的值。也可以用Spring容器获取Environment变量,然后getProperty获取到配置的value
*  ConfigurableEnvironment environment = context.getEnvironment();
*  String name = environment.getProperty("name");
*
*/
@Import(TestService.class)
@Configuration
@ComponentScan(value = "com.yang")
@PropertySource(value = {"classpath:/applicationContext.properties"})
public class ApplicationContextConfig { /**
* 向Spring容器中注册一个bean,默认作用域是单例的
* 类型为方法的返回值
* bean的id为方法名,也可以使用@Bean的name属性指定bean的名称。
   * JavaConfig如何实现注入依赖?
   *   1、直接引用创建依赖bean的方法,使用的是带参构造器。但是这种引用并不会创建多个依赖bean的对象,因为Spring将会拦截对它的调用,并确保直接返回
   *    该方法所创建的bean,而不是每次都对其进行实际的调用。
*   2、将依赖的bean类型当成@Bean所在方法的一个参数。当Spring调用@Bean注解所在方法创建bean的时候,会自动装配一个参数类型bean到方法之中,然后
   *    方法体就可以按照合适的方式来使用它。这种方式不会像方式1那样要求将依赖bean的声明和当前bean在同一个配置类之中,甚至不要求依赖bean必须在JavaConfig
   *    类中声明。
   *      疑问,若有多个同一类型的依赖bean,Spring怎么选择装配?
   *      答案:先参数类型,后参数名称。源码在DefaultListableBeanFactory#doResolveDependency:1213
   *
* @Scope bean的作用域,取值范围:
* ConfigurableBeanFactory#SCOPE_PROTOTYPE singleton 单例,默认,Spring容器启动就会创建对象并放在容器中,以后每次获取都去map中取
* ConfigurableBeanFactory#SCOPE_SINGLETON prototype 原型/多例,Spring容器启动时并不会去创建对象,每次获取的时候都回去创建一个新的对象
* org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request 同一个请求创建一个对象
* org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session 同一个session创建一个对象       问题?Spring为啥把bean默认设计成单例?
        优势:为了提高性能
          1、减少创建新实例的消耗
          2、减少JVM垃圾回收
          3、可以快速获取到bean
        劣势:不能做到线程安全,bean如果是有状态的话在并发环境下线程不安全
*
* @Lazy 即懒加载,针对单实例作用域的bean,默认值为true,即Spring容器启动的时候不创建,调用的时候才去创建
*
* @Conditional 按照一定的条件进行判断,满足条件给容器中注册bean,若用在配置类上,则此配置类上的所有的bean都满足条件在会去Spring容器中
* 去注册。使用场景如只在linux系统中才创建bean
* Class<? extends Condition>[] value(); 接收一个实现Condition接口的类的class对象,需要自定义类实现Condition接口在matches方法中实
* 现具体判断逻辑,返回true代表符合条件
*
*/
@Lazy(value = false)
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Bean
public Person person() {
return new Person("yangyongjie");
} /** public class StudentFactoryBean implements FactoryBean<Student> {
public Student getObject() throws Exception {
return new Student();
} public Class<?> getObjectType() {
return Student.class;
} public boolean isSingleton() {
return false;//false 多例 true 单例 }
}
实际是将Student对象注册到Spring容器中
*/
@Bean
public StudentFactoryBean studentFactoryBean(){
return new StudentFactoryBean();
}
}
 向Spring容器中注册组件的四种方式:
1、包扫描+组件标注注解(@Component、@Controller、@Service、@Repository)
  2、@Bean,适合导入第三方包里面的组件
3、@Import,快速给容器中导入一个组件
  1、@Import(要导入的组件的class对象),容器中就会自动注册这个bean,名称是全类名(com.yang.service.TestService)
   2、@Import(ImportSelector.class),在ImportSelector中返回需要导入的组件的全类名数组
  3、@Import(ImportBeanDefinitionRegistrar.class)手动注册bean到容器中(在ImportBeanDefinitionRegistrar中使用BeanDefinitionRegistry的registerBeanDefinition方法手动注册) 技巧:BeanDefinitionRegistry:接口,提供往Spring容器中注册bean,移除bean,获取容器中所有注册了的bean和判断Spring容器中是否注册了某个bean
4、使用Spring提供的 FactoryBean(工厂Bean),与普通bean不同的是,普通bean是调用其无参构造器去创建对象然后注册到容器中,FactoryBean是调用其
  T getObject()方法,将返回的对象注册到Spring容器中
1、默认获取的工厂bean调用getObject创建的对象
2、注册到容器中工厂bean的id为 “&+bean名称”,如&studentFactoryBean 问题:BeanFactory和FactoryBean的区别
BeanFactory是IOC容器的核心接口,Spring使用BeanFactory来实例化、配置和管理bean,采用延迟加载的方式来注入bean,只有在getBean的时候才去加载创建bean
FactoryBean是创建bean的工厂,通过自定义类实现FactoryBean<T>接口,实现其getObject()方法创建bean对象,在配置文件中使用FactoryBean创建bean对象 BeanFactory和ApplicationContext的区别
ApplicationContext是完整的IOC容器,其由BeanFactory派生而来,具有BeanFactory的所有功能,可以通过配置实现,且额外提供了messageSource,资源访问,aop和web应用
,加载多种应用上下文。并可在启动的时候一次性创建所有需要的单实例bean Spring容器中Bean的生命周期
  bean创建--初始化--销毁
   容器管理bean的生命周期,
    若是单例的bean,在Spring容器的启动后先创建对象,再初始化,容器关闭的时候销毁bean。
    若是多实例的bean,Spring容器在每次获取的时候才去创建对象,然后初始化,但是Spring容器关闭时并不会去销毁bean,需要手动去销毁。
bean初始化和销毁四种方式:
  1、指定在bean中自定义初始化和销毁方法,通过@Bean注解指定initMethod和destroyMethod
     @Bean(initMethod = "customInit",destroyMethod = "customDestory")
  2、指定在bean中自定义初始化和销毁方法,然后使用javax提供的注解
            @PostConstruct标注在bean的初始化方法上,将在bean创建并属性赋值完成,调用此方法
@PreDestroy标注在bean的销毁方法上,将在容器销毁对象之前调用   3、通过使Bean实现InitializingBean和DisposableBean接口,在重写的afterPropertiesSet和destroy方法实现初始化和销毁逻辑   4、bean的后置处理器,BeanPostProcessor接口,针对全局的bean,自定义bean实现了BeanPostProcessor接口,那么容器中其他所有bean(只针对单例的bean)在初始化方法之前之前
    都会调用postProcessBeforeInitialization,在初始化方法执行之后都会调用postProcessAfterInitialization
      postProcessBeforeInitialization 在初始化之前调用
postProcessAfterInitialization 在初始化之后调用 如果四种都写了,那么执行顺序为:

  constructor  创建bean实例
  postProcessBeforeInitialization  BeanPostProcessor的初始化前置方法
  PostConstruct  javax注解定义的初始化方法
  afterPropertiesSet  实现InitializingBean接口的初始化方法
  customInit  @Bean注解上自定义的初始化方法
  postProcessAfterInitialization  BeanPostProcessor的初始化后方法
  PreDestroy  javax注解定义的销毁方法
  destroy  实现DisposableBean接口的销毁方法
  customDestory  @Bean注解上自定义的销毁方法

初始化和销毁的执行顺序都是  BeanPostProcessor-->javax提供的注解-->InitializingBean/DisposableBean-->@Bean custom

BeanPostProcessor原理
  populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
  initializeBean 
  {
    applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
    applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

      遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization

  }

  Spring底层对BeanPostProcessor的使用:

    1、其实现类ApplicationContextAwareProcessor可对实现了ApplicationContextAware接口的bean(对bean类型作了判断,只针对部分bean)注入容器ApplicationContext依赖对象,代码如下:

        if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}

    2、其实现类InitDestroyAnnotationBeanPostProcessor,对bean中标注了@PostContruct和@PreDestory注解的方法进行调用执行

    3、其实现类AutowiredAnnotationBeanPostProcessor,在对象创建完成以后,对所有标注了@Autowired注解的属性进行注入值

   
@Value
  使用@Value注解为属性赋值
  1、可以直接写基本数值
  2、可以写SPEL表达式#{}
  3、可以写${},取出在配置文件中配置的,容器启动放到环境变量Environment中的值
自动装配
  创建容器中对象协作关系的行为叫做装配,是DI的核心,即为容器中各个组件的依赖关系赋值
  1、@AutoWired,自动注入
    1)默认按照类型byType去容器中找对应的组件,然后注入;applicationContext.getBean(xxx.class)
    2)如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
    3)@Qualifier("xxx"):执行需要装配的组件的id,而不是使用属性名
    4)自动装配默认一定要将属性赋值好,没有就会报错,可以使用@AutoWired(required=false),允许不注入
    5)@Primary:用在依赖的类上,让Spring进行自动装配的时候,默认首选装配它  2、@Resource
    和@AutoWired一样实现自动装配功能,默认是按照属性名称ByName进行装配的,按名称没有找到则再按类型进行装配,如果指定了name或者type则按照名称和类型进行装配,没有找到则报错
  3、Inject
    需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能
  4、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
    自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
    把Spring底层一些组件注入到自定义的Bean中;
    xxxAware:功能使用xxxProcessor,是通过后置处理器来实现的。
    ApplicationContextAware==》ApplicationContextAwareProcessor;

  技巧:自定义Bean实现xxxAware接口,重写其中的方法可为我们的自定义Bean注入Spring底层的组件xxx,原理是通过xxxProcessor,一个实现了后置处理器
    BeanPostProcessor的类通过在Bean初始化前后做的事情来实现的。如下面所示的实现ApplicationContextAware 接口,为我们的Bean注入了ApplicationContext 对象

 
@Component
public class Child implements ApplicationContextAware { private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
  @Autowired:Spring定义的; @Resource、@Inject都是java规范  
  AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能; 

   @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
    1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
    2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
    3)、放在参数位置:

高级装配

  @Profile(value = "env"):指定组件在哪个环境的情况下才能被注册到容器中。

    1、加了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中,默认激活环境是default

    2、若标注在配置类上,则只有在执行的环境激活的时候,整个配置类里面的所有配置才生效

    3、没有标注环境标识@Profile注解的bean在任何环境下都会注册的

  如何激活Profile环境:

  1、使用命令行动态参数:在虚拟机参数位置加载-Dspring.profiles.active=test

  2、代码的方式激活某个环境变量,需要在启动Spring容器的时候使用无参构造不传配置文件的参数

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 设置需要激活的环境
context.getEnvironment().setActiveProfiles("pre");
// 注册主配置类
context.register(ApplicationContextConfig.class);
// 启动刷新容器
context.refresh();

AOP

实现一个AOP的步骤:

  1、将业务逻辑组件和切面类都注入到Spring容器中,标注使Spring知道哪个是界面类,@Aspect(曾犯错,在切面类上没有标注@Component注解)

  2、在切面类上的每一个通知方法上都标注通知注解(如@Around),并在注解中指明切入点表达式

  3、开启基于注解的aop模式,在配置类上加上注解@EnableAspectJAutoProxy

@AfterReturning注解的returning可将方法的返回值映射到参数上

@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturning(Object result) throws Throwable {}

@AfterThrowing(value="pointCut()",throwing="exception")的throwing将异常信息映射到参数上

获取切入点方法名:joinPoint.getSignature().getName()

获取方法入参:joinPoint.getArgs()

    @Before("pointCut()")
// 方法若有多个参数,JoinPoint一定要放在第一位
public void before(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
}

@EnableAspectJAutoProxy注解做了什么?

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy

  往容器中导入了AspectJAutoProxyRegistrar,利用AspectJAutoProxyRegistrar自定义的往容器中注册了name为internalAutoProxyCreator,class

为AnnotationAwareAspectJAutoProxyCreator的bean。

  技巧:如果看到了Enable开头的注解,就看这个注解有没有往容器中注册一些组件,如果注册组件了,这些组件的功能是什么,如果具体功能搞明白了,那么这个注解的原理就清楚了。

  那么既然@EnableAspectJAutoProxy往Spring容器中注入了AnnotationAwareAspectJAutoProxyCreator这个bean(从名字看是:注解装配切面代理创造器),

那就看一下这个bean的功能是什么,源码走起:

class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator
  class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator
    abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator
      abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
        interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor
          interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor 其中:class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean

看到其祖先类实现了InstantiationAwareBeanPostProcessor 接口,而此接口又实现了BeanPostProcessor接口,而这个接口我们知道叫做后置处理器,后置处理器

的功能就是在bean初始化的前后做一些工作。

抽象父类AbstractAutoProxyCreator 又实现了BeanFactoryAware,可以往代码中注入BeanFactory。

那么先重点看与setBeanFactory以及与后置处理器相关的逻辑,通过源码发现AbstractAutoProxyCreator有setBeanFactory()以及后置处理器相关的逻辑。而在

AbstractAutoProxyCreator的子抽象类AbstractAdvisorAutoProxyCreator又重写了setBeanFactory()方法

    public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException("AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
} else {
this.initBeanFactory((ConfigurableListableBeanFactory)beanFactory);
}
}

由于其子类AnnotationAwareAspectJAutoProxyCreator 重写了initBeanFactory方法,所以最终调用的是AnnotationAwareAspectJAutoProxyCreator 中的initBeanFactory方法中的逻辑:

  protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
if (this.aspectJAdvisorFactory == null) {
this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
} this.aspectJAdvisorsBuilder = new AnnotationAwareAspectJAutoProxyCreator.BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}

断点看AOP的整个执行流程:

1、new AnnotationConfigApplicationContext(AopConfig.class); 创建Spring容器,接下来看其构造器逻辑

2、先注册配置类,然后调用refresh()方法刷新容器,接下来看refresh()的逻辑

3、invokeBeanFactoryPostProcessors(beanFactory);实例化并调用所有注册的BeanFactoryPostProcessor

4、registerBeanPostProcessors(beanFactory); 注册bean的后置处理器来拦截bean的创建

  1):先获取ioc容器中已经定义了的需要创建对象的所有BeanPostProcessor {String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);}

  2):给容器中添加了额外的BeanPostProcessor{beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));}

  3):遍历BeanPostProcessor按照是否实现PriorityOrdered接口进行分组,若实现了,根据其name获取其对象(beanFactory.getBean(ppName, BeanPostProcessor.class);)

  将其放进priorityOrderedPostProcessors集合,若其又实现了MergedBeanDefinitionPostProcessor,将其再放进internalPostProcessors集合;若其实现了Ordered接口,

  将其bean name放入orderedPostProcessorNames集合;否则将其name放进nonOrderedPostProcessorNames集合

  4):优先注册实现了PriorityOrdered接口的BeanPostProcessor,再注册实现了Ordered接口的BeanPostProcessor,最后注册其他的BeanPostProcessor

  5):注册其实就是步骤3)中的根据其name获取BeanPostProcessor对象{BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);},实际上就是

  创建BeanPostProcessor对象然后将其保存在Spring容器中。以创建name为internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】为例,看

  一下创建过程:

    (1):创建bean的实例(createBeanInstance)

    (2):给bean的各种属性赋值(populateBean)

    (3):初始化bean(initializeBean)

      (1):invokeAwareMethods()判断bean是不是实现了xxxAware接口,如果实现了就调用相关的setXxx方法给bean的属性赋值

      (2):wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);应用bean的后置处理器的初始化前置方法,获取所有的后置处理器,

        遍历之后顺序应用。for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessBeforeInitialization(result, beanName);...

      (3):invokeInitMethods(beanName, wrappedBean, mbd); 执行初始化方法,如果实现了InitializingBean,执行afterPropertiesSet,再执行自定义的初始化方法(如

        果在@Bean注解指定了initMethod的话)

      (4):wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);应用bean的后置处理器的初始化后置方法,获取所有的后置处理器,

        遍历之后顺序应用。for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessAfterInitialization(result, beanName);

  6):由于AnnotationAwareAspectJAutoProxyCreator实现了 BeanFactoryAware接口,所以在invokeAwareMethods()方法中,回到了上面说的调用其抽象父类

   AbstractAdvisorAutoProxyCreator重写的setBeanFactory()方法。然后走到了AnnotationAwareAspectJAutoProxyCreator 的initBeanFactory()方法,至此,

   BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功

  7):将所有的BeanPostProcessor注册到BeanFactory中 (registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);-->beanFactory.addBeanPostProcessor(postProcessor);)

  以上就是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程。

  接下来看其作为一个BeanPostProcessor如何影响bean的创建过程的:

5、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作,创建剩下的单实例bean,为什么说是剩下的呢?因为BeanPostProcessors类型的bean在步骤4已经创建

  完成了

  1):preInstantiateSingletons()-->遍历获取容器中所有的beanDefinitionNames,遍历依此创建对象getBean(beanName); 在DefaultListableBeanFactory类中

  2):getBean(beanName)-->doGetBean()-->getSingleton(beanName) ,在创建bean之前先尝试获取单实例Bean的实例,为什么呢?因为只要创建好的bean就会被缓存起来

    (1)先检查JVM缓存中是否有对应的实例,因为在创建单实例bean的时候会存在依赖注入的情况,为了避免循环依赖,Spring在创建bean

    的过程中,若发现有依赖bean,则尝试去创建依赖的bean,因此Spring将每一个正在创建的bean的beanName放在一个“当前创建

    bean池”中,bean在创建过程中,BeanName将一直存在这个池中。当前创建bean池:

    private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

    如果当前线程创建bean池有,则尝试从缓存中加载bean

    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    (2)如果缓存中加载不成功,则再次尝试从singletonFactories中获取提前曝光的ObjectFactory,如果根据beanName找到了ObjectFactory,则从ObjectFactory中获取bean

    实例,然后将其放在earlySingletonObjects 缓存中,再将其ObjectFactory从singletonFactories中移除。

    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

    因为Spring为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖

    上一个bean则直接使用ObjectFactory去获取依赖bean的实例。

  3):createBean(); 创建bean

    (1):Object bean=resolveBeforeInstantiation(beanName, mbdToUse); 参数:RootBeanDefinition mbdToUse,bean的定义信息。方法的作用是:给BeanPostProcessor

    一个机会返回一个代理对象就不用创建其对象了。如果可以,直接返回代理对象,如果不能,则继续执行doCreateBean(beanName, mbdToUse, args);,其实现为:

    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); 这个方法的逻辑是:先拿到所有的后置处理器然后遍历,如果其中一个后置处理器实现了

      InstantiationAwareBeanPostProcessor接口,则执行这个后置处理器的postProcessBeforeInstantiation方法,如果返回了bean对象,则执行applyBeanPostProcessorsAfterInitialization

    并返回bean实例,结束bean的创建,不走下面的doCreateBean。
    if (bean != null) {
      bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    }

    注意:InstantiationAwareBeanPostProcessor虽继承了BeanPostProcessor,但其定义了postProcessBeforeInstantiation和postProcessAfterInstantiation方法,与BeanPostProcessor

    的postProcessBeforeInitialization和postProcessAfterInitialization方法不同,BeanPostProcessor的两个方法是在bean实例创建完成之后的初始化前后调用的,而

    InstantiationAwareBeanPostProcessor的两个方法是在创建bean实例之前尝试返回代理对象的。

    这也是AnnotationAwareAspectJAutoProxyCreator的功能,在所有的bean创建之前会有一个拦截,尝试返回其代理对象。

    (2):doCreateBean,真正的创建bean的实例,即4.5的流程

AnnotationAwareAspectJAutoProxyCreator做了什么事情?

1、每一个bean创建之前,都会调用postProcessBeforeInstantiation();其处理逻辑是:

  1):判断当前bean是否在advisedBean(保存了所有增强过的bean)中

  2): 判断当前bean是否是 基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean或者是否是切面(是否有@Aspect注解)

  3):或者是否需要跳过

    (1):获取候选的增强器(切面里面的增强方法)[List<Advisor> candidateAdvisors = findCandidateAdvisors();]

      (断点来看我们的每一个封装的通知方法的增强器类型是InstantiationModelAwarePointcutAdvisor);遍历然后判断每一个增强器是否是AspectJPointcutAdvisor类型的,

      如果是,返回true,否则返回flase

2、postProcessAfterInstantiation和postProcessBeforeInitialization一个返回true,一个返回bean对象本身,什么都没做

3、postProcessAfterInitialization方法:创建完对象并初始化后,调用它。其逻辑是:

  判断当前bean是否已经代理过,若没有则:return wrapIfNecessary(bean, beanName, cacheKey);这个方法的作用是,封装bean如果必要的话。

  1):Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);找到所有能应用到当前bean的增强方法

    (1):获取配置的所有的切面增强方法

    (2):找到所有的增强方法中能应用到当前bean方法中的

    (3):获取到能在当前bean使用的增强方法的集合,并按照一定规则排序

    (4):如果能在当前bean使用的增强方法的集合不为空,则转为数组返回,否则返回null

  2):如果返回的数组不为空,则需要代理

    (1):将bean的name作为key,值为true,放在advisedBeans(所有增强过的bean的ConcurrentHashMap)中

    (2):创建代理对象

      step1:如果能在当前bean使用的增强方法

      step2:遍历将其添加到proxyFactory中

      step3: DefaultAopProxyFactory中创建代理对象,Spring自动决定

       JdkDynamicAopProxy(AdvisedSupport config)

      ObjenesisCglibAopProxy(AdvisedSupport config)

    (3):将bean作为key,代理对象的class对象作为value,保存到private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<Object, Class<?>>(16);中

    (4):返回代理对象

    (5):以后在容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行增强方法的流程

  3)否则不需要代理,将bean的name作为key,值为false,放在advisedBeans中

   

执行目标对象Target中的切入点方法,即执行被增强的方法的流程:

从容器中获取的对象是代理对象,即容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细的信息(比如要应用的切面中的增强方法,也叫增强器,目标对象等)

1、CglibAopProxy.intercept();拦截目标方法的执行

2、根据ProxyFactory对象获取将要执行的目标方法拦截器链

  List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    1):List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); 集合长度为一个默认的 ExposeInvocationInterceptor.ADVISOR和其他被执行方法的增强器

    2):遍历所有的增强器,将其转为MethodInterceptor  【MethodInterceptor[] interceptors = registry.getInterceptors(advisor);】

                        如果是MethodInterceptor,直接放入List<MethodInterceptor>集合中

         如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor,然后再放入List<MethodInterceptor>集合中

         最后将集合转为数组返回

3、如果没有拦截器链,则直接执行目标方法(拦截器链,每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

4、如果有拦截器链,把需要执行的目标对象,目标方法,拦截器等信息作为参数创建一个CglibMethodInvocation对象并调用其proceed()方法

  retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

  CglibMethodInvocation对象:

  

  拦截器列表:

  

5、拦截器的触发过程

  1):如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(即执行到了最后一个拦截器),则执行目标方法

  2):链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回后再去执行(类似于递归调用),

    如上面拦截器的调用顺序是 Expose-->AfterReturning-->Around-->Before,然后前面的等待后面的返回再执行,最后实际执行顺

    序是Before-->Around-->AfterReturning-->Expose

    拦截器链的机制,保证通知方法与目标方法的执行顺序。

  

AOP总结

  1、@EnableAspectJAutoProxy  开启AOP功能

  2、@EnableAspectJAutoProxy  会给容器中注册一个组件 AnnotationAwareApectJAutoProxyCreator

  3、AnnotationAwareApectJAutoProxyCreator是一个后置处理器

  4、容器的创建流程

    1):registerBeanPostProcessors(beanFactory);  注册后置处理器,创建AnnotationAwareApectJAutoProxyCreator对象

    2):finishBeanFactoryInitialization(beanFactory);  初始化剩下的单实例bean

      (1):创建业务逻辑自检和切面组件

      (2):AnnotationAwareApectJAutoProxyCreator  拦截组件的创建过程

      (3): 组件创建完成之后,作为后置处理器的AnnotationAwareApectJAutoProxyCreator在postProcessAfterInitialization

        方法中判断组件是否需要增强,如果需要则 将需要应用到目标对象的切面中的通知方法,包装成增强器Advisor,然后

        给业务逻辑组件创建一个代理对象。

  5、执行目标方法

    1):代理对象来执行目标方法

    2):CglibAopProxy.intercept();拦截目标方法的执行

      (1):得到目标方法执行的拦截器链

      (2):利用拦截器的链式机制,依次进入每一个拦截器进行执行

      (3):实际执行顺序:

      正常执行:前置通知-->目标方法-->后置通知-->返回通知

      出现异常:前置通知-->目标方法-->后置通知-->异常通知

      

     

回炉Spring--Bean生命周期及AOP的更多相关文章

  1. 大厂高频面试题Spring Bean生命周期最详解

    Spring作为当前Java最流行.最强大的轻量级框架.Spring Bean的生命周期也是面试高频题,了解Spring Bean周期也能更好地帮助我们解决日常开发中的问题.程序员应该都知道Sprin ...

  2. spring bean 生命周期和 ? 作用域? spirng bean 相互依赖? jvm oom ? jvm 监控工具? ThreadLocal 原理

    1. spring bean 生命周期 1. 实例化一个bean ,即new 2. 初始化bean 的属性 3. 如果实现接口 BeanNameAware ,调用 setBeanName 4. Bea ...

  3. Spring点滴四:Spring Bean生命周期

    Spring Bean 生命周期示意图: 了解Spring的生命周期非常重要,我们可以利用Spring机制来定制Bean的实例化过程. -------------------------------- ...

  4. Spring Bean 生命周期之destroy——终极信仰

    上一篇文章 Spring Bean 生命周期之我从哪里来 说明了我是谁? 和 我从哪里来? 的两大哲学问题,今天我们要讨论一下终极哲学我要到哪里去? 初始化 Spring Bean 有三种方式: @P ...

  5. 常见问题:Web/Servlet生命周期与Spring Bean生命周期

    Servlet生命周期 init()初始化阶段 Servlet容器加载Servlet(web.xml中有load-on-startup=1;Servlet容器启动后用户首次向Servlet发请求;Se ...

  6. Spring Bean生命周期,好像人的一生。。

    大家好,我是老三,上节我们手撸了一个简单的IOC容器五分钟,手撸一个Spring容器!,这节我们来看一看Spring中Bean的生命周期,我发现,和人的一生真的很像. 简单说说IoC和Bean IoC ...

  7. 睡前聊一聊"spring bean 生命周期"

    spring bean 生命周期=实属初销+2个常见接口+3个Aware型接口+2个生命周期接口 实属初销:spring bean生命周期只有四个阶段,即实例化->属性赋值->初始化-&g ...

  8. Spring bean 生命周期验证

    一.从源码注释看bean生命周期 从JDK源码上看,BeanFactory实现类需要支持Bean的完整生命周期,完整的初始化方法及其标准顺序(格式:接口 方法)为: 1.BeanNameAware s ...

  9. 【不懂】spring bean生命周期

    完整的生命周期(牢记): 1.spring容器准备 2.实例化bean 3.注入依赖关系 4.初始化bean 5.使用bean 6.销毁bean Bean的完整生命週期可以認為是從容器建立初始化Bea ...

  10. 一步步剖析spring bean生命周期

    关于spring bean的生命周期,是深入学习spring的基础,也是难点,本篇文章将采用代码+图文结论的方式来阐述spring bean的生命周期,方便大家学习交流.  一  项目结构及源码 1. ...

随机推荐

  1. thinkphp 带检索参数分页

    模板文件中,提交搜索内容要用get方法,同时设置g.m.c隐藏域. <form action="{:U('Sck/qbscHjsc')}" method="get& ...

  2. Linux下安装mysql教程

    Linux下安装mysql  MySQL官网:https://dev.mysql.com/downloads/mysql/ 到mysql官网下载mysql编译好的二进制安装包,在下载页面Select ...

  3. SPOJ AEROLITE

    题目链接: http://www.spoj.com/problems/AEROLITE/en/ ---------------------------------------------------- ...

  4. CSS中设置字体样式

    <style type="text/css"> body{ font-family: SimHei,"微软雅黑",sans-serif; } < ...

  5. phpstudy开启PHPSocket扩展(windows系统)

    PHP开启Socket扩展 一.windows系统(本地电脑) 1.打开phpstudy,设置——>配置文件——>打开php.ini(我安装的是PhpStudy v8.0,其他版本请自己找 ...

  6. Hibernate异常:IllegalArgumentException

    异常信息: java.lang.IllegalArgumentException: attempt to create delete event with null entity at org.hib ...

  7. 简单的C++11线程池实现

    线程池的C++11简单实现,源代码来自Github上作者progschj,地址为:A simple C++11 Thread Pool implementation,具体博客可以参见Jakob's D ...

  8. 【Python—参数】*arg与**kwargs参数的用法

    在python中,这两个是python中的可变参数,*arg表示任意多个无名参数,类型为tuple;**kwargs表示关键字参数,为dict. # *允许你传入0个或任意个参数,这些可变参数在函数调 ...

  9. HDFS-NameNode和SeconddaryNode

    一.NN和2N的工作机制 一.概述 一.概述 一.概述 一.概述 一.概述 一.概述 一.概述

  10. from、includes、indexOf

    from.includes.indexOf:https://blog.csdn.net/j59580/article/details/53897630?utm_source=blogxgwz1 语法 ...