Spring中的一些知识

对Spring的理解

首先Spring是一个生态,可以构建企业级应用程序所需的一切基础设施,通常的Spring指的是Spring Framework.

Spring有两大核心:

  1. IOC和DI支持

Spring就是一个大的容器工厂,可以维护所有对象的创建和依赖关系,Spring工厂用于生成Bean,并且管理Bean的生命周期,实现高内聚低耦合的设计理念。

  1. AOP编程的支持

Spring提供面向切面编程,面向切面编程允许我们将横切关注点从核心业务代码中抽离出来,实现代码的模块化和重用性。可以方便的实现对程序的权限拦截、运行监控、日志记录等切面功能。

总结:Spring是一个轻量级,非侵入式的控制反转(Ioc)和面向切面(AOP)的容器框架。

spring的缺点

  1. 学习难度较大:Spring框架是一个功能强大且灵活的框架,但是由于其内容较多,对于初学者而言需要花费一定的时间来理解和掌握Spring的核心概念和特性。
  2. 配置复杂:Spring框架的配置通常是使用XML完成的,这种方式可能会导致配置文件变得复杂和溶长,特别是大型项目,配置文件的维护和管理变得困难。
  3. 运行时性能:Spring提供了很多的功能特性,这可能会使得运行时性能相对较低。尤其是需要平凡的创建和管理对象的场景下,可能会对系统性能产生一定的影响。
  4. 过度依赖:由于Spring提供了很多功能和模块,开发人员可能会过度依赖Spring框架导致项目的可以执行和可维护性下降。
  5. 文档和社区支持:尽管Spring框架有很多优秀的文档和活跃的社区支持,但有时候可能会遇到文档不完善或者社区资源有限的情况。这可能会给开发人员带来一些困扰。

这些缺点并不意味着Spring框架不好,而是在应用过程中需要克服的问题。并且Spring框架的有点远远超过了它的缺点,所以他任然是一个非常受欢迎和广泛使用的框架。

使用@Component注解注册得Bean其name是什么?

spring再解析@Component注册为BeanDefinition时会调用AnnotationBeanNameGenerator.buildDefaultBeanName()方法

一般情况:name是类名得首字母大写变小写,例如HttpTest,name为httpTest。

特殊情况:当类名第一个和第二个字母都为大写,则不会做操作直接使用类名为name,例如HTTPTest其name为HTTPTest

此外如果你想自定义这个Bean的名称,可以使用@Component注解的value属性 , 如下所示:

 @Component("customName") 
public class MyService { 
     // ... 
 }
//使用如下方法自动注入指定名称的Bean
@Autowired
@Qualifier("customName")
private MyService myService;
单例模式和Bean单例的区别

Singleton模式模式是保证在java应用程序中,一个Class有且仅有一个实例。它的好处是可以避免不必要的重复操作,以防止踩踏事件。

而单例模式和Bean单例具有一定的区别,主要在于其运行环境。单例模式是针对JVM层面的单例,而Bean单例是针对Bean容器的单例。而一个Spring可能会有多个Bean容器,但是一个Java程序只有一个JVM。虽然Spring的Bean单例在一个容器只有一个,但是如果容器中有多个Bean容器,并且都有某个Bean那么Bean在这个程序中并非只有一个。但是在实际中,如果将对象的生命周期交由Spring管理(不自己创建对象),其实也能达到单例模式的效果。

Spring的事务隔离级别?

Spring的事务隔离级别/是指在并发条件下,事务之间的相互隔离程度。Spring框架支持多种事务隔离级别,可以根据程序的具体需求,选择合适的事务隔离级别,以下是常见的事务隔离级别:

  1. DEFAULT(默认):使用数据库默认的事务隔离级别。如Mysql的是REPEATABLE_READ,Oracle的是READ_COMMITTED。
  2. READ_UNCOMMITTED(读未提交):最低的隔离级别,事务可以读其他事务未提交的数据。可能会导致脏读,不可重复读,幻读的问题。
  3. READ_COMMITTED(读已提交):事务读到的数据是其他事务已经提交的数据。避免了脏读的问题,但是又不可重复读、幻读的问题。
  4. REPEATABLE_READ(可重复读):保证一个事务在同一个查询中的多次读取的数据是一致的。事务期间,其他事物的修改对该事务不可见。避免了脏读和不可重复读的问题,但是会导致幻读的问题。
  5. SERIALIZABLE(串行):最高的隔离级别,保证事务串行执行。避免了脏读、幻读、不可重复读的问题。但是会降低并发性能,因为事务串行执行。

其用法如下:

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class AccountService {

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void transferFunds(long fromAccountId, long toAccountId, BigDecimal amount) {
        // 执行逻辑,比如从一个账户扣除金额,然后添加到另一个账户
    }
}
Spring的事务传播行为

事物的传播特性指的是一个事务方法被另外一个事务方法调用时,这个事务方法应该如何进行?

Spring提供了多种事务传播行为:

  1. REQUIRED:如果当前存在事务,则加入该事务。如果当前没有事务,则创建一个新事务。这是最常见的传播行为,也是默认的事务传播行为。
  2. REQUIRED_NEW:无论当前是否存在事务,都会创建一个新的事务,若当前存在事务则挂起当前事务。适用于需要独立执行事务的场景,不受外部事物影响。
  3. SUPPORTS:如果当前存在事务,则加入该事务,如果当前没有事务则以非事务的方式执行。适用于不需要强制事务的场景,可与其他
  4. NOT_SUPPORTED:以非事务的方式执行,如果当前存在事务,则将当前事务挂起。适用于不需要事务执行的场景,可以在执行方法时,暂时禁用事务。
  5. MANDATORY:如果当前存在事务,则加入该事务,如果当前不存在事务,则抛出异常。适用于必须在事务中执行的场景。
  6. NESTED:如果当前存在事务,则嵌套在当前事务中执行,如果没有则创建新事物。嵌套事务是外部事务的一部分,可以独立提交或回滚。
  7. NEVER:以非事务的方式执行,如果当前存在事务,则抛出异常。适用于不允许事务执行的场景。如果存在事务则抛出异常。

通过@Transational注解的propagation属性来指定事务传播行为。

Spring AOP和Aspectj AOP之间的区别

Spring AOP:的实现是基于动态代理(JDK动态代理和CGLIB动态代理),来实现AOP横切逻辑的织入。对于基于接口的目标类,Spring使用JDK动态代理。对于没有实现接口的目标类,Spring使用CGLB生成子类来实现代理。 Spring AOP 的性能相对较高,因为它基于代理,对目标类的影响比较小。但是,Spring AOP 对于复杂的切面和大规模的系统可能会有一定的性能影响。

spectj AOP:是基于字节码增强的AOP实现, 它通过在编译期或者类加载期修改目标类的字节码来实现 AOP 横切逻辑的织入。AspectJ 提供了更强大和灵活的 AOP 功能,可以进行更细粒度的控制和切入。 AspectJ AOP 的性能一般比 Spring AOP 略低,因为它对字节码进行了修改和增强,对目标类的影响更大。但是 AspectJ 提供了更强大的功能和更高的灵活性。

Spring AOP通知和执行顺序

Spring切面可应用五种类型的通知

  1. 前置通知:在方法调用前执行
  2. 后置通知:在方法完成之后执行
  3. 返回通知:在方法成功执行返回之后执行
  4. 异常通知:在方法出现异常后执行
  5. 环绕通知: 环绕通知包裹了目标方法的调用,允许你在方法调用前后添加自定义的行为

执行顺序:

Spring在5.2.7之前执行顺序

正常:前置->方法->后置->返回

异常:前置->方法->后置->异常

Spring在5.2.7之后执行顺序

正常:前置->方法->返回->后置

异常:前置->方法->异常->后置

AutoWired和Resource的区别
  1. 来源不同:@Autowired是Spring 2.5定义的注解,@Resource注解是java定义的注解,来自于JSR-250规范提案)。
  2. 依赖查找顺序不同:@Autowired注释注解首先会按照类型进行匹配,如果有多个匹配的Bean就会根据名称进行匹配。@Resource注释注解首先会按照名称进行匹配,如果有多个匹配的Bean就会根据类型进行匹配。
  3. 支持的参数不同:@Autowired注释只支持一个 required (为true如果没有匹配的Bean失败报错,为false则是简单的不注入),@Resource注释支持7个参数,包括name:指定名称注入,type:指定类型注入等。
  4. 依赖注入的用法不同:@Autowired注释支持属性注入、构造方法注入、setter注入。@Resource注释只支持属性和setter注入。

Spring框架中的单例Bean是线程安全的吗?

在整个Spring的上下文中,单例Bean的实例只有一个,但是具体是不是线程安全的还需看实际的Bean的状态。

  1. 无状态Bean:如果一个单例Bean是无状态的,即它的操作不会依赖于实例变量,或者说它的实例变量是不可变的(使用final修饰且实例化后不再改变),那么这个Bean是线程安全的。
  2. 有状态Bean:如果单例Bean维护了可变的状态(例如,实例变量可以在Bean的方法中被修改),那么这个Bean就可能存在线程安全问题。在这种情况下,开发者需要手动实现线程安全措施,比如使用synchronized关键字、使用ThreadLocal变量或者采用其他并发控制机制。
@Transactional(readOnly=true)真的会提高性能吗?

@Transactional(readOnly=true) 标记一个方法或类中的所有方法作为只读事务 。使用此注解后,Spring框架会在数据库中操作SET TRANSACTION READ ONLY,开启数据库的只读事务,这意味着在此事务内部,所有的修改(instert、update、delete)操作都会被禁止,只能执行读取操作。并且只读事务依旧会运用到隔离级别(MVCC),而事务隔离级别需要一定的性能开销。

一般情况下,执行查询任务,不开启事务比开启事务的效率更高,因为开启事务会涉及事务管理和隔离级别的开销。

在查询任务中,如果不涉及事务级别和一致性的要求,并且不需要使用事务管理的功能,那么不开启事务则是更优的选择。这种情况下,查询操作会立即执行并返回结果,你会受到事务管理和隔离级别的开销影响。

如果查询保证一致性和隔离性,也就是需要使用到事务。那么开启只读查询的效率会更高。 数据库可以利用这一提示来选择更优的执行计划,避免不必要的锁和日志记录。

SpringMVC的拦截器和过滤器有什么区别?执行的顺序是什么?
  1. 归属不同:拦截器是SpringMVC框架的一部分,而过滤器是Servlet规范的一部分。拦截器主要用于对控制层请求的处理,提供了更细粒度的控制。可在请求进入控制器之前和之后执行特定的逻辑。例如身份验证、日志记录和权限检查。过滤器则独立于SpringMVC之外,用于处理通用的请求和响应内容。
  2. 执行顺序不同:拦截器的执行顺序由配置文件中的顺序执行,可以有多个拦截器,它们按照配置文件中的顺序执行。而过滤器的执行顺序由web.xml文件中的配置顺序决定,同样可以有多个过滤器。一般来说,首先执行过滤器,再执行拦截器。
  3. 用途不同:拦截器用于对SpringMVC的请求和响应进行特定的业务处理,通常与控制层的请求处理相关。过滤器对所有Servlet的请求和响应进行通用性的处理。通常关注请求和响应内容,而不涉及具体的业务逻辑。
Spring和SpringMVC为什么需要父子容器?
  1. 划分功能边界:父子容器的存在有助于帮助划分功能边界,清晰的定义每个容器的职责,从而提高代码的可维护性和可扩展性。

  2. 规范整体架构:可以将业务层逻辑(Service)和数据访问层(dao)交给Spring管理,而将控制层(controller)交给SpringMVC管理。这种规范有助于提高代码可读性,使团队协作更加顺畅。

  3. 限制组件之间的依赖关系:这可以确保模块之间的隔离。子容器可以访问父容器之中的组件,但是父容器不能访问子容器的组件。这有助于减少意外的依赖和提高代码的稳定性。

  4. 方便切换子容器:如果需要更换子容器,例如从SpringMVC替换为Struts只需更改子容器的配置而无需更改父容器,提供了更好的可维护性和扩展性。

  5. 节省资源:父容器中的Bean可以在整个应用程序之中共享,而无需重新创建。可以降低内存和性能开销。

Spring框架中用到的设计模式
  1. 简单工厂:

BeanFactory:Spring的BeanFactory充当工厂,负责根据配置信息创建Bean实例。是一种工厂模式的应用,根据指定的类名或ID创建Bean对象。

  1. 工厂方法:

FactoryBean:FactoryBean接口允许用户自定义Bean的创建逻辑,实现工厂方法模式。开发人员可以使用FactoryBean创建复杂的Bean实例。

  1. 单例模式:

Bean实例:Spring的Bean默认是单例模式,确保再容器中只有一个共享的实例,有助于节省资源和提高性能。

  1. 适配器模式:

SpringMVC中的HandlerAdapter:SpringMVC中的HandlerAdapter允许不同类型的处理器适配到处理器接口,以实现统一的处理器调用。是适配器模式的应用。

  1. 装饰器模式:

BeanWrapper:Spring的BeanWrapper允许在不修改Bean原始类的情况下添加额外的功能。

  1. 代理模式:

AOP底层:Spring的AOP(面向切面编程)底层通过代理模式来实现切面功能,包括JDK的动态代理和CGLIB代理。

  1. 观察者模式:

Spring的事件监听:允许组件监听和响应特定类型的时间,实现了松耦合的组件通信。

  1. 策略模式:

excludeFiliter、includeFiliter:Spring允许使用策略模式来定义包扫描时的过滤策略,如在@ComponentScan注解中使用的excludeFiliter、includeFiliter。

  1. 模板方法模式:

Spring几乎所有的外界扩展:Spring框架的许多模块和外部接口都采用了模板方法,例如:jdbcTemplate、HibernateTemplate等

  1. 责任链模式:

AOP的方法调用:Spring AOP通过责任链模式实现通知(Advice)的调用,确保通知按顺序执行。

Spring事件监听的核心机制是什么?

观察者模式:允许一个对象(称为主题或被观察者)维护一组依赖于它的对象(简称观察者),并在主题状态发生变化时通知观察者。

包含三个核心:

  1. 事件:事件是观察者模式中的主题状态变化的具体表示,封装了事件发生时的信息。在Spring中事件通常是普通的Java对象,用于传递数据或上下文信息。
  2. 事件发布者:在Spring中,事件发布者充当主题的角色,负责触发并发布事件。它通常实现了ApplicationEventPublisher接口或者使用@Aotowired来获得事件发布功能。
  3. 事件监听器:充当观察者的角色,负责监听并响应事件的发生,实现了ApplicationListener接口,通过onAplicationEvent()方法来处理事件。

总之Spring事件监听机制的核心是观察者模式,通过事件、事件发布者、事件监听器的协助,实现了松耦合的组件通信,使得应用程序更加灵活和可维护。

多线程事务如何保证事务的一致性

在多线程的情况下Spring事务管理默认情况下无法保证全局事务的一致性。因为Spring的本地事务管理是基于线程的,每个线程都有自己的独立事务。

  1. Spring的事务管理通常是将事务信息存储到ThreadLoacl中,这意味着每个线程只能拥有一个事务。确保单个线程内数据库的操作处于同一个事物中,保证原子性。
  2. 通过以下方案解决:
  • 编程式事务:使用编程式事务,手动在代码中显示的控制事务的边界操作,确保事务在合适的时机提交或回滚,以确保在多线程中的事务的一致性。
  • 分布式事务:如果应用程序需要跨多个资源(例如多个数据库)的全局事务一致性,可以使用分布式事务管理(如2PC/3PC TCC等)来管理全局事务。确保所有参与事务的资源都处于相同的全局事务中,以保证一致性。

在多线程中,Spring的本地事务管理器需要额外的协调管理才能实现事务的一致性。

什么情况下AOP会失效

  1. 内部方法调用:如果在同一个类的一个方法中调用另一个方法,AOP通知可能不会触发。因为AOP通常是通过代理对象拦截外部方法调用的。解决方式是注入本类对象进行调用,或者是暴露当前对象到本地线程中,可以通过AopContext.currentProxy()拿到当前正在调用的动态代理对象。
  2. 静态方法调用:AOP通常无法拦截静态方法的调用,因为静态方法不是通过对象调用的。
  3. AOP的配置问题:错误的AOP配置可能会导致通知不正确的应用于目标方法,或者在不希望的情况下应用。例如切点表达式的错误。
  4. 构造函数调用:AOP不能在构造函数调用中添加通知,因为代理对象是在构造函数调用之后创建的。
  5. 代理问题:如果代理不正确地创建或配置,AOP通知可能无法生效。解决方法是调试源码确保代理对象能够正确地创建,并且AOP通知能够拦截代理对象地方法调用。
JDK动态代理和CGLIB代理地区别

从特性对比:

JDK动态代理要求目标对象必须实现至少一个接口,因为它基于接口生成代理类。而CGLIB动态代理不依赖于目标对象是否实现接口,可以代理没有实现接口的类,通过继承或者代理目标对象地父类来实现代理。

从创建时地性能对比:

JDK动态代理通常比CGLIB动态代理速度快,因为它不需要生成字节码文件。而CGLIB动态代理的创建速度通常比较慢,因为它需要生成字节码文件。并且JDK代理生成的代理类较小,占用较少的内存,而CGLIB生成的代理类较大,占用更多的内存。

从调用时的性能对比:

JDK动态代理在方法调用时需要通过反射的机制调用目标方法,因此性能略低于CGLIB,尽管JDK动态代理在JDK 8中有了性能改进,但CGLIB动态代理任然具有更高的方法调用性能,CGLIB动态代理在方法调用时不需要通过反射,直接调用目标方法,通常具有更高的方法调用性能,同时无需类型转换。

Spring AOP的底层实现原理

Spring AOP的底层实现原理主要基于代理模式和动态代理。

底层实现主要分为两部分:创建AOP动态代理和调用代理

在启动Spring的时候会创建AOP动态代理:

首先通过AspectJ解析切点表达式:在创建代理对象时,Spring AOP使用AspectJ来解析切点表达式。他会根据定义的条件匹配目标Bean的方法。如果Bean不符合切点的条件,将跳过否则将会通过动态代理包装

Bean对象:具体会根据目标对象是否实现接口来选择使用JDK动态代理或CGLIB代理。使得AOP可以是和各种类型的目标对象。

在调用阶段:

  1. Spring AOP使用责任链模式来管理通知的执行顺序。通知拦截链包括前置通知、后置通知、异常通知、最终通知和环绕通知,它们按照配置的顺序形成链式结构。
  2. 通知的有序执行:责任链确保通知按照预期顺序执行。前置通知再目标方法执行前执行,后置通知在目标方法执行后执行,异常通知在方法抛出异常时执行,最终通知无论如何都会执行,而环绕通知包裹目标方法,允许在方法执行前后添加额外的行为。

综上:Spring AOP在创建启动阶段使用AspectJ解析切点的表达式,如果成功匹配则使用动态代理,而在调用阶段使用责任链模式确保通知的有序执行。这些机制共同构成了Spring AOP的底层实现。

Spring 如何解决循环依赖的?

Spring采用三级缓存(也就是三个Map)解决循环依赖。 关键:一定要有一个缓存保存它早期对象作为死循环的出口。

  1. 一级缓存:singletonObjects存放可以使用的单例,也就是完全初始化完成的Bean单例。
  2. 二级缓存:earlySingletonObjects存放半成品Bean。也就是尚未完全初始化但已经实例化完成的Bean.
  3. 三级缓存:singtonFactories存放对象工厂。当Bean还未实例化时,这里存放的是一个工厂对象,该工厂对象负责实例化Bean.

当 Spring 初始化一个 Bean A 并发现它依赖于 Bean B 时,Spring 会先尝试从一级缓存获取 B。如果 B 不存在于一级缓存,Spring 将会进入初始化 B 的过程,同时将 A 的工厂放入三级缓存。当初始化 B 的过程需要 A 时,Spring 会检查三级缓存,找到 A 的工厂,然后提前创建一个未完全初始化的 A 的实例放入二级缓存。这个实例可以被 B 使用,从而避免了循环依赖。

需注意的是Spring无法解决构造器注入的循环依赖。

假设我们有两个简单的Bean,BeanABeanB,它们相互依赖:

@Service
public class BeanA {

    private BeanB beanB;

    @Autowired
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Service
public class BeanB {

    private BeanA beanA;

    @Autowired
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}
解决循环依赖的方法
1. 使用@Lazy注解

@Lazy注解可以标记一个Bean在Spring容器中以懒加载的方式初始化。当一个懒加载的Bean被请求时,Spring容器会立即返回一个代理对象,这个代理对象在真正需要的时候才会创建并初始化Bean。

@Service
public class BeanA {
    private BeanB beanB;

    @Autowired
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Service
@Lazy
public class BeanB {
    private BeanA beanA;

    @Autowired
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}@Service
public class BeanA {
    private BeanB beanB;

    @Autowired
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Service
@Lazy
public class BeanB {
    private BeanA beanA;

    @Autowired
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}

在上面的例子中,通过在构造器参数上使用@Lazy注解,我们可以指示Spring在必要时才初始化BeanBBeanA

2. 使用属性注入(Setter Injection)

虽然构造器注入是一种推荐的做法,但在循环依赖的情况下,使用setter注入可以避免问题,因为Spring可以先创建Bean实例,然后在后续步骤中注入依赖。

@Service
public class BeanA {

    private BeanB beanB;

    @Autowired
    public void setBeanB(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Service
public class BeanB {

    private BeanA beanA;

    @Autowired
    public void setBeanA(BeanA beanA) {
        this.beanA = beanA;
    }
}

在这种情况下,Spring会先创建BeanABeanB的实例,然后在初始化阶段通过setter方法注入依赖。

注意事项

尽管上述方法可以解决循环依赖问题,但在设计系统时应尽量避免这种情况,因为它可能会降低系统的可测试性和可维护性。最佳实践是重构代码,使依赖关系更加清晰和单一,例如通过引入第三个服务作为协调者,或者使用事件驱动架构来替代直接的依赖注入。

Spring Bean的生命周期

Bean的生命周期分为:实例化、依赖注入(DI)、初始化、销毁这四大部分。

  1. 实例化
    • 通过反射去推断构造函数进行实例化
    • 实例工厂、静态工厂
  2. 依赖注入(DI)
    • 解析自动装配(byname bytype constractor none @Autowired)
  3. 初始化
    • 调用很多Aware回调方法(Spring创建Bean的过程对用户是不可见的,可以通过实现Aware回调方法使得Spring创建Bean的过程通过打印的方式对用户可见)
    • 调用BeanPostProcessor.postProcessBeforeInitialization( 允许开发者在Bean初始化之前执行自定义的处理逻辑 )
    • 调用生命周期回调初始化方法
    • 调用BeanPostProcessor.postProcessAfterInitialization( 允许开发者在Bean初始化之后执行自定义的处理逻辑 ),如果Bean实现AOP则会在这里创建动态代理。
  4. 销毁
    • 在Spring容器关闭的时候进行调用
    • 调用生命周期回调销毁方法
单例Bean和单例模式有什么区别
  1. 定义和用途:
    • 单例Bean:在一个Spring Bean容器中只存在一个实例Bean。单例Bean的作用是共享和复用对象实例,以提高性能和减少资源消耗。
    • 单例模式:一种设计模式,用于确保一个类只有一个实例,并且提供一个全局的访问点来获取该实例。单例模式的目的是限制类的实例化次数,以保证全局唯一性和避免资源浪费。
  2. 实现方式:
    • 单例Bean:在Spring中,单例Bean的创建和管理由Spring容器负责,Spring容器在启动的时候会创建单例Bean的实例,并在整个应用程序的生命周期中共享该实例。
    • 单例模式:单例模式的实现方式有多种,常见的方式包括饿汉模式(在类加载的时候就创建实例)、懒汉模式(在第一次使用的时候创建实例)。
  3. 适用范围
    • 单例Bean:适用于需要共享和复用对象实例的场景,例如数据库连接、线程池等。
    • 单例模式:适用于需要确保全局唯性和避免资源浪费的场景,例如配置信息管理、日志记录器等。
Spring IOC的实现机制是什么?
  1. 反射:Spring使用Java的反射机制来实现动态创建和管理Bean对象。通过反射,Spring可以在运行时动态地实例化Bean对象,调用Bean地方法和设置属性。
  2. 配置元数据:Spring使用配置元数据来描述Bean的定义和依赖关系。配置元数据可以通过XML配置文件、注解和Java代码等方式进行定义。Spring在启动时会解析配置元数据,根据配置信息创建和管理Bean对象。
  3. Bean定义:Spring使用Bean定义来描述Bean属性、依赖关系和生命周期等信息。Bean定义可以通过XML配置文件中元素、注解(@Component、@Service等)以及java代码中的@Bean注解等方式定义。
  4. Bean工厂:Spring的Bean工厂负责创建和管理Bean对象。Bean工厂可以是BeanFactory接口的实现。例如经常使用的DefaultListableBeanFactory。Bean工厂负责解析配置元数据,根据Bean定义创建Bean对象,并将其放入容器中管理。
  5. 依赖注入:Spring使用依赖注入来解决Bean之间的依赖关系。通过依赖注入,Spring容器负责将Bean所依赖的其他Bean实例注入到他们之中。Spring使用反射和配置元数据来确定依赖关系,并在运行时进行注入。

总结:Spring IOC底层主要依赖反射、配置元数据、Bean定义、Bean工厂和依赖注入等技术和组件。通过这些机制实现了Bean的创建、配置和管理、以及Bean之间的解耦和依赖注入。

Bean的配置有哪几种方式?
  1. XML配置:使用XML文件通过元素定义Bean的属性和依赖关系。可以使用Spring的XML命名空间和标签来简化配置。
  2. 注解配置:通过在Bean类上添加注解,如@Component、@Service、@Repository等来标识Bean的角色和作用。
  3. javaConfig方式:使用JAVA类来配置Bean,通过编写一个配置类,使用@Configuration注解表示,然后在方法上使用@Bean注解来定义Bean。
  4. @Import:@Import注解可以用于导入其他配置类,也可以用于导入其他普通类。当导入的是配置类时,被导入的配置类中定义的Bean会被纳入到当前配置类的上下文;当导入的是普通类的时候,被导入的类会被当做一个Bean进行注册。
BeanFactory和ApplicationContext的区别

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当作Spring的容器,ApplicationContext是BeanFactory的子接口。

BeanFactory是Spring框架的核心接口之一,可称之为“低级容器”。因为Bean的生产过程分为【配置的解析】和【Bean的创建】,而BeanFactory只有Bean的创建功能,但是它内存占用更加小,在早期会在一些内存受限的可穿戴设备中作为Spring容器使用。

ApplicationContext可以称之为“高级容器”。比BeanFactory多了更多的功能,继承了多个接口,具备更多功能。例如配置的读取、解析、扫描等。还加入了如事件监听机制以及后置处理器让Spring提升了扩展性。该接口定义了一个Refresh方法用于刷新整个容器,即重新加载/刷新所有Bean。

初始化时机:

  • BeanFactory:当使用getBean方法请求Bean时,Bean会被创建或者实例化。这意味着如果Bean配置有误的话,错误不会立即显现,直到Bean被首次请求时。
  • ApplicationContext:在容器启动的时候,会预加载所有的Bean定义并创建单例Bean,因此任何错误配置都会在启动阶段被检测到。
BeanFactory和FactoryBean的区别
  • BeanFactory是Spring框架的核心接口之一,用于管理和获取Bean对象,所以也可称为bean的容器。使用了简单工厂模式,提供getBean方法获取Bean。

  • FactoryBean是一个Bean,但是比较特殊。它是一个接口,且必须被一个Bean实现。

    FactoryBean接口定义了两个方法:getObject()和getObjectType()。

    getObjectType()用于返回创建的Bean的类型。

    getObject()用于返回创建的Bean的对象,最终该Bean对象会进行注入,MyBatis集成Spring的时候的SqlSessionFactoryBean就实现了FactoryBean,最终通过getObject()将SqlSessionFactory注入到IOC容器中。

Spring IOC容器的加载过程

Spring IOC容器的工作过程,可以分为两个阶段:配置解析Bean的创建阶段。

  • 配置解析主要工作是加载和解析配置文件,将配置的Bean解析成BeanDefinition

过程如下:

  1. 读取配置:通过BeanDefitionReader读取配置文件或配置类
  2. 解析配置信息:如ComponentScan、Bean配置等
  3. 扫描类注解:根据ComonentScan扫描@Component、@Bean、@Configuration、@Import等注解
  4. 将符合的Bean注册为BeanDefinition
  • Bean的创建过程主要是根据BeanDefintion创建Bean

过程如下:

  1. 实例化Bean:容器根据配置文件中的Bean定义,实例化Bean对象。
  2. 注入Bean属性:容器会为实例化的Bean对象设置属性值,可以通过Setter方法或者是构造函数注入。
  3. 处理依赖关系:容器会处理Bean之间的依赖关系,将依赖的Bean注入到需要注入的地方。
  4. 执行初始化方法:容器会调用初始化方法,可以通过实现initializingBean接口在配置文件中自定义初始化逻辑。
  5. 注册Bean:容器会将实例化、属性设置和初始化完成的Bean对象注册到容器中,以便后续的使用和管理。
  6. 完成加载:容器完成所有Bean的加载和初始化后,即完成了IOC容器的加载过程。此时可以通过容器调用getBean获取Bean对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

末、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值