Java面试准备之Spring框架系列二

hresh 394 0

Java面试准备之Spring框架系列二

BeanDefinition

配置文件元素标签拥有 class、scope、lazy-init 等属性,BeanDefinition 则提供了相应的 beanClass、scope、lazyInit 属性,BeanDefinition 和<bean>中的属性一一对应。

Spring 通过 BeanDefinition 将配置文件中的配置信息转换为容器的内部表示,并将这些 BeanDefinition 注册到 BeanDefinitionRegistry 中。Spring 容器的 BeanDefinitionRegistry 就像是 Spring 配置信息的内存数据库,主要以 map 的形式保存,后续操作直接从 BeanDefinitionRegistry 中读取配置信息。

配置文件解析主要是对标签的解析,分为默认标签和自定义标签。默认标签包括 import、alias、bean、beans, 对应 bean 标签的解析是最核心的功能,对于 alias、import、beans 标签的解析都是基于 bean 标签解析的。

推荐阅读:Spring IoC之存储对象BeanDefinition

Spring如何实现自定义标签

扩展 Spring 自定义标签配置一般需要以下几个步骤:

  1. 创建一个需要扩展的组件
  2. 定义一个 XSD 文件,用于描述组件内容
  3. 创建一个实现 AbstractSingleBeanDefinitionParser 接口的类,又或者创建一个实现 BeanDefinitionParser 接口的类,用来解析 XSD 文件中的定义和组件定义。这两种实现方式对应不同的 XSD 文件配置方式。
  4. 创建一个 Handler,继承 NamespaceHandlerSupport ,用于将组件注册到 Spring 容器
  5. 编写 Spring.handlers 和 Spring.schemas 文件

除了可以自定义标签外,还可以自定义属性,以及自定义子标签,具体实现都比较相似。Spring 源码中也大量使用了自定义标签,比如 Spring 的 AOP 的定义,其标签为 <aspectj-autoproxy />

推荐阅读:Spring自定义标签的实现

你听说过BeanWrapper吗

BeanWrapper 是 Spring 的低级 JavaBeans 基础结构的中央接口, 相当于一个代理器, 提供用于分析和操作标准 JavaBean 的操作:获得和设置属性值(单独或批量),获取属性描述符以及查询属性的可读性/可写性的能力。BeanWrapper 大部分情况下是在 Spring IoC 内部进行使用,通过 BeanWrapper,Spring IoC 容器可以用统一的方式来访问 bean 的属性。用户很少需要直接使用 BeanWrapper 进行编程。

推荐阅读:Spring IoC之BeanWrapper

PropertyEditor

任何实现 java.beans.PropertyEditor 接口的类都是属性编辑器。属性编辑器的主要功能就是将外部的设置值转换为 JVM 内部的对应类型,所以属性编辑器其实就是一个类型转换器。

在 Spring 配置文件里,我们往往通过字面值为 Bean 各种类型的属性提供设置值:不管是 double 类型还是 int 类型,在配置文件中都应字符串类型的字面值。BeanWrapper 填充 Bean 属性时通过 PropertyEditor 将这个字面值转换为对应的 double 或 int 等内部类型。

当 Spring 内置的 PropertyEditor 无法满足我们的要求的时候,我们可以根据 Spring 提供的扩展机制来自定义 PropertyEditor,下面通过一个例子来介绍如何实现自定义的 PropertyEditor,这个 PropertyEditor 是一个时间相关的 Editor,它可以一个满足特定时间格式的字符串转换成日期对象。

推荐阅读:Spring之PropertyEditor

Spring Bean 的转换过程

Java面试准备之Spring框架系列二

Spring 中的 bean 生命周期

  • Bean 容器找到配置文件中 Spring Bean 的定义。
  • Bean 容器利用 Java Reflection API 创建一个Bean的实例。
  • 如果涉及到一些属性值 利用 set()方法设置一些属性值。
  • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。
  • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
  • 如果Bean实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory 对象的实例。
  • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
  • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
  • 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。
  • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
  • 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
  • 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
  • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

图示:

Java面试准备之Spring框架系列二

推荐阅读:

  • https://yemengying.com/2016/07/14/spring-bean-life-cycle/
  • https://www.cnblogs.com/zrtqsk/p/3735273.html

Spring 中 bean 初始化方式

对于Spring Bean 的初始化归纳了下,主要可以归纳一下三种方式

  • @PostConstruct 标注方法
  • 自定义初始化方法
    • 通过实现InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
    • 通过 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;

Spring 注入方式?

1、注解注入;2、setter方法注入;3、构造方法注入

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

  • 工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。

Spring 管理事务的方式有几种?

  1. 编程式事务,对基于 POJO 的应用来说是唯一选择,在代码中硬编码,需要在代码中调用 beginTransaction()、commit()、rollback()等事务管理相关的方法。(不推荐使用)
  2. 声明式事务,在配置文件中配置(推荐使用)

声明式事务又分为两种:

  1. 基于XML的声明式事务
  2. 基于注解@Transactional 的声明式事务

Spring 事务中的隔离级别有哪几种?

TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

Spring 事务中哪几种事务传播行为?

支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

@Transactional(rollbackFor = Exception.class)注解了解吗?

我们知道:Exception 分为运行时异常 RuntimeException和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。

当@Transactional 注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。

在@Transactional 注解中如果不配置 rollbackFor 属性,那么事务只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。

如何使用JPA在数据库中非持久化一个字段?

假如我们有有下面一个类:

Entity(name="USER")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;

    @Column(name="USER_NAME")
    private String userName;

    @Column(name="PASSWORD")
    private String password;

    private String secrect;

}

如果我们想让secrect 这个字段不被持久化,也就是不被数据库存储怎么办?我们可以采用下面几种方法:

static String transient1; // not persistent because of static

final String transient2 = “Satish”; // not persistent because of final

transient String transient3; // not persistent because of transient

@Transient
String transient4; // not persistent because of @Transient

一般使用后面两种方式比较多,我个人使用注解的方式比较多。

Spring 容器启动方式

在Web项目中,启动Spring容器的方式有三种,ContextLoaderListener、ContextLoadServlet、ContextLoaderPlugin。

1、监听器方式

2、servlet 方式

3、通过plugin方式

认证 (Authentication) 和授权 (Authorization)

Authentication(认证) 是验证您的身份的凭据(例如用户名/用户ID和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。

Authorization(授权) 发生在 Authentication(认证)之后。授权嘛,光看意思大家应该就明白,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如admin,有些对系统资源操作比如删除、添加、更新只能特定人才具有。

这两个一般在我们的系统中被结合在一起使用,目的就是为了保护我们系统的安全性。

发表评论 取消回复
表情 图片 链接 代码

分享