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 自定义标签配置一般需要以下几个步骤:
- 创建一个需要扩展的组件
- 定义一个 XSD 文件,用于描述组件内容
- 创建一个实现 AbstractSingleBeanDefinitionParser 接口的类,又或者创建一个实现 BeanDefinitionParser 接口的类,用来解析 XSD 文件中的定义和组件定义。这两种实现方式对应不同的 XSD 文件配置方式。
- 创建一个 Handler,继承 NamespaceHandlerSupport ,用于将组件注册到 Spring 容器
- 编写 Spring.handlers 和 Spring.schemas 文件
除了可以自定义标签外,还可以自定义属性,以及自定义子标签,具体实现都比较相似。Spring 源码中也大量使用了自定义标签,比如 Spring 的 AOP 的定义,其标签为 <aspectj-autoproxy />
。
推荐阅读:Spring自定义标签的实现
你听说过BeanWrapper吗
BeanWrapper 是 Spring 的低级 JavaBeans 基础结构的中央接口, 相当于一个代理器, 提供用于分析和操作标准 JavaBean 的操作:获得和设置属性值(单独或批量),获取属性描述符以及查询属性的可读性/可写性的能力。BeanWrapper 大部分情况下是在 Spring IoC 内部进行使用,通过 BeanWrapper,Spring IoC 容器可以用统一的方式来访问 bean 的属性。用户很少需要直接使用 BeanWrapper 进行编程。
PropertyEditor
任何实现 java.beans.PropertyEditor
接口的类都是属性编辑器。属性编辑器的主要功能就是将外部的设置值转换为 JVM 内部的对应类型,所以属性编辑器其实就是一个类型转换器。
在 Spring 配置文件里,我们往往通过字面值为 Bean 各种类型的属性提供设置值:不管是 double 类型还是 int 类型,在配置文件中都应字符串类型的字面值。BeanWrapper 填充 Bean 属性时通过 PropertyEditor 将这个字面值转换为对应的 double 或 int 等内部类型。
当 Spring 内置的 PropertyEditor 无法满足我们的要求的时候,我们可以根据 Spring 提供的扩展机制来自定义 PropertyEditor,下面通过一个例子来介绍如何实现自定义的 PropertyEditor,这个 PropertyEditor 是一个时间相关的 Editor,它可以一个满足特定时间格式的字符串转换成日期对象。
Spring Bean 的转换过程
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 属性,执行指定的方法。
图示:
推荐阅读:
- 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 管理事务的方式有几种?
- 编程式事务,对基于 POJO 的应用来说是唯一选择,在代码中硬编码,需要在代码中调用 beginTransaction()、commit()、rollback()等事务管理相关的方法。(不推荐使用)
- 声明式事务,在配置文件中配置(推荐使用)
声明式事务又分为两种:
- 基于XML的声明式事务
- 基于注解@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,有些对系统资源操作比如删除、添加、更新只能特定人才具有。
这两个一般在我们的系统中被结合在一起使用,目的就是为了保护我们系统的安全性。
本文作者为hresh,转载请注明。