Skip to content

spring

容器

  1. 代码:Spring 容器就是一个实现了 ApplicationContext 接口的对象
  2. 业务:用来管理对象的 : 容器将创建的对象,把它们连接在一起,配置它们,并管理它们的整个生命周期从创建到销毁
  3. 对象实例化,只是得到一个对象,还不是一个完全的Spring中的bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的生命周期 Spring官网上说到,在Spring实例化一个对象有三种方式: 构造函数 实例化工厂方法 静态工厂方法

spring 特征

spring:

父子容器

HierarchicalBeanFactory 父子级联 父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器; 通过HierarchicalBeanFactory 接口, Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。 Spring 使用父子容器实现了很多功能,比如在 Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。

类初始化流程

这个图不完善或者说有些问题

最近学习理解流程

  1. new classpathxmlapplication
  2. refersh
  3. obtainfreshbeanfactory
    1. 通过这里面的loadbeandefination 将 <BEAN>的相关内容注入到容器中,此时没有进行初始化和实例化
    2. 主要通过recourceLoader读取配置文件、
    3. 将配置文件解析成beandefination
    4. 通过registerbean,将bean以bdf形式注入到map<name, beandefination>的map容器中
  4. 像容器中注册beanpostprocess接口,用于初始化时候进行回调
  5. 初始化当前 ApplicationContext 的事件广播器,这里也不展开了 ---- initApplicationEventMulticaster
  6. 开始初始化bean(初始化所有的 singleton beans,除了lazy-init标志的)
    1. getbean - dogetbean - creatbean - docreatebean
    2. 实例化 - 属性注入 - 初始化
    3. initializeBean 这里面回调beanpostprocess 里的before和after的接口,在before和after中间去实现init-method 和 afterPropertiesSet (属于initiliazingBean)
    4. 在 beanpostprocess 的postProcessAfterInitialization里会去实现对bean的代理对象生成
      1. 这里涉及代理对象的生成,但是在aop中,出现循环依赖,代理对象需要提前暴露不能在最后暴露所以,在finshbeanfactoryinitialization里面中的createInstance之后设置属性之前调用
      2. getEarlyBeanReference 提前暴露工厂方法到三级缓存中用户后续循环依赖的处理(还有种说法是在过MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition 里面提前暴露)
      3. 不过一定是在实例化结束,设置属性之前调用

Autowired

  1. docreatebean中
  2. 实例化完成,在applyMergedBeanDefinitionPostProcessors 去获取添加了Autowired属性的bean相关信息(元信息)
  3. 在populateBean中对属性进行填充,通过反射的方式;执行AutowiredAnnotationBeanPostProcessor#postProcessProperties方法,进行相应注入
  4. @Autowired的注入流程首先是需要构建一个InjectionMetadata,并通过InjectionMetadata的inject方法来进行注入
  5. 最终都会通过通过DefaultListableBeanFactory#resolveDependency获取依赖对象,然后通过反射进行相应属性赋值或者方法调用
  6. 问题:Autowired注解默认根据Type查找依赖的bean,如果找到多个如何处理?
    1. 通过qualifier 指定名字, 或者 Primary(指定一个主要的),order好像也可以实现先用谁
  7. 总结:
    1. 在执行registerBeanPostProcessors(beanFactory)时完成了AutowiredAnnotationBeanPostProcessor的创建与注册(refresh的步骤)
    2. 在用户bean创建过程中(doCreateBean),实例化(createBeanInstance)之后,通过执行applyMergedBeanDefinitionPostProcessors方法,实现@Autowired注解元数据的预解析
    3. 在进行属性填充(populateBean)的过程中,通过调用InstantiationAwareBeanPostProcessor的postProcessProperties方法,在里面执行 inject 完成真正的注入

Resource

@Resource相当于@Autowired+@Qualifier,它可以直接指定bean的名称,而@Autowired不能直接指定,需要和@Qualifier配合使用

  1. @Resource进行依赖查找的时候,首先是通过名称查找,如果匹配不到则退化到使用类型匹配;
  2. @Autowired则是先通过类型查找,如果匹配到多个再通过名称查找
  3. @Resource是通过CommonAnnotationBeanPostProcessor实现

aop

.JavaConfig方式如何启用AOP?如何强制使用cglib?

  1. @EnableAspectJAutoProxy
  2. //(proxyTargetClass = true) //强制CGLIB
  3. //(exposeProxy = true) 在线程中暴露代理对象@EnableAspectJAutoProxy

啥时候未生效spring事务失效:

  1. 不是public
  2. 没有被sping管理
  3. 切点配置异常
  4. 内部调用没有走代理
    1. 必须走代理,在本类中自动注入当前的bean

三级缓存

  1. singletonobject: 缓存最终的单例池结果
  2. earlysingletonobject: 缓存的单例不完善,没有填充属性等,用于解决循环依赖场景
  3. 三级缓存:主要用于切面场景:在aop场景中暴露出来的对象应该是代理对象而不是原对象,但是代理对象是初始化完成才进行生成,如果出现循环依赖场景就无法生成,此时就会用到三级缓存保存所有对象的动态代理配置信息,提前进行aop,生成动态代理

依赖注入

依赖注入属于ioc的具体实现,如何将bean交由spring管理,以及spring如何创建一个完整的bean

三种方式

属性注入

  1. java代码
java
 @RestController
 public class UserController {
     // 属性对象
     @Autowired
     private UserService userService;

     @RequestMapping("/add")
     public UserInfo add(String username, String password) {
         return userService.add(username, password);
     }
 }

问题:

  1. 无法注入一个不可变的final对象
  2. 只能适用于ioc容器,移植性不好
  3. 过于简单的注入方式导致一个bean很臃肿,可能多出很多的无用注入,可能影响单一职责

构造器注入

java
@RestController
public class UserController {
    // 构造方法注入
    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping("/add")
    public UserInfo add(String username, String password) {
        return userService.add(username, password);
    }
}

特点:

  1. 可注入不可变的对象final修饰的对象,如上述代码中的private final UserService userService;
  2. 注入的对象不会被修改
    1. 通过构造方法注入,只有在对象创建的时候执行一次构造方法,注入bean
  3. 注入对象完全初始化
    1. 在执行构造方法之前,需要注入的对象会被完全初始化
  4. 移植性更好

setter注入

java
@RestController
public class UserController {
    // Setter 注入
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping("/add")
    public UserInfo add(String username, String password) {
        return userService.add(username, password);
    }
}

问题 :

  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。
    1. Setter 注入提供了 setXXX 的方法,意味着你可以在任何时候、在任何地方,通过调用 setXXX 的方法来改变注入对象,所以 Setter 注入的问题是,被注入的对象可能随时被修改。

注入不可变对象

依赖注入用构造器方式注入final修饰的对象

  1. 线程安全需求:在多线程场景下,为防止被注入的对象发生意外修改

springMvc 与 spring的父子容器

子父容器:子可以访问父,父不可以访问子

  1. 所以父子容器的主要作用应该是划分框架边界。有点单一职责的味道。service、dao层我们一般使用spring框架来管理、controller层交给springmvc管理
  2. 如果现在我们想把web层从spring mvc替换成struts,那么只需要将spring­mvc.xml替换成Struts的配置文件struts.xml即可,而spring­core.xml不需要改变
  3. 为了节省重复bean创建

是否可以将所有的bean在父容器创建?在子容器创建呢

  1. 不可以在父容器创建
  2. mvc中在初始化HandlerMethods并没有去查找父容器的bean
  3. 都在子容器可以 因为父容器的体现无非是为了获取子容器不包含的bean,  如果全部包含在子容器完全用不到父容器了

spring的拦截器和过滤器有什么区别

  1. 拦截器incepter不依赖与servlet容器,过滤器filter依赖与servlet容器

springboot

自动装配

  1. 通过@SpringBootConfiguration 引入了@EnableAutoConfiguration (负责启动自动配置功能)
  2. @EnableAutoConfiguration 引入了@Import
  3. Spring容器启动时:加载Ioc容器时会解析@Import 注解
  4. @Import导入了一个autoconfigurationImportSelector
  5. 然后读取所有的/META-INF/spring.factories文件(SPI)
  6. 过滤出所有AutoConfigurtionClass类型的类
  7. 最后通过@ConditioOnXXX排除无效的自动配置类

什么是自动装配

SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,执行类中定义的各种操作

  1. @SpringBootApplication 项目启动使用注解,内部包含以下注解
    1. @SpringBootConfiguration 即为@Configuration
      1. 允许在上下文中注册额外的 bean 或导入其他配置类,作用与 applicationContext.xml 的功能相同。spring中与@bean共同作用
    2. @EnableAutoConfiguration
      1. 重点自动装配
      2. 内部调用AutoConfigurationImportSelector
        1. AutoConfigurationImportSelector 类实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中
        2. selectimport实现了getAutoConfigurationEntry()方法
        3. 通过loadSpringFactories 方法将所有 META-INF/spring.factories都会被读取到。
        4. 最后会根据@ConditionalOnXXX 过滤,符合条件的类才会生效
    3. @ComponentScan
      1. 扫描包下的类中添加了@Component注解的类

基于 VitePress 构建