1、Spring框架中的Bean的作用域
- singleton:Spring只会为该bean对象创建唯一实例,Spring中的bean默认都是单例
- prototype:每次获取bean,Spring会创建一个新的实例
- requset:每一次HTTP请求,Spring会创建一个新的bean实例
- session:不同的HTTP会话,Spring会创建不同的的bean实例
XML
#通过XML方式设置bean的作用域
<bean id="demoDaoBean" class="com.apesource.dao.DemoDAOImpl" scope="singleton"/>
XML
//通过注解方式设置bean的作用域
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DemoDAOImpl implements IDemoDAO{ }
2、Spring框架中Bean的线程安全
- 对于 prototype 作用域的 Bean,每次都创建一个新对象 ,也就是线程之间不存在Bean共享,因此不会有线程安全问题
- 对于 singleton 作用域的 Bean,所以的线程都共享一个单例状态的Bean,存在资源竞争,因此时存在线程安全的
- 解决办法
- 对于 singleton 作用域的单例Bean,他的线程安全问题,有常见的两种解决办法
- 在bean 中尽量避免定义可变的成员变量(用于保存数据的成员变量);
- 在类中定义一个ThreadLocal成员变量,将需要可变的成员变量保存在ThreadLocal中;
- 对于 singleton 作用域的单例Bean,他的线程安全问题,有常见的两种解决办法
3、Spring框架中的Bean声生命周期
Spring Bean的生命周期总体分为四个阶段:实例化=>属性化=>初始化=>销毁
1、实例化Bean:
根据配置文件中Bean的定义,利用Java Reflection反射技术创建Bean的实例
2、注入对象的属性值(或对象)
3、处理各种Aware接口:
Spring会检测Bean是否实现了xxxAware接口,通过Aware类型的接口,可以让Spring框架为当前Bean注入响应的内容
- 如果 Bean 实现 BeanNameAware 接口,会调用它实现的 setBeanName(String beanId)方法,注入 Bean 的名字;
- 如果 Bean 实现 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,注入ClassLoader 对象的实例;
- 如果 Bean 实现 BeanFactoryAware 接口,会调用它实现的setBeanFactory()方法,注入的是 Spring 工厂;
- 如果 Bean 实现 ApplicationContextAware 接口,会调用 setApplicationContext()方法,注入 Spring 上下文;
4 、执行BeanPostProcessor前置处理:
如果想对 Bean进行一些自定义的前置处理,那么可以让 Bean 实现了BeanPostProcessor 接口,将会在该阶段调用 postProcessBeforeInitialization(Object obj, String s)方法。
5、 执行InitializingBean初始化方法:
如果 Bean 实现了InitializingBean 接口,执行 afeterPropertiesSet()方法。
6、执行init-method自定义初始化方法:
如果 Bean 在 Spring 配置文件中配置了init-method 属性,则会自动调用其配置的初始化方法。
7、执行BeanPostProcessor后置处理:
如果这个 Bean 实现了BeanPostProcessor接口,将会调用 postProcessAfterInitialization(Object obj,String s)方法,由于这个方法是在 Bean初始化结束后调用
8、执行DisposableBean销毁Bean:
当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用其实现的destroy()方法执行销毁;
9、执行destroy-method自定义销毁方法:
如果这个Bean的Spring 配置中配置了 destroy-method 属性,会自动调用其配置的自定义销毁方法。
4、Sprng框架如何解决循环依赖
循环依赖问题是指:类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。例如A类依赖了B类,B类依赖了C类,而最后C类又依赖了A类,这样就形成了循环依赖问题。
为了解决循环依赖问题,Spring容器采用了以下几种策略:
三级缓存:Spring容器使用三级缓存来解决属性循环依赖。
- 第一级缓存是SingletonObjects,用于存储完全初始化的bean;
- 第二级缓存是earlySingletonObjects,用于存储已经创建但尚未初始化的bean;
- 第三级缓存是singletonFactories,用于存储用于创建bean的ObjectFatory
- 当容器遇到循环依赖时,它会尝试从第二级或第三级缓存中获取bean,而不是直接实例化它。
延迟初始化 :Spring容器允许延迟初始化bean,这意味着在需要时才创建和初始化bean。这有助于避免在循环依赖的情况下过早地实例化bean。
代理对象 :对于通过AOP增强的bean,Spring容器会创建一个代理对象来替代原始bean。这个代理对象负责处理循环依赖,同时保留原始bean的行为。
5、Spring框架中有那些注解
- 用于声明的注解:
- @Component:通用Bean的注解,可标注任意类为Bean如果一个Bean不知道属于哪个层,可以使用@Component注解标注。
- @Repository:定义数据访问层Bean的注解。
- @Service:定义业务层Bean的注解。
- @Controller:定义控制器Bean的注解。
- 用于注入的注解:
- @Autowired:按类型自动注入
- @Qualifier::按名称自动注入
- 声明配置、扫描、启用特性的注解:
- @Configuration:声明配置类
- @ComponentScan:组件扫描
- @EnableScheduling:任务调度
- @EnableAspectJAutoProxy:启动自动代理工程、
6、Spring框架中用到的设计模式
- 工厂模式:Spring 使用工厂模式,通过 BeanFactory 或 ApplicationContext 来创建对象;
- 单例模式:Bean 默认作用域为单例,按照单例设计模式进行设计实现;
- 策略模式: Resource 的实现类,针对不同的资源文件,实现了不同方式的资源获取策略;
- 代理模式: Spring 的 AOP 的实现依靠动态代理(JDK的反射和CGLIB);
- 模板方法: Spring 提供了 JdbcTemplate,RedisTemplate 等模板对象,将相同的操作步骤进行了封装;
- 适配器模式:Spring AOP 的增强或通知(Advice)使用到了适配器模式, Spring MVC 中也用到了适配器模式适配 Controller;
7、Spring 框架中AOP的基本理解
关键字:AOP名词解释,AOP实现原理(动态代理)
- AOP(Aspect-Oriented Programming:面向切面编程):将那些与业务无关,但为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为"切面"(Aspect),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
- Spring AOP 基于动态代理实现:
- 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象( JDK 动态代理的核心是 InvocationHandler接口和 Proxy类);
- 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy去进行代理了,这时侯SpringAOP会使用使用Cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(Cglib 动态代理的核心是 MethodInterceptor 接口和 Enhancer类);
8、Spring AOP和AspectJ AOP有什么区别?
关键字:增强方式的差异(运行时、编译时),实现方式的差异(动态代理、字节码操作)
- Spring AOP 已经集成了 AspectJ, Aspect] 是一个 Java 技术生态系统中实现 AOP 编程思想的独立框架; AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单更容易;
- Spring AOP 属于运行时增强,而 AspectJ是编译时增强;
- Spring AOP 基于动态代理( Proxying),而 AspectJ基于字节码操作( Bytecode Manipulation );
9、Spring AOP有哪些通知类型?
关键字: 分别介绍每种通知的实现接口,执行方式
- 前置通知:实现 MethodBeforeAdvice 接口,在目标方法调用前,执行通知;
- 环绕通知:实现 MethodInterceptor 接口,是一个包围目标方法的通知。环绕通知可以在方法调用前后完成自定义的行为。
- 后置通知:实现 AfterReturningAdvice 接口,在在目标方法调用后,执行通知(如果方法抛出异(=常,则不执行通知);
- 异常通知:实现 ThrowsAdvice 接口,在方法抛出异常时,执行通知;
10、Spring 管理事务的方式有几种?
- 编程式事务:在代码中硬编码(不推荐使用):通过TransactionTemplate或者TransactionManager手动个管理事物实际应用中很少使用,用于理解 Spring 事务管理。
- 声明式事物:在 XML 配置文件或者基于注解@Transactional (推荐使用),通过 AOP实现。
11、Spring 事务中有哪几种事务传播行为?
事务传播行为是为了解决业务层方法之间互相调用时,产生事务问题。
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务传播行为有如下分类:
- TransactionDefinition.PROPAGATION_REQUIRED
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 @Transactional
注解默认使用的事物传播行为
- TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
- TransactionDefinition.PROPAGATION NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取
值等价于TransactionDefinition.PROPAGATION_REQUIRED。
- TransactionDefinition.PROPAGATIÓN_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:代表强制性)
- TransactionDefinition.PROPAGATION_SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER
以非事务方式运行,如果当前存在事务,则抛出异常。
12、Spring 事务中有哪几种事务隔离级别?
- TransactionDefinition.ISOLATION_DEFAULT 默认隔离级别
使用当前数据库的默认隔离级别,MySQL 默认采用的是可重复读 REPEATABLE_READ 隔离级
别。Oracle 默认采用的是读已提交 READ_COMMITTED 隔离级别.
2.TransactionDefinition.ISOLATION_READ_UNCOMMITTED 读未提交
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- TransactionDefinition.ISOLATION_READ_COMMITTED 读已提交
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- TransactionDefinition.ISOLATION_REPEATABLE_READ 可重复读
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- TransactionDefinition.ISOLATION_SERIALIZABLE 串行化
最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读,不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。