spring

Spring 框架中都用到了哪些设计模式?

单例模式——Bean的默认scope

工厂模式——用BeanFactory创建对象

代理模式——AOP

模板方法——jdbcTemplate, RestTemplate

前端控制器——DispatchServlet来对请求分发

可以通过多少种方式完成依赖注入?各种方式比较

spring中使用构造注入和setter注入这两种。没有绝对好坏之分,区别主要在于使用场景不同。

建议采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无需变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入。

两种容器BeanFactory和ApplicationContext 

BeanFactory只提供容器最基本的IOC,AOP功能,ApplicationContext则在此基础上扩展,提供了以下功能

  1. 继承 MessageSource,提供国际化的标准访问策略。
  2. 继承 ApplicationEventPublisher ,提供强大的事件机制。
  3. 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源。
  4. 对 Web 应用的支持。

BeanFactory 粗暴简单,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。

ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。

Spring IOC原理机制

工厂模式+反射机制,工作原理步骤如下:

a. 加载配置文件,解析成 BeanDefinition 放在 Map 里。

b. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。

Spring有哪些事件

Spring监听事件的方法:实现ApplicationListener 接口,重写onApplicationEvent()方法,如下

public class AllApplicationEventListener implements ApplicationListener<ApplicationEvent> {  
    
    @Override  
    public void onApplicationEvent(ApplicationEvent applicationEvent) {  
        // process event  
    }
    
}
  1. 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext 被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的 #refresh() 方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext 的 #start() 方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用 ConfigurableApplicationContext 的 #stop() 方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext 被关闭时触发该事件。容器被关闭时,其管理的所有单例 Bean 都被销毁。
  5. 请求处理事件(RequestHandledEvent):在 We b应用中,当一个HTTP 请求(request)结束触发该事件。

Bean scope (简单了解)

实际常用的只有 Singleton 和 Prototype 两种级别。

Spring Bean生命周期(高级)

整体流程如下

d7d06fd6ee12f002690763d9f10b468a8a4.jpg

另外一个

dd6b091c72a85f19477194a819317d1b5f0.jpg

各个步骤解释如下

  1. 实例化Bean对象:容器通过BeanDefinition(由XML/注解/config提供)来实例化出一个对象
  2. 设置对象属性:即依赖注入
  3. 调用BeanNameAwre接口的setBeanName()方法:扩展接口。让Bean获得自己的name相关信息,可以配合实现log打印。
  4. 调用BeanFactoryAware的setBeanFactory()方法:扩展接口。让Bean获得容器(上下文),可以使用到容器中的Bean等资源(比如实例化一个lazy Bean)
  5. 调用ApplicationContextAware的setApplicationContext()方法():扩展接口。让Bean获取容器,特别是web容器,例如获取servlet上下文。
  6. 调用BeanProcessor的preProcessBeforeInitialization()方法:扩展接口,实例化前调用对Bean增强。
  7. 调用InitializingBean的afterPropertiesSet()方法:扩展接口,实例化
  8. 调用bean配置的init方法
  9. 调用BeanPostProcessor的postProcessAfterInitialization()方法:扩展接口。实例化后对Bean增强。
  10. 单例Bean缓存在容器中待使用,原型Bean交给用户管理
  11. 调用DisposableBean的destroy()方法:扩展接口
  12. 调用bean中配置的destroy方法

注意以上一系列接口只是对ApplicationContext容器才会自动加载并初始化。

 

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

spring对单例bean没有做安全方面的设置,需要开发者自己控制

Spring Bean 怎么解决循环依赖的问题?(高级)

首先要明白的是spring只解决单例的循环依赖,对原型bean的循环依赖直接抛出异常。

两种场景:

  1. 构造器的循环依赖。
  2. field 属性的循环依赖。

对于构造器的循环依赖,Spring 是无法解决的,也是直接抛出异常。

解决单例循环依赖的手段是,将预初始化(未初始化完成)的Bean加入特殊缓存容器,这个容器中的Bean是个半成品,但是可以提前曝光给需要依赖他的Bean,从而让目标Bean走完初始化流程。

Spring AOP 有哪些实现方式?

广义上分为静态代理(AspectJ AOP)和动态代理(Spring AOP)。

  1. 静态代理是在编译阶段完成,要么通过特殊的编译器完成,要么通过特殊的类加载器完成。
  2. 动态代理是在运行期间,在内存中动态生成代理类,然后用反射创建对象。

spring中使用了JDK和CGLIB两种动态代理,区别如下

JDK 动态代理

通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是 InvocationHandler 接口和 Proxy 类。

CGLIB 动态代理

CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final ,那么它是无法使用 CGLIB 做动态代理的

spring推荐JDK动态代理,因为一是无需依赖第三方,二是不受final影响,三是JDK8后与CGLIB性能差异不大。但是如果被代理类没有实现接口,就只能用CGLIB做动态代理。

列举 Spring 支持的事务管理类型?

声明式事务:通过XML或者注解,使事务控制与代码分离

编程式事务:通过编码方式显式调用提交,回滚等操作。

实际开发中通常用springboot+注解声明事务。

  • 开启事务支持

spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。
关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。

 

各种框架的PlatformTransactionManager实例

82ce54c4ded43e025650e9ce90237134ef5.jpg

 

  • 声明式事务一些常用语法

开启事务

@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {

 

指定回滚条件

@Transactional(rollbackFor=Exception.class) 
    public void methodName() {
       // 不会回滚
       throw new Exception("...");
    } 

@Transactionl常用属性(value,propagation,isolation,timeout,rollbackFor。。。)

 

什么是事务的传播级别?分成哪些传播级别?(Spring特有)

事务的传播行为,指的是当前带有事务配置的方法,需要怎么处理事务。事务传播级别是spring自己定义的。

例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行

三类七种事务传播类型

// TransactionDefinition.java

// ========== 支持当前事务的情况 ========== 

/**
 * 如果当前存在事务,则使用该事务。
 * 如果当前没有事务,则创建一个新的事务。
 */
int PROPAGATION_REQUIRED = 0;
/**
 * 如果当前存在事务,则使用该事务。
 * 如果当前没有事务,则以非事务的方式继续运行。
 */
int PROPAGATION_SUPPORTS = 1;
/**
 * 如果当前存在事务,则使用该事务。
 * 如果当前没有事务,则抛出异常。
 */
int PROPAGATION_MANDATORY = 2;

// ========== 不支持当前事务的情况 ========== 

/**
 * 创建一个新的事务。
 * 如果当前存在事务,则把当前事务挂起。
 */
int PROPAGATION_REQUIRES_NEW = 3;
/**
 * 以非事务方式运行。
 * 如果当前存在事务,则把当前事务挂起。
 */
int PROPAGATION_NOT_SUPPORTED = 4;
/**
 * 以非事务方式运行。
 * 如果当前存在事务,则抛出异常。
 */
int PROPAGATION_NEVER = 5;

// ========== 其他情况 ========== 

/**
 * 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行。
 * 如果当前没有事务,则等价于 {@link TransactionDefinition#PROPAGATION_REQUIRED}
 */
int PROPAGATION_NESTED = 6;

其中绝大部分场景都用第1种 PROPAGATION_REQUIRED

其中前6中继承自EJB,第7种为spring特有,即嵌套事务(在事务中创建子事务,内部事务依赖外部事务的提交或者回滚),很少用到。

Spring的各种扩展增强接口

主要分三类,一类是容器扩展接口例如BeanFactoryPostProcessor,

另一类是Bean扩展接口例如BeanPostProcessor

再就是感知类接口,xxxAwre

容器扩展接口

BeanFactoryPostProcessor:在beanfactory完成标准初始化并加载了definitions集合之后,对beanfactory的一些属性扩展,例如PropertyPlaceholderConfigurer用来替代配置文件中的一些变量,CustomEditorConfigurer用来定义配置文件中一些特殊类型(例如日期)的处理器。

FactoryBean:只要重写了其中的getObject方法,就能按预定方式生成bean。例如要扩展XML的属性,定义特殊属性,然后实例化Bean,整个过程也是AOP的过程。

Bean扩展接口

BeanPostProcess:可以在Bean实例化前后做一些扩展,这里又分为两类。

BeanPostProcess的InstantiationAwareBeanPostProcessor接口方法将在Bean实例话之前调用,一般用来做代理返回代理实例,也就是AOP原理。

postProcessBeforeInitialization 和 postProcessAfterInitialization 则是在Bean实例化后,在初始化前后调用,增强Bean

感知类接口

XXXXXAware:用来获取Bean本身信息,例如BeanNameAware在Factory.getBean()中可以先获取名字。 或者用来获取容器,例如ApplicationContextAware

 

 

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/u/3300976/blog/3031949

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值