Spring核心功能
感谢拉勾教育
控制反转和依赖注入的理解(通俗易懂)
Spring5–03—IOC 操作 Bean 管理
Resource与@Autowired用法区别
文章目录
- Spring Bean 的作用域有哪些?它的注册方式有几种?
- XML 配置文件注册方式
- Java 注解注册方式
- Java API 注册方式
- Bean 的作用域一共有 5 个
- 配置方式
- 同名Bean问题
- Bean 生命周期
- ==执行Aware回调==
- ==BeanPostProcessor==
- ==InitializingBean==
- ==afterPropertiesSet==
- ==注册回调方法==
- ==使用Bean==
- ==销毁==
- 生命周期
- IOC和DI
- Spring IoC 的优点
- IOC优点
- SpringIOC容器原理:
- IOC容器基本概念
- 创建对象的方式由哪些:
- Iocr容器底层实现的技术
Spring Bean 的作用域有哪些?它的注册方式有几种?
在Spring容器中管理一个或者多个Bean,这些Bean的定义表示为beanDefinition对象,这些对象包括一下重要信息
- Bean的实际作用域
- Bean的作用范围
- Bean的引用或者依赖项
Bean的注册方式有三种
- XML配置文件的注册方式
- Java注解的注册方式
- JavaAPI的注册方式
XML 配置文件注册方式
<bean id="person" class="org.springframework.beans.Person">
<property name="id" value="1"/>
<property name="name" value="Java"/>
</bean>
Java 注解注册方式
@Component
public class Person {
private Integer id;
private String name
// 忽略其他方法
}
也可以使用 @Bean 注解方式来注册 Bean,代码如下:
@Configuration
public class Person {
@Bean
public Person person(){
return new Person();
}
// 忽略其他方法
}
@Configuration可以理解为XML配置里的<Beans>标签,而 @Bean 可理解为用 XML 配置里面的 标签。
Java API 注册方式
使用 BeanDefinitionRegistry.registerBeanDefinition() 方法的方式注册 Bean,代码如下:
public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RootBeanDefinition personBean = new RootBeanDefinition(Person.class);
// 新增 Bean
registry.registerBeanDefinition("person", personBean);
}
}
Bean 的作用域一共有 5 个
- singleton作用域: 表示Spring容器只有一个bean实例,以单例的形式存在,是默认开启的
- prototype 作用域: 原型域,每次调用bean时都会创建一个新实例,也就是说每次调用getbean()方法时,相当于执行的New Bean();
- request作用域: 每次http请求时都会创建一个新的Bean,该作用域仅适应于 WebApplicationContext 环境。
- session 作用域:同一个HTTP Session 共享一个Bean对象,不同的Session拥有不同的Bean对象,仅适用于WebApplicationContext 环境。
- application 作用域:全局的WEB作用域,类似于 Servlet 中的 Application
配置方式
<bean class="..." scope="application"></bean>
@Scope(WebApplicationContext.SCOPE_APPLICATION)
@RequestScope(WebApplicationContext.SCOPE_APPLICATION)
同名Bean问题
- 每个Bean拥有一个或者多个标识符,在基于XML的配置中,我们可以使用id或者name来作为Bean的标识符,通常Bean的标识符由字母组成,允许使用特殊字符
- 同一个Spring配置文件中的BEAn和id 和 name 是不能够重复的,否则Spring 容器启动时会报错
但如果 Spring 加载了多个配置文件的话,可能会出现同名 Bean 的问题。同名 Bean 指的是多个 Bean 有相同的 name 或者 id。 - pring 对待同名 Bean 的处理规则是使用最后面的 Bean 覆盖前面的 Bean,所以我们在定义 Bean 时,尽量使用长命名非重复的方式来定义,避免产生同名 Bean 的问题。
- Bean 的 id 或 name 属性并非必须指定,如果留空的话,容器会为 Bean 自动生成一个唯一的
Bean 生命周期
Bean的实例化
对于Spring bean来说,并不是启动阶段就会触发Bean的实例化,只有当客户端通通过显示或者隐式的方法调用BeanFactory的GetBean() 方法时,他才会触发该类的实例化方法,当然对于BeanFactory来说,也不是所有的getBean()方法否会实例化bean对象,例如作用域为singleton 时,就会在第一次,实例化该Bean对象,之后直接返回该对象,如果使用的时ApplicationContext ,则会在该容器启动的时候,立即调用注册到该容器所有 Bean 的实例化方法。
getBean()既然时Bean对象的入口,我们就先从这个方法说起,getBean()方法是属于BeanFActory接口,它是通过AbstractAutowireCapableBeanFactory 的 createBean() 方法,而 createBean() 是通过 doCreateBean() 来实现的,具体源码实现如下:
createBean()源码
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 确定并加载 Bean 的 class
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// 验证以及准备需要覆盖的方法
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 给BeanPostProcessors 一个机会来返回代理对象来代替真正的 Bean 实例,在这里实现创建代理对象功能
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
// 创建 Bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
注入对象属性
doCreateBean 源码如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 实例化 bean,BeanWrapper 对象提供了设置和获取属性值的功能
BeanWrapper instanceWrapper = null;
// 如果 RootBeanDefinition 是单例,则移除未完成的 FactoryBean 实例的缓存
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建 bean 实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 获取 BeanWrapper 中封装的 Object 对象,其实就是 bean 对象的实例
final Object bean = instanceWrapper.getWrappedInstance();
// 获取 BeanWrapper 中封装 bean 的 Class
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 应用 MergedBeanDefinitionPostProcessor 后处理器,合并 bean 的定义信息
// Autowire 等注解信息就是在这一步完成预解析,并且将注解需要的信息放入缓存
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 为了避免循环依赖,在 bean 初始化完成前,就将创建 bean 实例的 ObjectFactory 放入工厂缓存(singletonFactories)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 对 bean 属性进行填充
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
// 调用初始化方法,如 init-method 注入 Aware 对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
} else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
// 如果存在循环依赖,也就是说该 bean 已经被其他 bean 递归加载过,放入了提早公布的 bean 缓存中
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 如果 exposedObject 没有在 initializeBean 初始化方法中被增强
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
} else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 依赖检测
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// 如果 actualDependentBeans 不为空,则表示依赖的 bean 并没有被创建完,即存在循环依赖
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
try {
// 注册 DisposableBean 以便在销毁时调用
registerDisposableBeanIfNecessary(beanName, bean, mbd);
} catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
- 由上诉源码可以看出,在doCreateBean()方法中,首先是对Bean进行实例化,他是通过调用createBeanInstance()方法来实现的,该方法返回一个BeanWrapper 对象,BeanWrapper 对象是Spring 中一个基础的 Bean 结构接口,说它是基础接口是因为它连基本的属性都没有
- BeanWrapper 接口有一个默认实现类,BeanWrapperImpl,主要作用说就是对Bean 进行填充,比如填充和注入 Bean 的属性等。
- 当Spring完成Bean对象实例化并且设置完相关属性和依赖后,则会调用Bean的初始化方法initializeBean(),初始化第一个阶段时检查当前的Bean对象,否则实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 等接口
执行Aware回调
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
- BeanNameAware 是把 Bean 对象定义的 beanName 设置到当前对象实例中
- BeanClassLoaderAware 是将当前 Bean 对象相应的 ClassLoader 注入到当前对象实例中
- BeanFactoryAware 是 BeanFactory 容器会将自身注入到当前对象实例中,这样当前对象就会拥有一个 BeanFactory 容器的引用。
初始化第二个阶段
BeanPostProcessor
InitializingBean
afterPropertiesSet
注册回调方法
使用Bean
销毁
- BeanPostProcessor 增强处理,他主要是对Spring容器提供的Bean实例对象进行有效的扩展,允许Sping在初始化Bean阶段进行定制修改,比如处理标记接口或者为其提供代理实现
- 初始化的前置处理完成之后就会检查和执行 InitializingBean 和 init-method 方法。
- InitializingBean 是一个接口,他有一个==afterPropertiesSet()==方法,在Bean初始化时会判断当前Bean是否实现InitializingBean,如果实现了则调用afterPropertiesSet()方法,进行初始化工作;然后再检查是否也指定init-method,如果指定了则通过反射机制调用指定的init-method方法,
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 判断当前 Bean 是否实现了 InitializingBean,如果是的话需要调用 afterPropertiesSet()
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) { // 安全模式
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet(); // 属性初始化
return null;
}, getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
((InitializingBean) bean).afterPropertiesSet(); // 属性初始化
}
}
// 判断是否指定了 init-method()
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 利用反射机制执行指定方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
初始化完成之后就可以正常的使用Bean对象,再Spring容器关闭的时候会执行销毁方法,但是Spring 容器不会自动去调用销毁方法,而是需要我们主动的调用。
- 如果是BeanFactory 容器,那么我们需要主动调用==destroySingletons()==方法通知 BeanFactory 容器去执行相应的销毁方法;
- 如果是ApplicationContext 容器,那么我们需要主动调用registerShutdownHook(),告知ApplicationContext 容器执行相应的销毁方法。
生命周期
IOC和DI
控制反转和依赖注入的理解(通俗易懂)
IOC不是一个技术,是一种设计思想.与传统的控制流相比,IoC 会颠倒控制流,在传统的编程中需要开发者自行创建并销毁对象,而在IOC中会把这些操作交给框架进行处理,这样开发者就这样开发者就不用关注具体的实现细节了,拿来直接用就可以了,这就是控制反转。
IOC很好的额体现出了面向对象的设计法则之一——好莱坞法则:“别找我们,我们找你”。即由 IoC 容器帮对象找到相应的依赖对象并注入,而不是由对象主动去找。
DI依赖注入,表示组件之间的依赖关系交由容器再运行期自动生成,也就是说,由容器动态的将某个依赖关系注入到组件之中,这样就能提升组件的重用频率,通过依赖注入机制,我们只需要通过简单的配置,就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心资源来自哪里、由谁实现等问题。
IoC 和 DI 其实是同一个概念从不同角度的描述的,由于控制反转这个概念比较含糊(可能只理解成了容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以 2004 年被开发者尊称为“教父”的 Martin Fowler(世界顶级专家,敏捷开发方法的创始人之一)又给出了一个新的名字“依赖注入”,相对 IoC 而言,“依赖注入”明确描述了“被注入的对象依赖IOC容器配置依赖对象”。
Spring IoC 的优点
IOC优点
- 使用方便,拿来即用,无需显示的创建和销毁的过程
- 可以很容易提供众多的服务,比如事务管理,消息服务等
- 提供了单例模式的支持
- 提供了AOP抽象,利用他很很容易进行拦截,运行期监控等功能
- 更符合面向对象的设计法则
- 低侵入设计,代码的污染低,降低了业务对象替换的复杂性。
SpringIOC容器原理:
IOC容器中非常核心的接口: BeanFactory bean对象 Factory工厂
IOC容器基本概念
IOC容器基本概念::控制反转: 降低计算机代码的冗余度
把对象的创建对象与使用统一交给我们的Spring来进行管理 不需要开发者自己去new对象
创建对象的方式由哪些:
每次单独new对象,没有实现统一的管理对象,如果后期需要userDao的名称信息发布的情况下,需要改变的引用的地方比较多,耦合度太高
概念:统一管理和维护我们每个对象创建与使用的过程
不需自己new对象
Iocr容器底层实现的技术
:反射技术, 工厂模式 .解析XML
- 使用解析XML技术.解析Spring.xml配置文件
- 获取<bean\ id=’’ class=’’/> 类的完整路径地址
- 使用到反射技术初始化对象
- 使用工厂模式封装初始化对象
IOC核心接口
- IOC容器接口
- IOC容器底层基于反射 +工厂实现的
- Spring体系中提供了两种IOC容器的实现方案
3.1BeanFactory不是提供给给开发人员的 主要提供给我们Spring内部自己实现
加载配置文件的时候不会根据配置文件的内容创建对象,当我们真正徐娅使用该对象的时候才会创建对象
___3.2 ApplicationFactory是提供给我们开发人员使用的,对于我们传统的BeanFactory实现非常多的扩 展 功能,ApplicationFactory属性BeanFactory接口下的子接口
加载配置文件的时候,会根据配置文件的内容创建对象,并且缓存起来
javaweb项目的时候, 使用ApplicationFactory
刚启动的时候,程序卡,后面就好了,
Spring IoC 注入方式汇总
IoC 的注入方式有三种:构造方法注入、Setter 注入和接口注入,注解注入。
Spring5–03—IOC 操作 Bean 管理
Resource与@Autowired用法区别
AOP 概述
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是spring框架中的的一个重 要内容。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各 部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
AOP(Aspect-OrientedProgramming,面向切面编程)可以说是 OOP(Object-Oriented Programing,面向对象编程)的补充和完善,OOP 引入封装、继承和多态性等概念来建立一种公共对象处理的能力,当我们需要处理公共行为的时候,OOP 就会显得无能为力,而 AOP 的出现正好解决了这个问题。比如统一的日志处理模块、授权验证模块等都可以使用 AOP 很轻松的处理。
Spring AOP 目前提供了三种配置方式:
基于 Java API 的方式;
基于 @AspectJ(Java)注解的方式;
基于 XML 标签的方式。
Spring AOP 的原理其实很简单,它其实就是一个动态代理,我们在调用 getBean() 方法的时候返回的其实是代理类的实例,而这个代理类在 Spring 中使用的是 JDK Proxy 或 CgLib 实现的,它的核心代码在 DefaultAopProxyFactory#createAopProxy(…) 中,源码如下:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 判断目标类是否为接口
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 是接口使用 jdk 的代理
return new JdkDynamicAopProxy(config);
}
// 其他情况使用 CgLib 代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
// 忽略其他代码
}