Spring面试题
Spring面试题
1.谈谈你对Spring的理解?
- Spring是一个轻量级Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级
开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
- Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的
两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspectoriented programming,AOP)。
2.什么是SpringIOC?
- IOC即控制反转,Inversion of Control,把传统的由程序代码直接操控的对象调用权交给Spring容器,通过容器实现对象的组件的装配和管理。所谓控制反转,就是对组件对象控制权的转移,从程序代码转移到了外部容器
- Spring IOC负责创建对象,管理对象,通过依赖注入,装配对象,配置对象,并管理对象的整个生命周期对 IOC来说,最重要的就是容器,容器管理Bean的生命周期,控制Bean的依赖注入
IOC有什么用?
- 管理对象的创建和依赖关系的维护。
- 解耦,由容器去维护具体的对象
- 托管了类的产生过程,比如我们需要在类的产生过程做一些处理,比如代理,如果有容器就可以吧这部分交给容器,应用程序则无需关心如何完成代理的
举例
UserService userService = newUserService() //耦合度太高,维护不方便
引入IOC,就是将创建对象的控制权交给Spring容器,以前由程序员自己创建,现在交给SpringIOC去创建,使用的时候就可以通过DI(@Autowired)去使用,而且默认是单例的
IOC的优点?
- 最小的代价和最小的侵入性使得松散耦合得以实现
- IOC容器支持加载服务时的饿汉式加载和懒汉式加载
IOC的实现机制是什么:工厂+反射
3.Spring的优缺点是什么?
优点
- 方便解耦,简化开发
Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。
- AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
- 声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程。
- 方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序。
- 方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、
Hibernate、MyBatis等)。
- 降低JavaEE API的使用难度
Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使
这些API应用难度大大降低。
缺点
- Spring明明一个很轻量级的框架,却给人感觉大而全
- Spring依赖反射,反射影响性能
- 使用门槛升高,入门Spring需要较长时间
4.Spring 框架中都用到了哪些设计模式?
- 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
- 单例模式:Bean默认为单例模式。
- 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
- 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
- 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
5.ApplicationContext 和BeanFactory的区别?
BeanFactory是Spring的核心组件,BeanFactory是Spring里面最底层的接口,定义了Ioc的基本功能,包含各种Bean的定义、
加载、实例化,依赖注入和生命周期管理;而ApplicationContext是BeanFactory的子接口。
ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
①继承MessageSource,因此支持国际化
②资源文件访问,如URL和文件(ResourceLoader)
3提供在监听器中注册bean的事件;
6.ApplicationContext通常的实现是什么?
- FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean配置文件的全路径名必须提供给它的构造函数。
- ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
- WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
7.什么是Spring的依赖注入?
依赖注入(Dependency Injection),简称DI,类之间的依赖关系由容器来负责。简单来讲a依赖b,但a不创建(或销毁)b,仅使用b,b的创建(或销毁)交给容器。
8.有哪些不同类型的依赖注入实现方式?
依赖注入是时下最流行的IOC实现方式,依赖注入分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。其中接口注入由于
在灵活性和易用性比较差,现在从Spring4开始已被废弃。
- 构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
- Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入
9.如何给Spring 容器提供配置元数据?Spring有几种配置方式
- XML配置文件。
- 基于注解的配置。
- 基于java的配置。
10.解释Spring支持的几种bean的作用域
Spring框架支持以下五种bean的作用域:
- singleton : bean在每个Spring ioc 容器中只有一个实例。
- prototype:一个bean的定义可以有多个实例。
- request:每次http请求都会创建一个bean,该作用域仅在基于web的SpringApplicationContext情形下有效。
- session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
- global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
11.Spring框架中的单例bean是线程安全的吗?
- 不是,Spring框架中的单例bean不是线程安全的。
- spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。实际上大部分时候 spring bean 无状态的(比如 dao 类),
所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,
- 最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于new Bean()了,所以就可以保证线程安全了。
- 有状态就是有数据存储功能。
- 无状态就是不会保存数据。
12.Spring如何处理线程并发问题?
- 在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处
理,解决线程安全问题。
- ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要
排队。而ThreadLocal采用了“空间换时间”的方式。
- ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提
供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
13.Spring框架中bean的生命周期
- 1.实例化
-- 通过反射推断构造函数进行实例化
- 2.属性赋值
-- 解析自动装配,DI的体现
-- 循环依赖的解决
- 3.初始化
-- 调用很多的--Aware,比如BeanNameAware,BeanClassLoaderAware的回调方法
-- 调用初始化生命周期的回调
-- 如果Bean实现了AOP,那么创建动态代理
- 4.销毁
-- 在Spring容器关闭的时候进行调用
14.什么是bean的自动装配?
意思就是spring会在上下文中自动寻找,并自动给bean装配属性。
15.spring 自动装配 bean 有哪些方式?
在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。
在Spring框架xml配置中共有5种自动装配:
- no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
- byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
- byType:通过参数的数据类型进行自动装配。
- constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
- autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用byType的方式自动装配。
16.@Autowired注解自动装配的过程是怎样的?
- 使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。
- 在启动spring IOC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IOC容器自动查找需要的bean,
- 并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
- 如果查询的结果不止一个,那么@Autowired会根据名称来查找;如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
17.什么是基于Java的Spring注解配置?
- 基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。
- 以@Configuration 注解为例,Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean,相当于之前写的xml文件。
- 另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文,相当在xml中定义Bean标签。
18.@Component, @Controller, @Repository, @Service 有何区别?
- @Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
- @Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IOC 容器中。
- @Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
- @Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IOC 容器,并使未经检查的异常有资格转换为 Spring
DataAccessException。
19.@Autowired 注解有什么作用
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,
包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
20.@Autowired和@Resource之间的区别
@Autowired和@Resource之间的区别在于
- @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)
- @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。
21.Spring的事务传播行为
- ① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就 加入该事务,该设置是最常用的设置。
- ② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不 存在事务,就以非事务执行。
- ③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前 不存在事务,就抛出异常。
- ④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
- ⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前 事务挂起。
- ⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- ⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则 按REQUIRED属性执行。
22.spring 的事务隔离?
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离
级别和数据库的隔离级别一致:
1.ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
2.ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
3.ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
4.ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
5.ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
23.什么是AOP?有哪些实现方式?
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
- AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
- Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在
特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
- JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和
业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
- 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
- CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,
因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体
入参, 在方法反射调用时使用。
24.Spring AOP里面的几个名词
(1)切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
(2)连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插
入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
(3)通知(Advice):在AOP术语中,切面的工作被称为通知。
(4)切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
(5)引入(Introduction):引入允许我们向现有类添加新方法或属性。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行
时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
(7)织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:
25.SpringAOP通知有哪些类型?
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning ):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
26.Spring容器的启动流程
- 首先会进行扫描,扫描到所有的BeanDefinition对象,并存在一个Map中;
- 然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例Bean不需要在启动时去创建,而是在每次获取Bean时利用BeanDefinition去创建
- 接着利用BeanDefinition创建Bean,即Bean的创建生命周期,期间包括,BeanDefinition,推断构造方法,实例化,属性填充,初始化前,初始化,初始化后等步骤,AOP就是发生在初始化后这一步
- 单例Bean创建完之后,Spring会发布一个容器启动事件
- Spring启动结束
- Spring启动中还会去处理@Import等注解
27.哪些情况下会导致Spring事务失效?原因是什么
- 方法内自调用:Spring事务是基于AOP的,只要使用代理对象调用某个方法时,Spring事务才能生效,而在一个方法内调用this.method()时,this并不是代理对象,所以会导致事务失效。
解决方法1:把调用方法拆分到另外一个Bean中
解决方法2:启用@EnableAspectAutoPorxy(exposeProxy = true)+AopContext.currentProxy()
- 方法是private:Spring事务会给予CGLIB进行AOP,而CGLIB会基于继承父类来实现,如果父类中的方法是private,则子类中无法重写,也就没办法进行Spring的事务逻辑了
- 方法是final的:原因类似,final的方法无法重写
- 单独的线程调用方法:当mybatis或者jdbcTemplate执行sql时,会从ThreadLocal中拿链接对象,如果开启事务的线程和执行sql的是同一个线程,就能拿到链接对象,
如果不是则拿不到,这样Mybatis或JDBCTemplate就会自己新建一个链接去执行sql,此数据库链接的autocommit= true,那么执行完sql就会提交,后续再抛异常也不能回滚之前提交的sql了
- 异常被吃掉:如果Spring事务没有捕获到异常,就不会回滚,默认Spring会捕获RuntimeException和Error
- 类没有被Spring管理
- 数据库不支持事务
28.Spring循环依赖问题
1.什么是循环依赖问题:
- 两个或则两个以上的对象互相依赖对方,最终形成 闭环 。例如 A 对象依赖 B 对象,B 对象也依赖 A 对象
2.循环依赖会有什么问题?
- 对象的创建过程会产生死循环,类似如下
3.Spring 是如何解决的呢?
- 通过三级缓存提前暴露对象来解决的
4.三级缓存里面分别存的什么?
- 一级缓存里存的是成品对象,实例化和初始化都完成了,我们的应用中使用的对象就是一级缓存中的
- 二级缓存中存的是半成品,用来解决对象创建过程中的循环依赖问题
- 三级缓存中存的是 ObjectFactory<?> 类型的 lambda 表达式,用于处理存在 AOP 时的循环依赖问题
5.为什么要用三级缓存来解决循环依赖问题(只用一级缓存行不行,只用二级缓存行不行)
- 只用一级缓存也是可以解决的,但是会复杂化整个逻辑
- 半成品对象是没法直接使用的(存在 NPE 问题),所以 Spring 需要保证在启动的过程中,所有中间产生的半成品对象最终都会变成成品对象
- 如果将半成品对象和成品对象都混在一级缓存中,那么为了区分他们,势必会增加一些而外的标记和逻辑处理,这就会导致对象的创建过程变得复杂化了
- 将半成品对象与成品对象分开存放,两级缓存各司其职,能够简化对象的创建过程,更简单、直观
- 如果 Spring 不引入 AOP,那么两级缓存就够了,但是作为 Spring 的核心之一,AOP 怎能少得了呢
- 所以为了处理 AOP 时的循环依赖,Spring 引入第三级缓存来处理循环依赖时的代理对象的创建
6、三级缓存各自的作用
- 第一级缓存存的是对外暴露的对象,也就是我们应用需要用到的
- 第二级缓存的作用是为了处理循环依赖的对象创建问题,里面存的是半成品对象或半成品对象的代理对象
- 第三级缓存的作用处理存在 AOP + 循环依赖的对象创建问题,能将代理对象提前创建
7.Spring 为什么要引入第三级缓存
- 严格来讲,第三级缓存并非缺它不可,因为可以提前创建代理对象
- 提前创建代理对象只是会节省那么一丢丢内存空间,并不会带来性能上的提升,但是会破环 Spring 的设计原则
- Spring 的设计原则是尽可能保证普通对象创建完成之后,再生成其 AOP 代理(尽可能延迟代理对象的生成)
- 所以 Spring 用了第三级缓存,既维持了设计原则,又处理了循环依赖;牺牲那么一丢丢内存空间是愿意接受的.
29.BeanFactory和FactoryBean的区别
区别:BeanFactory是个Factory,也就是IOC容器或对象⼯⼚,FactoryBean是个Bean。在Spring中, 所有的Bean都是由BeanFactory(也就是IOC容器)来进⾏管理的。
但对FactoryBean⽽⾔,这个Bean不是简单的Bean,⽽是⼀个能⽣产或者修饰对象⽣成的⼯⼚Bean, 它的实现与设计模式中的⼯⼚模式和修饰器模式类似。
BeanFactory,以Factory结尾,表示它是⼀个⼯⼚类(接⼝), 它负责⽣产和管理bean的⼀个⼯⼚。在 Spring中,BeanFactory是IOC容器的核⼼接⼝,它的职责包括:实例化、定位、配置应⽤程序中的 对象及建⽴这些对象间的依赖。
BeanFactory只是个接⼝,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常⽤的⼀个,该实现将以XML⽅式描述组成应⽤的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并⽤它来构建⼀个完全可配置的系统或应⽤。 都是附加了某种功能的实现。它为其他具体的IOC容器提供了最基本的规范,例如 DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext 等具体的容器都是实现了 BeanFactory,再在其基础之上附加了其他的功能。
BeanFactory和ApplicationContext就是spring框架的两个IOC容器,现在⼀般使⽤ApplicationnContext,其不但包含了BeanFactory的作⽤,同时还进⾏更多的扩展。
BeanFacotry是spring中⽐较原始的Factory。如XMLBeanFactory就是⼀种典型的BeanFactory。 原始的BeanFactory⽆法⽀持spring的许多插件,如AOP功能、Web应⽤等。ApplicationContext接⼝, 它由BeanFactory接⼝派⽣⽽来, ApplicationContext包含BeanFactory的所有功能,通常建议⽐BeanFactory优先ApplicationContext以⼀种更向⾯向框架的⽅式⼯作以及对上下⽂进⾏分层和实现继承,ApplicationContext包还提供了以下的功能:
- MessageSource, 提供国际化的消息访问
- 资源访问,如URL和⽂件
- 事件传播
- 载⼊多个(有继承关系)上下⽂ ,使得每⼀个上下⽂都专注于⼀个特定的层次,⽐如应⽤的web层;
BeanFactory提供的⽅法及其简单,仅提供了六种⽅法供客户调⽤:
- boolean containsBean(String beanName) 判断⼯⼚中是否包含给定名称的bean定义,若有则返回true
- Object getBean(String) 返回给定名称注册的bean实例。根据bean的配置情况,如果是 singleton模式将返回⼀个共享实例,否则将返回⼀个新建的实例,如果没有找到指定bean,该⽅法可能会抛出异常
- Object getBean(String, Class) 返回以给定名称注册的bean实例,并转换为给定class类型
- Class getType(String name) 返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除NoSuchBeanDefinitionException异常
- boolean isSingleton(String) 判断给定名称的bean定义是否为单例模式
- String[] getAliases(String name) 返回给定bean名称的所有别名
⼀般情况下,Spring通过反射机制利⽤ <bean><bean> 的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程⽐较复杂,如果按照传统的⽅式,则需要在 <bean> <bean> 中提供⼤量的配置信息。配置⽅式的灵活性是受限的,这时采⽤编码的⽅式可能会得到⼀个简单的⽅案。
Spring为此提供了⼀个org.springframework.bean.factory.FactoryBean的⼯⼚类接⼝,⽤户可以通过实现该接⼝定制实例化Bean的逻辑。FactoryBean接⼝对于Spring框架来说占⽤重要的地位,Spring⾃身就提供了70多个FactoryBean的实现。它们隐藏了实例化⼀些复杂Bean的细节,给上层应⽤带来了便利。从Spring3.0开始,FactoryBean开始⽀持泛型,即接⼝声明改为 FactoryBean<T> 的形式以Bean结尾,表示它是⼀个Bean,不同于普通Bean的是:它是实现了 FactoryBean<T> 接⼝的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,⽽不是FactoryBean本身,如果要获取FactoryBean对象,请在id前⾯加⼀个&符号来获取。
例如⾃⼰实现⼀个FactoryBean,功能:⽤来代理⼀个对象,对该对象的所有⽅法做⼀个拦截,在调⽤前后都输出⼀⾏LOG,模仿ProxyFactoryBean的功能。FactoryBean是⼀个接⼝,当在IOC容器中的Bean实现了FactoryBean后,通过getBean(StringBeanName)获取到的Bean对象并不是FactoryBean的实现类对象,⽽是这个实现类中的getObject()⽅法返回的对象。要想获取FactoryBean的实现类,就要getBean(&BeanName),在BeanName之前加上&。
总结
BeanFactory是个Factory,也就是IOC容器或对象⼯⼚,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进⾏管理的。
但对FactoryBean⽽⾔,这个Bean不是简单的Bean,⽽是⼀个能⽣产或者修饰对象⽣成的⼯⼚Bean,它的实现与设计模式中的⼯⼚模式和修饰器模式类似
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了