【Spring】Spring面试常见问题

目录

说一说IOC

IOC底层原理(xml,反射,工厂模式)

Spring Bean的四个阶段生命周期

Spring中的设计模式

Spring 中的 aware 接口

BeanFactory 和 FactoryBean的区别

Spring事务


说一说IOC

IOC就是控制反转,是一种思想,把你设计好的对象交给Spirng容器(BeanFactory,ApplicationContext)控制,从而降低程序集间耦合度

通过 DI (依赖注入)把对于属性的值注入到具体的对象中,@Autowire,populateBean 都可以完成属性值的注入

ioc容器:spring就是用一个ConcurrentHashMap的数据结构来作为Ioc容器的,key就是beanName,value就是Bean的实例对象,

过程1:容器的创建BeanFactory接口下面DefaultListableBeanFactory创建容器,同时向容器中设置一些参数

过程2:加载解析Bean对象:准备要创建Bean对象的定义对象BeanDefinition,(通过XML和注解的方式,这些方式是通过BeanDefinitionReader接口实现的)BeanDefinition 用于保存 Bean 的相关信息,包括属性、构造方法参数、依赖的 Bean 名称及是否单例、延迟加载等,它是实例化 Bean 的原材料。

关于 BeanDefinitionReader 的结构图如下:

  • AbstractBeanDefinitionReader:实现了 EnvironmentCapable,提供了获取/设置环境的方法。定义了一些通用方法,使用策略模式,将一些具体方法放到子类实现。
  • XmlBeanDefinitionReader:读取 XML 文件定义的 BeanDefinition
  • PropertiesBeanDefinitionReader:可以从属性文件,Resource,Property 对象等读取 BeanDefinition
  • GroovyBeanDefinitionReader:可以读取 Groovy 语言定义的 Bean

过程三:BeanFactoryPostProcessor的处理(Spring对外暴露的扩展点):在Bean实例化之前对bean定义进行修改,通过设置’order’属性来确定各BeanFactoryPostProcessor执行顺序(用法:解析XML引入资源配置文件里的值)

过程四:BeanPostProcessor的注册(Spring对外暴露的扩展点):

  • 对外提供了对bean实例化后操作的空间,主要是涉及到对bean执行初始化方法前后调用对应方法实现对bean的各种功能增强,(用法:比如spring aop是利用BeanPostProcessor实现的代理增强)

过程五:通过反射的方式将BeanDefinition对象实例化成具体Bean对象

过程六:Bean对象的初始化

  1. populateBean方法 填充属性
  2. 调用aware子类的方法 (辅助Spring访问容器中的数据)
  3. 调用BeanPostProcessor的前置处理方法 postProcessBeforeInitialization 方法
  4. 调用init-method 方法
  5. 调用BeanPostProcessor的后置处理方法 postProcessAfterInitialization 方法

过程七:生成完整的Bean对象,通过getBean方法直接获取

过程八:销毁Bean对象


IOC底层原理(xml,反射,工厂模式)

  1. 先通过 createBeanFactory 创建一个Bean工厂
  2. 开始循环创建对象,因为容器中的bean默认是单例的,优先通过getBean从容器中查找对象
  3. 找不到,通过 createBean 的方法以反射的方式创建对象(一般使用无参构造方法 getDeclaredConstructor.newInstance)
  4. 通过 populateBean 进行对象的属性填充
  5. initializingBean 方法进行其他初始化操作

Spring Bean的四个阶段生命周期

 

Spring生命周期包括四个阶段

  1. 实例化(Instantiation)
  2. 属性赋值(Populate)
  3. 初始化(Initialization)
  4. 销毁(Destruction)

实例化

  • 实例化Bean:通过反射的方式生成Bean对象

属性赋值

  • 填充Bean属性(依赖注入):populateBean方法

初始化

  • 调用aware接口相关方法:获得BeanName,BeanClassLoader等属性配置
  • 调用BeanPostProcessor中的前置处理方法(设置ApplicationContext)
  • 调用 initmethod 方法初始化对象
  • 调用BeanPostProcessor中的后置处理方法(AOP)
  • 调用 getBean 方法获取对象

销毁

  • 调用 destroymethod 方法 销毁对象 

Spring中的设计模式

单例模式:生成的IOC容器,默认的bean都是单例的

工厂模式:beanfactory生成bean

责任链模式:使用AOP的时候会先生成一个拦截器链

代理模式:AOP的底层动态代理

观察者模式:listener监听器 @EventListener注解使用,event事件

装饰器模式:BeanWrapper,对Bean的一种包装,包括对Bean的属性、方法,数据等。

模板方法模式:PostProcessBeanFactory,提供给想要实现BeanPostProcessor的三方框架使用

策略模式:XmlBeanDefinitionReader,BeanDefinition的一种实现

适配器模式:Adapter


Spring 中的 aware 接口

简介

  • Spring框架中提供了许多实现了Aware接口的类,这些类主要是为了辅助Spring访问容器中的数据,比如BeanNameAware,这个类能够在Spring容器加载的过程中将Bean的名字(id)赋值给变量。

常用的Aware

  • BeanNameAware:能够获取bean的名称,即是id
  • BeanFactoryAware:获取BeanFactory实例
  • ApplicationContextAware:获取ApplicationContext
  • MessageSourceAware:获取MessageSource
  • ResourceLoaderAware:获取ResourceLoader
  • EnvironmentAware:获取Environment

ApplicationContextAware

  • ApplicationContext可以获取容器中的bean,但是必须注入才能使用,当一些类不能注入的时候怎么才能获得bean呢?比如Utils中的类,通常不能直接通过注入直接使用ApplicationContext,此时就需要借助ApplicationContextAware这个接口了。
  • ApplicationContextAware的实现类如下
/**
 * 自定义一个实现类,一定要注入到容器中
 */
@Component
public class ApplicationContextAwareImpl implements ApplicationContextAware {
    /**
     * 容器启动的时候会调用这个方法,只需要将applicationContext设置即可
     * @param applicationContext 容器启动会自动注入
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //将其设置到ApplicationContextUtil
        ApplicationContextUtil.setApplicationContext(applicationContext);
    }
}
  • ApplicationContextUtil如下:
import org.springframework.context.ApplicationContext;
/**
 * ApplicationContext的工具类
 */
public class ApplicationContextUtil {
    /**
     * ApplicationContext对象,会ApplicationContextAwareImpl中的setApplicationContext方法中赋值
     */
    private static ApplicationContext applicationContext;
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
    public static void setApplicationContext(ApplicationContext applicationContext) {
        ApplicationContextUtil.applicationContext = applicationContext;
    }
    /**
     * 根据类型获取指定的bean
     * @param requiredType Class
     * @param <T> 泛型
     * @return
     */
    public static <T> T getBean(Class<T> requiredType ){
        return applicationContext.getBean(requiredType);
    }
    /**
     * 根据名称和类型获取Bean
     * @param name bean的id
     * @param requiredType class
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name,Class<T> requiredType){
        return applicationContext.getBean(name,requiredType);
    }
}
  • 配置了如上的工具类,那么就可以直接使用ApplicationContextUtil获取ApplicationContext对象了,而不需要注入了,如下:
	StringRedisTemplate redisTemplate=ApplicationContextUtil.getBean("stringRedisTemplate",StringRedisTemplate.class);

总结

  • spring底层的一些东西并不能通过自动注入直接从ioc容器中获取,但是spring提供了其他的一些方法获取相应的对象,比如一些

    Aware
    

    要向成功获取指定的对象,必备的条件如下:

    • 实现xxxAware接口
    • 自定义的类注入到容器中

BeanFactory 和 FactoryBean的区别

相同点

都是用来创建bean对象的

不同点

BeanFactory创建对象交给spring容器管理的时候,必须要遵循Bean生命周期流程

FactroyBean是接口,自定义某个对象的创建,同时创建完成的对象交给spring容器来管理

FactoryBean接口需要实现的三个方法

  • isSingleton:是否是单例对象
  • getObjectType:获取返回对象的类型
  • getObject:自定义创建对象的过程(new,反射,动态代理)

Spring事务

Spring 事务分为两种,声明式和编程式, 声明式就是在方法或者接口上加 @Transactional的注解, 这样就可以交给Spring 管理它的提交,回滚,等等, 编程式就是用spring提供的模板 ,通过回调方法实现. 不管使用哪一种,最后事务的执行入口都是TransactionInterceptor的invoke方法,

数据库中事务进行流程如下:

声明式事务管理建立在AOP之上的。事务通过一个 TransactionIntercepter ,然后调用 invoke 方法实现具体事务逻辑其本质是对方法前后进行拦截,然后在目标方法开始之前创建数据库连接或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

使用说明

  • 在启动类上添加@EnableTransactionManagement注解。
  • 用于类上时,该类的所有公共 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
  • 在项目中,@Transactional(rollbackFor=Exception.class),如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
  • 在@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。

注解失效问题

正常情况下,只要在方法上添加@Transactional注解就完事了,但是需要注意的是,虽然使用简单,但是如果不合理地使用注解,还是会存在注解失效的问题。

@Transactional 应用在非公共 修饰的方法上

事务拦截器在目标方法执行前后进行拦截,内部会调用方法来获取Transactional 注解的事务配置信息,调用前会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

@Transactional 注解属性 rollbackFor 设置错误

rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定rollbackFor属性。

同一个类中方法调用,导致@Transactional失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。
那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

异常被你的 catch “吃了”导致@Transactional失效

如果你手动的catch捕获这个异常并进行处理,事务管理器会认为当前事务应该正常commit,就会导致注解失效,如果非要捕获且不失效,就必须在代码块内throw new Exception抛出异常。

数据库引擎不支持事务

开启事务的前提就是需要数据库的支持,我们一般使用的Mysql引擎时支持事务的,所以一般不会出现这种问题。

开启多线程任务时,事务管理会受到影响

因为线程不属于spring托管,故线程不能够默认使用spring的事务,


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值