Spring源码分析(八)CreateBean与DoCreateBean
写目录
- 一 CreateBean
- 二 doCreateBean
- 2.1 BeanWrapper
- 2.2 缓存删除
- 2.3 实例化Bean
- 2.3.1 Supplier创建对象
- 2.3.2 工厂创建对象
- 2.3.3 构造器创建实例
- 无参构造
- 构造器依赖注入
- Setter的依赖注入
- autowireConstructor实例化
- instantiateBean 方法
- 2.4 Bean的前置处理
- 官网:Home
- 参考书籍:Spring源码深度解析-郝佳编著-微信读书
上一节我们看到正对不同作用域Bean的加载,但是Bean的核心创建我们还没有说,下面我们来看看Bean的核心加载也就是CreateBean与DoCreateBean方法的核心实现
一 CreateBean
AbstractAutowireCapableBeanFactory
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {if (logger.isDebugEnabled()) {logger.debug("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// 确保此时的 bean 已经被解析了// 如果获取的class 属性不为null,则克隆该 BeanDefinition// 主要是因为该动态解析的 class 无法保存到到共享的 BeanDefinitionClass<?> 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 一个机会用来返回一个代理类而不是真正的类实例// AOP 的功能就是基于这个地方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.isDebugEnabled()) {logger.debug("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);}
}
- 根据设置的class属性或者根据className来解析Class,这里面逻辑十分复杂,但是我们可以猜想他的作用,不就是通过类加载来实例化我们编写,并组装成RootBeanDefinition,前面我们也说过所有的Bean后续处理都是针对于RootBeanDefinition的
// 确保此时的 bean 已经被解析了// 如果获取的class 属性不为null,则克隆该 BeanDefinition// 主要是因为该动态解析的 class 无法保存到到共享的 BeanDefinitionClass<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}
- 对override属性进行标记及验证,lookup-method和replace-method的,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性里,而这个函数的操作其实也就是针对于这两个配置的
try {// 验证和准备覆盖方法mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}
public void prepareMethodOverrides() throws BeanDefinitionValidationException {// Check that lookup methods exists.//检测是否存在方法注入,并循环预处理方法注入// Check that lookup methods exist and determine their overloaded status.if (hasMethodOverrides()) {getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);}}protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {// 统计注入的方法个数 int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());if (count == 0) {throw new BeanDefinitionValidationException("Invalid method override: no method with name '" + mo.getMethodName() +"' on class [" + getBeanClassName() + "]");}// 如果为1,则将注入方法标记为未重载// 注意:当有多个重载方法时,为了确定调用哪个具体的方法,Spring对重载方法的参数解析是很复杂的// 所以,如果注入方法没有被重载这里就将其标记,省去了对方法参数的解析过程,直接调用即可else if (count == 1) {// Mark override as not overloaded, to avoid the overhead of arg type checking.mo.setOverloaded(false);}}
- 应用初始化前的后处理器,解析指定bean是否存在初始化前的短路操作:
在真正调用doCreate方法创建bean的实例前使用了这样一个方法resolveBeforeInstantiation (beanName, mbd)对BeanDefinigiton中的属性做些前置处理,当经过前置处理后返回的结果如果不为空,那么会直接略过后续的Bean的创建而直接返回结果。这一特性虽然很容易被忽略,但是却起着至关重要的作用,我们熟知的AOP功能就是基于这里的判断的
try {// 给 BeanPostProcessors 一个机会用来返回一个代理类而不是真正的类实例// AOP 的功能就是基于这个地方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);}
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;// 如果没有被解析if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.// synthetic表示合成,如果某些Bean式合成的,那么则不会经过BeanPostProcessor的处理if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) // 确定目标类型Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {// 在实例化之前应用 Bean 后处理器bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {// 初始化后应用 Bean 后处理器bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}mbd.beforeInstantiationResolved = (bean != null);}return bean;}
此方法中最吸引我们的无疑是两个方法applyBeanPostProcessorsBeforeInstantiation以及applyBeanPostProcessorsAfterInitialization。
两个方法实现的非常简单,无非是对后处理器中的所有InstantiationAwareBeanPostProcessor类型的后处理器进行postProcessBeforeInstantiation方法和BeanPostProcessor的postProcessAfterInitialization方法的调用。(后面生命周期详细讲解)
参考文章:spring bean扩展点:后处理器 BeanPostProcessor_postprocessbeforeinstantiation
如果没有代理对象,就只能走常规的路线进行 bean 的创建了,该过程有 doCreateBean() 实现
二 doCreateBean
当经历过resolveBeforeInstantiation方法后,程序有两个选择,如果创建了代理或者说重写了InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法并在方法postProcess BeforeInstantiation中改变了bean,则直接返回就可以了,否则需要进行常规bean的创建。
AbstractAutowireCapableBeanFactory
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器BeanWrapper instanceWrapper = null;// 单例模型,则从未完成的 FactoryBean 缓存中删除if (mbd.isSingleton()) {anceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 使用合适的实例化策略来创建新的实例:工厂方法、构造函数自动注入、简单初始化if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}// 包装的实例对象final Object bean = instanceWrapper.getWrappedInstance();// 包装的实例对象的类型Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// 检测是否有后置处理// 如果有后置处理,则允许后置处理修改 BeanDefinitionsynchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {// applyMergedBeanDefinitionPostProcessors// 后置处理修改 BeanDefinitionapplyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// 解决单例模式的循环依赖// 单例模式 & 运行循环依赖&当前单例 bean 是否正在被创建boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 提前将创建的 bean 实例加入到ectFactory 中// 这里是为了后期避免循环依赖addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}/** 开始初始化 bean 实例对象*/Object exposedObject = bean;try {// 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性// 则会递归初始依赖 beanpopulateBean(beanName, mbd, instanceWrapper);// 调用初始化方法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) {// 获取 earlySingletonReferenceObject earlySingletonReference = getSingleton(beanName, false);// 只有在存在循环依赖的情况下,earlySingletonReference 才不会为空if (earlySingletonReference != null) {// 如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强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);}}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 {// 注册 beanregisterDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
我们来总结一下上面函数的大体步骤:
- 如果是单例则需要首先清除缓存
- 实例化bean,将BeanDefinition转换为BeanWrapper
- MergedBeanDefinitionPostProcessor的应用
- 依赖处理
- 属性填充,将所有属性填充至bean的实例中
- 循环依赖检查
- 注册DisposableBean
- 完成创建并返回
我们依次来看看呗
2.1 BeanWrapper
在详细介绍之前,我们先了解BeanWrapper这个类的作用
BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器,由于BeanWrapper接口是PropertyAccessor的子接口,因此其也可以设置以及访问被包装对象的属性值。
BeanWrapper大部分情况下是在spring ioc内部进行使用,通过BeanWrapper,spring ioc容器可以用统一的方式来访问bean的属性。用户很少需要直接使用BeanWrapper进行编程。
2.2 缓存删除
// BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器BeanWrapper instanceWrapper = null;// 单例模型,则从未完成的 FactoryBean 缓存中删除if (mbd.isSingleton()) {anceWrapper = this.factoryBeanInstanceCache.remove(beanName);}
2.3 实例化Bean
参考博客:Spring IoC 依赖注入(四)构造器或工厂注入
// 使用合适的实例化策略来创建新的实例:工厂方法、Spring IoC 依赖注入(四)构造器或工厂注入 - binarylei - 博客园构造函数自动注入、简单初始化if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}
AbstractAutowireCapableBeanFactory
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 1.1 确保此时beanClassName已经加载,当然注解驱动时不会设置beanClassName属性Class<?> beanClass = resolveBeanClass(mbd, beanName);// 1.2 校验beanClass允许访问if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException();}// 2. Supplier创建对象Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// ========= 工厂方法实例化 =========// 3. 工厂方法实例化,包括实例化工厂和静态工厂if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// ========= 构造器实例化 =========// 4. 快速实例化对象,所谓的快速实例化,实际上是说使用缓存boolean resolved = false;boolean autowireNecessary = false;// 4.1 args: 外部化参数,只能当无外部参数时才使用缓存。不推荐使用外部化参数if (args == null) {synchronized (mbd.constructorArgumentLock) {// 4.2 是否使用缓存,其中autowireNecessary表示是否使用有参构造器// 无参时肯定不会解析,为false。有参时会解析,为trueif (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}// 4.3 使用缓存,其中autowireNecessary表示是否使用有参构造器if (resolved) {if (autowireNecessary) {// 4.4 有参构造器实例化return autowireConstructor(beanName, mbd, null, null);} else {// 4.5 无参构造器实例化return instantiateBean(beanName, mbd);}}// 5. 到此,只能老老实实的解析,当然解析后会将解析后的构造器或参数缓存起来// 5.1 是否指定了构造器,ibp#determineCandidateConstructorsConstructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);// 5.2 构造器实例化if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// 5.3 不用管,默认都是 null。ctors = mbd.getPreferredConstructors();if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, null);}// 5.4 无参构造器实例化return instantiateBean(beanName, mbd);
}
- 说明: createBeanInstance 方法在 bd.beanClass 校验后进行对象实例化,而 Supplier 实例化比较简单,工厂方法实例化又全部委托给了 instantiateUsingFactoryMethod 方法,基本上主要的功能就是,判断如何使用构造器实例化对象。
- 判断能否使用缓存快速实例化对象。使用缓存实例化有两个条件:
- 外部化参数为 null。Spring 中获取对象时,允许使用外部化参数 args 覆盖配置参数 bd.args。此时,缓存的构造器和参数全部失效。虽然 Spring 提供了外部化参数 args,但不推荐使用 getBean(beanName, args) 。
- 缓存命中,也就是构造器和参数已经解析完成。如果无参,则不会解析参数,此时 bd.constructorArgumentsResolved=false,而有参则为 true。也就是可以使用该变量来判断是否使用有参构造器。
- 判断使用有参还是无参构造器实例化对象。使用有参构造器实例化对象有以下条件:
- 指定构造器。ibp#determineCandidateConstructors 后置处理器可以指定实例化的构造器 ctors。AutowiredAnnotationBeanPostProcessor 会指定实例化的构造器。
- 指定注入模式为构造器自动注入模式。目前这种模式只能通过 XML 方式配置。。
- 指定参数。包括配置参数 bd.constructorArgumentValues 或外部参数 args。
- 有参实例调用 autowireConstructor 方法。而无参实例化调用 instantiateBean 方法。无参实例化 instantiateBean 方法比较简单
首先我们了解一下Spring创建Bean的方式:
Spring创建对象的主要方式有
- 通过自定义BeanPostProcessor,生成代理对象InstantiationAwareBeanPostProcessor
createBean() -> resolveBeforeInstantiation()
- 通过supplier创建对象
createBean() -> doCreateBean() -> createBeanInstance() -> obtainFromSupplier()
- 通过FactoryMethod创建对象
createBean() -> doCreateBean() -> createBeanInstance() -> instantiateUsingFactoryMethod()
- 通过反射创建对象
createBean() -> doCreateBean() -> createBeanInstance() -> instantiateBean()
- 通过FactoryBean创建对象
下面我们一一来看一看
2.3.1 Supplier创建对象
我们先来写一个案例,这种用法比较偏门
User
package com.shu.supplier;/*** @description:* @author: shu* @createDate: 2023/8/31 14:11* @version: 1.0*/
public class User {private String name;public User() {System.out.println("User 无参构造函数");}public User(String name) {this.name = name;System.out.println("User 有参构造函数");}public String getName() {return name;}public void setName(String name) {this.name = name;System.out.println("User setName");}public void init() {System.out.println("User init");}public void destroy() {System.out.println("User destroy");}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +'}';}
}
CreateSupplier
package com.shu.supplier;/*** @description:* @author: shu* @createDate: 2023/8/31 14:11* @version: 1.0*/
public class CreateSupplier {public static User createUser() {User xiaomi = new User("xiaomi");System.out.println("CreateSupplier createUser:"+xiaomi);return xiaomi;}
}
SupplierBeanFactoryPostProcessor
package com.shu.supplier;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;/*** @description:* @author: shu* @createDate: 2023/8/31 14:12* @version: 1.0*/
public class SupplierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanDefinition;genericBeanDefinition.setInstanceSupplier(CreateSupplier::createUser);genericBeanDefinition.setBeanClass(User.class);}
}
配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="com.shu.supplier.User"></bean><bean id="supplierBeanFactoryPostProcessor" class="com.shu.supplier.SupplierBeanFactoryPostProcessor"></bean>
</beans>
测试
import com.shu.supplier.User;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;/*** @description:* @author: shu* @createDate: 2023/7/29 23:05* @version: 1.0*/
public class AplTest {/*** 测试FactoryBean*/@Testpublic void factoryBeanTestSupplier() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("supplier.xml");User user = applicationContext.getBean(User.class);System.out.println(user.getName());}}
下面我们来具体分析看看呗
AbstractAutowireCapableBeanFactory
// 如果获取getInstanceSupplier不为空,参考:genericBeanDefinition.setInstanceSupplier(CreateSupplier::createUser);
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);
}
下面进入obtainFromSupplier进行看看,那么回到这个方法中,主体就是调用get()这个方法获取实例
protected BeanWrapper obtainFromSupplier(Supplier instanceSupplier, String beanName) {Object instance;// 获得原当前线程正在创建的 Bean 的名称String outerBean = this.currentlyCreatedBean.get();// 设置当前线程正在创建的 Bean 的名称this.currentlyCreatedBean.set(beanName);try {//调用 Supplier 的 get(),返回一个实例对象instance = instanceSupplier.get();}finally {if (outerBean != null) {// 设置原当前线程正在创建的 Bean 的名称到当前线程变量中this.currentlyCreatedBean.set(outerBean);}else {this.currentlyCreatedBean.remove();}}// 未创建 Bean 对象,则创建 NullBean 空对象if (instance == null) {instance = new NullBean();}//将实例对象封装成 BeanWrapper 对象BeanWrapper bw = new BeanWrapperImpl(instance);//初始化这个 BeanWrapper 对象initBeanWrapper(bw);return bw;
}
过程如下:
调用 Supplier 接口的 get(),返回 instance 实例对象
将 instance 封装成 BeanWrapper 对象 bw
对 bw 进行初始化,设置 ConversionService 类型转换器,并注册自定义的属性编辑器
然后再进行实例化
AbstractAutowireCapableBeanFactory
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;
}
2.3.2 工厂创建对象
知识回顾:
静态工厂实例化
在定义一个用静态工厂方法创建的Bean时,使用 class 属性来指定包含 static 工厂方法的类,并使用名为 factory-method 的属性来指定工厂方法本身的名称。你应该能够调用这个方法(有可选的参数,如后文所述)并返回一个活的对象,随后该对象被视为通过构造函数创建的。
package com.shu.factory;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;/*** @description: 静态工厂, 用于书写创建复杂对象的代码* @author: shu* @createDate: 2023/7/21 20:14* @version: 1.0*/
public class StaticConnectionFactory {public static Connection getConnection(){Connection conn = null;try {Class.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/auth?useSSL=false", "root", "123456");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}return conn;}
}
<bean id="db" class="com.shu.factory.StaticConnectionFactory" factory-method="getConnection"/>
/*** 静态工厂实例化bean*/@Testpublic void test8(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Object db = context.getBean("db");System.out.println("db = " + db);}
实例工厂方法进行实例化
与 通过静态工厂方法进行的实例化 类似,用实例工厂方法进行的实例化从容器中调用现有 bean 的非静态方法来创建一个新的 bean。要使用这种机制,请将 class 属性留空,并在 factory-bean 属性中指定当前(或父代或祖代)容器中的一个 Bean 的名称,该容器包含要被调用来创建对象的实例方法。用 factory-method 属性设置工厂方法本身的名称。
package com.shu.factory;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class ConnectionFactory {public Connection getConnection(){Connection conn = null;try {Class.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/auth?useSSL=false", "root", "123456");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}return conn;}
}
<bean id="connFactory" class="com.shu.factory.ConnectionFactory"></bean><bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>
@Testpublic void test10() throws Exception {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Object conn = context.getBean("conn");System.out.println("conn = " + conn);}
让我们回到代码部分
AbstractAutowireCapableBeanFactory
//如果配置了 `factory-method` 工厂方法,则调用该方法来创建一个实例对象// 通过 @Bean 标注的方法会通过这里进行创建if (mbd.getFactoryMethodName() != null) {// 这个过程非常复杂,你可以理解为:// 找到最匹配的 Method 工厂方法,获取相关参数(依赖注入),然后通过调用该方法返回一个实例对象(反射机制)return instantiateUsingFactoryMethod(beanName, mbd, args);}
通过 factoryMethodName 工厂方法创建一个实例对象,例如 XML 配置的 factory-method 属性或者 @Bean 标注的方法都会解析成 factoryMethodName 属性
这个过程非常复杂,你可以理解为去找到最匹配的 Method 工厂方法,获取相关入参(依赖注入),然后调用该方法返回一个实例对象(反射机制)
AbstractAutowireCapableBeanFactory
protected BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
创建 ConstructorResolver 对象,然后调用其 instantiateUsingFactoryMethod(…) 方法,如下:
// ConstructorResolver.java
public BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {// 构造 BeanWrapperImpl 对象BeanWrapperImpl bw = new BeanWrapperImpl();// 初始化 BeanWrapperImpl,设置 ConversionService 类型转换器,并注册自定义的属性编辑器this.beanFactory.initBeanWrapper(bw);// -------------------------获取工厂方法的相关信息-------------------------//获取工厂方法的相关信息// 工厂方法所在类对应的 Bean(静态方法不会有)Object factoryBean;// 工厂方法所在类的 Class 对象Class factoryClass;// 是否为 static 修饰的静态方法boolean isStatic;// 获取工厂方法所在类对应的 Bean 的名称(静态方法不会有)String factoryBeanName = mbd.getFactoryBeanName();//非静态方法if (factoryBeanName != null) {if (factoryBeanName.equals(beanName)) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,"factory-bean reference points back to the same bean definition");}// 获取工厂方法所在类对应的 Bean,不然无法调用工厂方法factoryBean = this.beanFactory.getBean(factoryBeanName);// 如果是单例模式,已经存在对应的 Bean,则抛出重复创建的异常if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {throw new ImplicitlyAppearedSingletonException();}factoryClass = factoryBean.getClass();isStatic = false;}//静态方法else {// It's a static factory method on the bean class.// 静态方法没有找到对应的 Class 对象无法被调用,则抛出异常if (!mbd.hasBeanClass()) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,"bean definition declares neither a bean class nor a factory-bean reference");}factoryBean = null;factoryClass = mbd.getBeanClass();isStatic = true;}// -------------------------尝试获取工厂方法对象和入参-------------------------//尝试获取工厂方法对象和参数// 工厂方法对象Method factoryMethodToUse = null;ArgumentsHolder argsHolderToUse = null;// 方法参数Object[] argsToUse = null;//如果方法入参指定了参数,则直接使用if (explicitArgs != null) {argsToUse = explicitArgs;}//否则,尝试从 RootBeanDefinition 中获取已解析出来的工厂方法和入参else {Object[] argsToResolve = null;// 因为可能前面解析了,会临时缓存,避免再次解析synchronized (mbd.constructorArgumentLock) { // 加锁factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;// 如果工厂方法被解析了,那么参数也可能解析过if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {// Found a cached factory method...argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 没有解析过的参数,则尝试从 RootBeanDefinition 中获取未被解析过的参数argsToResolve = mbd.preparedConstructorArguments;}}}// 如果获取到了未被解析过的入参,则进行解析if (argsToResolve != null) {// 处理参数值,类型转换,例如给定方法 A(int, int),配置了 A("1"、"2") 两个参数,则会转换为 A(1, 1)argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);}}// -------------------------找到所有匹配的工厂方法-------------------------//如果上一步没有找到工厂方法对象或方法入参集合,则需要进行接下来的解析过程,首先找到所有匹配的工厂方法if (factoryMethodToUse == null || argsToUse == null) {// Need to determine the factory method...// Try all methods with this name to see if they match the given arguments.//获取工厂方法所在的类的实例 Class 对象,因为可能是 Cglib 提升过的子类factoryClass = ClassUtils.getUserClass(factoryClass);//获取工厂方法所在的类中所有方法对象Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);//找到这个类中匹配的工厂方法ListcandidateList = new ArrayList<>();for (Method candidate : rawCandidates) {if (Modifier.isStatic(candidate.getModifiers()) == isStatic // 是否和 `isStatic` 匹配&& mbd.isFactoryMethod(candidate)) { // 和定义的工厂方法的名称是否相等candidateList.add(candidate);}}//如果只有一个匹配的方法,且这个方法没有给指定的入参,且本身也没有定义参数,且这个方法没有定义入参// 则直接调用这个方法创建一个实例对象(反射机制),并返回if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {Method uniqueCandidate = candidateList.get(0);if (uniqueCandidate.getParameterCount() == 0) {mbd.factoryMethodToIntrospect = uniqueCandidate;synchronized (mbd.constructorArgumentLock) {mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;mbd.constructorArgumentsResolved = true;mbd.resolvedConstructorArguments = EMPTY_ARGS;}bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));return bw;}}// -------------------------开始找最匹配的工厂方法-------------------------//开始找最匹配的工厂方法// 将匹配的工厂方法转换成数组Method[] candidates = candidateList.toArray(new Method[0]);// 将匹配的方法进行排序,public 方法优先,入参个数多的优先AutowireUtils.sortFactoryMethods(candidates);// 用于承载解析后的方法参数值ConstructorArgumentValues resolvedValues = null;// 是否是构造器注入boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);int minTypeDiffWeight = Integer.MAX_VALUE;// 匹配方法的集合SetambiguousFactoryMethods = null;// -------------------------确定方法参数的入参数量-------------------------//确定方法参数的入参数量,匹配的方法的入参数量要多余它// 方法的参数数量的最小值int minNrOfArgs;//如果当前方法指定了入参,则使用其个数作为最小值if (explicitArgs != null) {minNrOfArgs = explicitArgs.length;}//否则,从 RootBeanDefinition 解析出方法的参数个数作为最小值else {// We don't have arguments passed in programmatically, so we need to resolve the// arguments specified in the constructor arguments held in the bean definition.// RootBeanDefinition 定义了参数值if (mbd.hasConstructorArgumentValues()) {// 方法的参数ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();// 解析定义的参数值,放入 `resolvedValues` 中,并返回参数个数minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}else {minNrOfArgs = 0;}}// 记录 UnsatisfiedDependencyException 异常的集合LinkedListcauses = null;// 遍历匹配的方法for (Method candidate : candidates) {// 方法体的参数Class[] paramTypes = candidate.getParameterTypes();if (paramTypes.length >= minNrOfArgs) {// -------------------------解析出工厂方法的入参-------------------------//解析出工厂方法的入参// 保存参数的对象ArgumentsHolder argsHolder;//如果当前方法指定了入参,则直接使用if (explicitArgs != null) {// Explicit arguments given -> arguments length must match exactly.// 显示给定参数,参数长度必须完全匹配if (paramTypes.length != explicitArgs.length) {continue;}// 根据参数创建参数持有者 ArgumentsHolder 对象argsHolder = new ArgumentsHolder(explicitArgs);}//否则,通过**依赖注入**获取入参else {// Resolved constructor arguments: type conversion and/or autowiring necessary.// 为提供参数,解析构造参数try {String[] paramNames = null;// 获取 ParameterNameDiscoverer 参数名称探测器ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();// 获取方法的参数名称if (pnd != null) {paramNames = pnd.getParameterNames(candidate);}// 解析出方法的入参,参数值会被依赖注入argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,paramTypes, paramNames, candidate, autowiring, candidates.length == 1);}catch (UnsatisfiedDependencyException ex) {// 若发生 UnsatisfiedDependencyException 异常,添加到 causes 中。if (logger.isTraceEnabled()) {logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);}// Swallow and try next overloaded factory method.if (causes == null) {causes = new LinkedList<>();}causes.add(ex);continue;}}// -------------------------根据权重获取最匹配的方法-------------------------//因为会遍历所有匹配的方法,所以需要进行权重的判断,拿到最优先的那个// 判断解析构造函数的时候是否以宽松模式还是严格模式,默认为 true// 严格模式:解析构造函数时,必须所有的都需要匹配,否则抛出异常// 宽松模式:使用具有"最接近的模式"进行匹配// typeDiffWeight:类型差异权重int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));// Choose this factory method if it represents the closest match.// 代表最匹配的结果,则选择作为符合条件的方法if (typeDiffWeight < minTypeDiffWeight) {factoryMethodToUse = candidate;argsHolderToUse = argsHolder;argsToUse = argsHolder.arguments;minTypeDiffWeight = typeDiffWeight;ambiguousFactoryMethods = null;}// Find out about ambiguity: In case of the same type difference weight// for methods with the same number of parameters, collect such candidates// and eventually raise an ambiguity exception.// However, only perform that check in non-lenient constructor resolution mode,// and explicitly ignore overridden methods (with the same parameter signature).// 如果具有相同参数数量的方法具有相同的类型差异权重,则收集此类型选项// 但是,仅在非宽松构造函数解析模式下执行该检查,并显式忽略重写方法(具有相同的参数签名)else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&!mbd.isLenientConstructorResolution() &¶mTypes.length == factoryMethodToUse.getParameterCount() &&!Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {// 查找到多个可匹配的方法if (ambiguousFactoryMethods == null) {ambiguousFactoryMethods = new LinkedHashSet<>();ambiguousFactoryMethods.add(factoryMethodToUse);}ambiguousFactoryMethods.add(candidate);}}}//没有找到对应的工厂方法,则抛出异常if (factoryMethodToUse == null) {if (causes != null) {UnsatisfiedDependencyException ex = causes.removeLast();for (Exception cause : causes) {this.beanFactory.onSuppressedException(cause);}throw ex;}ListargTypes = new ArrayList<>(minNrOfArgs);if (explicitArgs != null) {for (Object arg : explicitArgs) {argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");}}else if (resolvedValues != null) {SetvalueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());valueHolders.addAll(resolvedValues.getGenericArgumentValues());for (ValueHolder value : valueHolders) {String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :(value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));argTypes.add(argType);}}String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);throw new BeanCreationException(mbd.getResourceDescription(), beanName,"No matching factory method found: " +(mbd.getFactoryBeanName() != null ?"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +"Check that a method with the specified name " +(minNrOfArgs > 0 ? "and arguments " : "") +"exists and that it is " +(isStatic ? "static" : "non-static") + ".");}else if (void.class == factoryMethodToUse.getReturnType()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Invalid factory method '" + mbd.getFactoryMethodName() +"': needs to have a non-void return type!");}else if (ambiguousFactoryMethods != null) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous factory method matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousFactoryMethods);}//将解析出来的工厂方法和入参缓存,设置到 RootBeanDefinition 中,因为整个过程比较复杂,避免再次解析if (explicitArgs == null && argsHolderToUse != null) {mbd.factoryMethodToIntrospect = factoryMethodToUse;argsHolderToUse.storeCache(mbd, factoryMethodToUse);}}Assert.state(argsToUse != null, "Unresolved factory method arguments");//调用工厂方法创建一个实例对象(反射机制),并设置到 `bw` 中bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));return bw;
}
- 先创建一个 BeanWrapperImpl 对象 bw,并初始化,设置 ConversionService 类型转换器,并注册自定义的属性编辑器
// 构造 BeanWrapperImpl 对象BeanWrapperImpl bw = new BeanWrapperImpl();// 初始化 BeanWrapperImpl,设置 ConversionService 类型转换器,并注册自定义的属性编辑器this.beanFactory.initBeanWrapper(bw);
- 获取工厂方法的相关信息,根据 RootBeanDefinition 的 factoryBeanName 属性判断是否为静态方法,这个属性表示这个工厂方法所在类对应 Bean 的名称,当然静态方法“不属于”这个 Bean,所以它的这个属性为空
- factoryBeanName 属性不为空,表示不是静态方法,则需要根据 factoryBeanName 找到(依赖查找)对应的 Bean,作为 factoryBean
- 否则,就是静态方法,获取所在类的 Class 对象即可
// 获取工厂方法所在类对应的 Bean 的名称(静态方法不会有)String factoryBeanName = mbd.getFactoryBeanName();//非静态方法if (factoryBeanName != null) {if (factoryBeanName.equals(beanName)) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,"factory-bean reference points back to the same bean definition");}// 获取工厂方法所在类对应的 Bean,不然无法调用工厂方法factoryBean = this.beanFactory.getBean(factoryBeanName);// 如果是单例模式,已经存在对应的 Bean,则抛出重复创建的异常if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {throw new ImplicitlyAppearedSingletonException();}factoryClass = factoryBean.getClass();isStatic = false;}//静态方法else {// It's a static factory method on the bean class.// 静态方法没有找到对应的 Class 对象无法被调用,则抛出异常if (!mbd.hasBeanClass()) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,"bean definition declares neither a bean class nor a factory-bean reference");}factoryBean = null;factoryClass = mbd.getBeanClass();isStatic = true;}
:::warning
这一步找到了三个对象:factoryBean(工厂方法所在类对应的 Bean,静态方法不会有)、factoryClass(工厂方法所在类的 Class 对象)、isStatic(是否为静态方法)。所以想要通过工厂方法获取一个 Bean,则需要方法所在类对应的 Bean 先初始化,然后才能调用这个方法创建 Bean;而静态方法就不用,因为它可以根据所在类的 Class 对象就能调用这个方法创建 Bean,这就是两者的区别。
:::
- 尝试获取工厂方法 Method 对象和入参
- 如果方法入参指定了参数,则直接使用
- 否则,尝试从 RootBeanDefinition 中获取已解析出来的工厂方法和入参,如果获取到了未被解析过的入参,则进行解析(类型转换)
- 例如给定方法 A(int, int),配置了 A(“1”、“2”) 两个参数,则会转换为 A(1, 1),这一步尝试获取两个对象:factoryMethodToUse(对应的工厂方法 Method)、argsToUse(工厂方法的入参集合)
//如果方法入参指定了参数,则直接使用if (explicitArgs != null) {argsToUse = explicitArgs;}//否则,尝试从 RootBeanDefinition 中获取已解析出来的工厂方法和入参else {Object[] argsToResolve = null;// 因为可能前面解析了,会临时缓存,避免再次解析synchronized (mbd.constructorArgumentLock) { // 加锁factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;// 如果工厂方法被解析了,那么参数也可能解析过if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {// Found a cached factory method...argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 没有解析过的参数,则尝试从 RootBeanDefinition 中获取未被解析过的参数argsToResolve = mbd.preparedConstructorArguments;}}}// 如果获取到了未被解析过的入参,则进行解析if (argsToResolve != null) {// 处理参数值,类型转换,例如给定方法 A(int, int),配置了 A("1"、"2") 两个参数,则会转换为 A(1, 1)argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);}}
:::warning
这一步尝试获取两个对象:factoryMethodToUse(对应的工厂方法 Method)、argsToUse(工厂方法的入参集合)
:::
- 如果上一步没有找到工厂方法对象或方法入参集合,找到所有匹配的工厂方法,首先找到所有匹配的工厂方法
- 获取工厂方法所在的类的实例 Class 对象,因为可能是 Cglib 提升过的子类
- 获取工厂方法所在的类中所有方法对象
- 找到这个类中匹配的工厂方法,是否和 isStatic 匹配,并且和定义的工厂方法的名称是否相等
- 如果只有一个匹配的方法,且这个方法没有给指定的入参,且本身也没有定义参数,且这个方法没有定义入参,则直接调用这个方法创建一个实例对象(反射机制),并返回
// -------------------------找到所有匹配的工厂方法-------------------------//如果上一步没有找到工厂方法对象或方法入参集合,则需要进行接下来的解析过程,首先找到所有匹配的工厂方法if (factoryMethodToUse == null || argsToUse == null) {// Need to determine the factory method...// Try all methods with this name to see if they match the given arguments.//获取工厂方法所在的类的实例 Class 对象,因为可能是 Cglib 提升过的子类factoryClass = ClassUtils.getUserClass(factoryClass);//获取工厂方法所在的类中所有方法对象Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);//找到这个类中匹配的工厂方法ListcandidateList = new ArrayList<>();for (Method candidate : rawCandidates) {if (Modifier.isStatic(candidate.getModifiers()) == isStatic // 是否和 `isStatic` 匹配&& mbd.isFactoryMethod(candidate)) { // 和定义的工厂方法的名称是否相等candidateList.add(candidate);}}//如果只有一个匹配的方法,且这个方法没有给指定的入参,且本身也没有定义参数,且这个方法没有定义入参// 则直接调用这个方法创建一个实例对象(反射机制),并返回if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {Method uniqueCandidate = candidateList.get(0);if (uniqueCandidate.getParameterCount() == 0) {mbd.factoryMethodToIntrospect = uniqueCandidate;synchronized (mbd.constructorArgumentLock) {mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;mbd.constructorArgumentsResolved = true;mbd.resolvedConstructorArguments = EMPTY_ARGS;}bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));return bw;}}
- 开始找最匹配的工厂方法,先排序,public 方法优先,入参个数多的优先
- 确定方法参数的入参数量,匹配的方法的入参数量要多余它
- 如果当前方法指定了入参,则使用其个数作为最小值
- 否则,从 RootBeanDefinition 解析出方法的参数个数作为最小值,没有定义则设置为 0
- 这一步主要是确定入参的个数,并排序所有匹配的方法,接下来会遍历所有匹配的方法
// -------------------------开始找最匹配的工厂方法-------------------------//开始找最匹配的工厂方法// 将匹配的工厂方法转换成数组Method[] candidates = candidateList.toArray(new Method[0]);// 将匹配的方法进行排序,public 方法优先,入参个数多的优先AutowireUtils.sortFactoryMethods(candidates);// 用于承载解析后的方法参数值ConstructorArgumentValues resolvedValues = null;// 是否是构造器注入boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);int minTypeDiffWeight = Integer.MAX_VALUE;// 匹配方法的集合SetambiguousFactoryMethods = null;// -------------------------确定方法参数的入参数量-------------------------//确定方法参数的入参数量,匹配的方法的入参数量要多余它// 方法的参数数量的最小值int minNrOfArgs;//如果当前方法指定了入参,则使用其个数作为最小值if (explicitArgs != null) {minNrOfArgs = explicitArgs.length;}//否则,从 RootBeanDefinition 解析出方法的参数个数作为最小值else {// We don't have arguments passed in programmatically, so we need to resolve the// arguments specified in the constructor arguments held in the bean definition.// RootBeanDefinition 定义了参数值if (mbd.hasConstructorArgumentValues()) {// 方法的参数ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();// 解析定义的参数值,放入 `resolvedValues` 中,并返回参数个数minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}else {minNrOfArgs = 0;}}// 记录 UnsatisfiedDependencyException 异常的集合LinkedListcauses = null;
:::warning
这一步会找到这个方法的入参,依赖注入的方式
:::
- 因为会遍历所有匹配的方法,所以需要进行权重的判断,拿到最优先的那个
- 通常情况下,我们只有一个匹配的方法,如果存在多个,会根据方法的参数类型进行权重
// -------------------------根据权重获取最匹配的方法-------------------------//因为会遍历所有匹配的方法,所以需要进行权重的判断,拿到最优先的那个// 判断解析构造函数的时候是否以宽松模式还是严格模式,默认为 true// 严格模式:解析构造函数时,必须所有的都需要匹配,否则抛出异常// 宽松模式:使用具有"最接近的模式"进行匹配// typeDiffWeight:类型差异权重int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));// Choose this factory method if it represents the closest match.// 代表最匹配的结果,则选择作为符合条件的方法if (typeDiffWeight < minTypeDiffWeight) {factoryMethodToUse = candidate;argsHolderToUse = argsHolder;argsToUse = argsHolder.arguments;minTypeDiffWeight = typeDiffWeight;ambiguousFactoryMethods = null;}// Find out about ambiguity: In case of the same type difference weight// for methods with the same number of parameters, collect such candidates// and eventually raise an ambiguity exception.// However, only perform that check in non-lenient constructor resolution mode,// and explicitly ignore overridden methods (with the same parameter signature).// 如果具有相同参数数量的方法具有相同的类型差异权重,则收集此类型选项// 但是,仅在非宽松构造函数解析模式下执行该检查,并显式忽略重写方法(具有相同的参数签名)else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&!mbd.isLenientConstructorResolution() &¶mTypes.length == factoryMethodToUse.getParameterCount() &&!Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {// 查找到多个可匹配的方法if (ambiguousFactoryMethods == null) {ambiguousFactoryMethods = new LinkedHashSet<>();ambiguousFactoryMethods.add(factoryMethodToUse);}ambiguousFactoryMethods.add(candidate);}}}
- 没有找到对应的工厂方法,则抛出异常
- 将解析出来的工厂方法和入参缓存,设置到 RootBeanDefinition 中,因为整个过程比较复杂,避免再次解析
会缓存这几个数据:resolvedConstructorOrFactoryMethod(已经解析出来的工厂方法)、constructorArgumentsResolved(方法入参已经解析出来了 true)、resolvedConstructorArguments(解析出来的入参)
//没有找到对应的工厂方法,则抛出异常if (factoryMethodToUse == null) {if (causes != null) {UnsatisfiedDependencyException ex = causes.removeLast();for (Exception cause : causes) {this.beanFactory.onSuppressedException(cause);}throw ex;}ListargTypes = new ArrayList<>(minNrOfArgs);if (explicitArgs != null) {for (Object arg : explicitArgs) {argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");}}else if (resolvedValues != null) {SetvalueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());valueHolders.addAll(resolvedValues.getGenericArgumentValues());for (ValueHolder value : valueHolders) {String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :(value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));argTypes.add(argType);}}String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);throw new BeanCreationException(mbd.getResourceDescription(), beanName,"No matching factory method found: " +(mbd.getFactoryBeanName() != null ?"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +"Check that a method with the specified name " +(minNrOfArgs > 0 ? "and arguments " : "") +"exists and that it is " +(isStatic ? "static" : "non-static") + ".");}else if (void.class == factoryMethodToUse.getReturnType()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Invalid factory method '" + mbd.getFactoryMethodName() +"': needs to have a non-void return type!");}else if (ambiguousFactoryMethods != null) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous factory method matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousFactoryMethods);}//将解析出来的工厂方法和入参缓存,设置到 RootBeanDefinition 中,因为整个过程比较复杂,避免再次解析if (explicitArgs == null && argsHolderToUse != null) {mbd.factoryMethodToIntrospect = factoryMethodToUse;argsHolderToUse.storeCache(mbd, factoryMethodToUse);}}
- 调用工厂方法创建一个实例对象(反射机制),并设置到 bw 中
Assert.state(argsToUse != null, "Unresolved factory method arguments");//调用工厂方法创建一个实例对象(反射机制),并设置到 `bw` 中bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));return bw;
上面整个过程非常复杂,这里进行简单概括:
- 找到对应的工厂方法,如果是非静态方法,则需要先依赖查找到所在类对应的 Bean,因为需要根据这个 Bean 去调用对应的工厂方法,而静态方法不用,可以根据其 Class 对象调用对应的工厂方法
- 如果工厂方法有入参,则需要注入相关对象(依赖注入)
- 调用这个方法(反射机制),返回一个实例对象
2.3.3 构造器创建实例
当你用构造函数的方法创建一个Bean时,所有普通的类都可以被Spring使用并与之兼容。也就是说,被开发的类不需要实现任何特定的接口,也不需要以特定的方式进行编码。只需指定Bean类就足够了。然而,根据你对该特定Bean使用的IoC类型,你可能需要一个默认(空)构造函数。
无参构造
package com.shu.pojo;/*** @description:* @author: shu* @createDate: 2023/7/20 23:34* @version: 1.0*/
public class User {private String name;public User() {System.out.println("User的无参构造");}public User(String name) {this.name = name;System.out.println("User的有参构造");}public String getName() {return name;}public void setName(String name) {this.name = name;System.out.println("User的setName方法");}public void show() {System.out.println("name=" + name);}
}
<!-- 无参构造 --><bean id="user" class="com.shu.pojo.User"/>
/*** 构造函数实例化bean*/@Testpublic void test6(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");User user = (User) context.getBean("user");user.show();}
有参构造
介绍有参构造之前我们需要了解依赖注入的概念?
- 依赖注入(DI)是一个过程,对象仅通过构造参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后在其上设置的属性来定义它们的依赖(即与它们一起工作的其它对象)。然后,容器在创建 bean 时注入这些依赖。这个过程从根本上说是Bean本身通过使用类的直接构造或服务定位模式来控制其依赖的实例化或位置的逆过程(因此被称为控制反转)。
- 采用DI原则,代码会更干净,当对象被提供其依赖时,解耦会更有效。对象不会查找其依赖,也不知道依赖的位置或类别。因此,你的类变得更容易测试,特别是当依赖是在接口或抽象基类上时,这允许在单元测试中使用stub或mock实现。DI有两个主要的变体,基于构造器的依赖注入 和 基于setter的依赖注入。
构造器依赖注入
基于构造函数的 DI 是通过容器调用带有许多参数的构造函数来完成的,每个参数代表一个依赖。
<!-- 有参构造 --><bean id="user2" class="com.shu.pojo.User"><constructor-arg name="name" value="shu"/></bean>
package com.shu.pojo;/*** @description:* @author: shu* @createDate: 2023/7/20 23:34* @version: 1.0*/
public class User {private String name;public User() {System.out.println("User的无参构造");}public User(String name) {this.name = name;System.out.println("User的有参构造");}public String getName() {return name;}public void setName(String name) {this.name = name;System.out.println("User的setName方法");}public void show() {System.out.println("name=" + name);}
}
Setter的依赖注入
基于 Setter 的 DI 是通过容器在调用无参数的构造函数或无参数的 static 工厂方法来实例化你的 bean 之后调用 Setter 方法来实现的。简单来说就是调用了你的set发放,完成赋值,这也决定我们需要编写Set方法的实现,当然这也是我们Spring中常用的实例化
<bean id="user" class="com.shu.pojo.User"><property name="name" value="shu"/></bean>
下面我们来具体看看代码部分
AbstractAutowireCapableBeanFactory
// ========= 构造器实例化 =========// 4. 快速实例化对象,所谓的快速实例化,实际上是说使用缓存boolean resolved = false;boolean autowireNecessary = false;// 4.1 args: 外部化参数,只能当无外部参数时才使用缓存。不推荐使用外部化参数if (args == null) {synchronized (mbd.constructorArgumentLock) {// 4.2 是否使用缓存,其中autowireNecessary表示是否使用有参构造器// 无参时肯定不会解析,为false。有参时会解析,为trueif (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}// 4.3 使用缓存,其中autowireNecessary表示是否使用有参构造器if (resolved) {if (autowireNecessary) {// 4.4 有参构造器实例化return autowireConstructor(beanName, mbd, null, null);} else {// 4.5 无参构造器实例化return instantiateBean(beanName, mbd);}}// 5. 到此,只能老老实实的解析,当然解析后会将解析后的构造器或参数缓存起来// 5.1 是否指定了构造器,ibp#determineCandidateConstructorsConstructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);// 5.2 构造器实例化if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// 5.3 不用管,默认都是 null。ctors = mbd.getPreferredConstructors();if (ctors != null) {return autowireConstructor(beanName, mbd, ctors, null);}// 5.4 无参构造器实例化return instantiateBean(beanName, mbd);
}
我们可以看到上面代码的两个关键方法有参实例化与无参实例化
autowireConstructor实例化
这个过程和上一个方法一样非常复杂,不过差不太多,你可以理解为去找到当前 Bean 的构造方法,获取相关入参(构造器注入),然后调用该构造方法返回一个实例对象(反射机制)
AbstractAutowireCapableBeanFactory
protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor[] ctors, @Nullable Object[] explicitArgs) {return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
创建 ConstructorResolver 对象,然后调用其 autowireConstructor(…) 方法
ConstructorResolver
// ConstructorResolver.java
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,@Nullable Constructor[] chosenCtors, @Nullable Object[] explicitArgs) {// 构造 BeanWrapperImpl 对象BeanWrapperImpl bw = new BeanWrapperImpl();// 初始化 BeanWrapperImpl,设置 ConversionService 类型转换器,并注册自定义的属性编辑器this.beanFactory.initBeanWrapper(bw);// -------------------------尝试获取构造方法和入参-------------------------//尝试获取构造方法和入参// 构造方法Constructor constructorToUse = null;ArgumentsHolder argsHolderToUse = null;// 构造方法的入参集合Object[] argsToUse = null;//如果当前方法入参指定了参数,则直接使用if (explicitArgs != null) {argsToUse = explicitArgs;}//否则,尝试从 RootBeanDefinition 中获取已解析出来的构造方法和入参else {// 因为可能前面解析了,会临时缓存,避免再次解析Object[] argsToResolve = null;synchronized (mbd.constructorArgumentLock) {constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod;// 如果构造方法被解析了,那么参数也可能解析过if (constructorToUse != null && mbd.constructorArgumentsResolved) {// Found a cached constructor...argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 没有解析过的参数,则尝试从 RootBeanDefinition(合并后)中获取未被解析过的参数argsToResolve = mbd.preparedConstructorArguments;}}}// 如果获取到了未被解析过的入参if (argsToResolve != null) {// 处理参数值,类型转换,例如给定方法 A(int, int),配配置了 A("1"、"2") 两个参数,则会转换为 A(1, 1)argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);}}// -------------------------开始获取构造方法和入参-------------------------//如果上一步没有找到构造方法或入参集合,找到所有匹配的工厂方法,首先找到所有匹配的构造方法if (constructorToUse == null || argsToUse == null) {// Take specified constructors, if any.//获取所有的构造方法,如果当前方法指定了构造方法的集合,则使用这个集合Constructor[] candidates = chosenCtors;if (candidates == null) {Class beanClass = mbd.getBeanClass();try {candidates = (mbd.isNonPublicAccessAllowed() ?beanClass.getDeclaredConstructors() : beanClass.getConstructors());}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Resolution of declared constructors on bean Class [" + beanClass.getName() +"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);}}//如果构造方法只有一个,且当前方法的入参没有指定参数,且本身也没有定义参数,且这个构造方法没有定义入参// 则直接调用这个构造方法创建一个实例对象(反射机制),并返回if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {Constructor uniqueCandidate = candidates[0];if (uniqueCandidate.getParameterCount() == 0) {synchronized (mbd.constructorArgumentLock) {mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;mbd.constructorArgumentsResolved = true;mbd.resolvedConstructorArguments = EMPTY_ARGS;}bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));return bw;}}// Need to resolve the constructor.// 是否是构造器注入boolean autowiring = (chosenCtors != null ||mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);// 用于承载解析后的方法参数值ConstructorArgumentValues resolvedValues = null;// -------------------------确定构造方法的入参数量-------------------------//确定构造参数的入参数量,匹配的方法的入参数量要多余它// 方法的参数数量的最小值int minNrOfArgs;if (explicitArgs != null) {minNrOfArgs = explicitArgs.length;}// 从 RootBeanDefinition 解析出方法的参数个数作为最小值else {ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}//将所有的构造方法进行排序,public 方法优先,入参个数多的优先AutowireUtils.sortConstructors(candidates);int minTypeDiffWeight = Integer.MAX_VALUE;Set<Constructor> ambiguousConstructors = null;LinkedListcauses = null;// 遍历所有构造函数for (Constructor candidate : candidates) {// 获取该构造方法的参数类型Class[] paramTypes = candidate.getParameterTypes();// 如果前面已经找到匹配的构造方法和入参,则直接结束循环if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) {// Already found greedy constructor that can be satisfied ->// do not look any further, there are only less greedy constructors left.break;}// 如果这个构造方法的参数个数小于入参数量,则跳过if (paramTypes.length < minNrOfArgs) {continue;}// -------------------------解析出构造方法的入参-------------------------//解析出构造方法的入参// 保存参数的对象ArgumentsHolder argsHolder;//通过**依赖注入**获取入参if (resolvedValues != null) {try {// 获取构造方法的参数名称(@ConstructorProperties 注解)String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);if (paramNames == null) {// 没有获取到则通过 ParameterNameDiscoverer 参数探测器获取参数名称ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();if (pnd != null) {paramNames = pnd.getParameterNames(candidate);}}// 解析出方法的入参,参数值会被依赖注入argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);}catch (UnsatisfiedDependencyException ex) {if (logger.isTraceEnabled()) {logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);}// Swallow and try next constructor.if (causes == null) {causes = new LinkedList<>();}causes.add(ex);continue;}}//如果当前方法的入参指定了参数,如果个数相等则直接使用else {// Explicit arguments given -> arguments length must match exactly.if (paramTypes.length != explicitArgs.length) {continue;}argsHolder = new ArgumentsHolder(explicitArgs);}// -------------------------根据权重获取最匹配的方法-------------------------//因为会遍历所有匹配的方法,所以需要进行权重的判断,拿到最优先的那个// 判断解析构造函数的时候是否以宽松模式还是严格模式,默认为 true// 严格模式:解析构造函数时,必须所有的都需要匹配,否则抛出异常// 宽松模式:使用具有"最接近的模式"进行匹配// typeDiffWeight:类型差异权重int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));// Choose this constructor if it represents the closest match.// 代表最匹配的结果,则选择作为符合条件的方法if (typeDiffWeight < minTypeDiffWeight) {constructorToUse = candidate;argsHolderToUse = argsHolder;argsToUse = argsHolder.arguments;minTypeDiffWeight = typeDiffWeight;ambiguousConstructors = null;}else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {if (ambiguousConstructors == null) {ambiguousConstructors = new LinkedHashSet<>();ambiguousConstructors.add(constructorToUse);}ambiguousConstructors.add(candidate);}}//没有找到对应的构造方法,则抛出异常if (constructorToUse == null) {if (causes != null) {UnsatisfiedDependencyException ex = causes.removeLast();for (Exception cause : causes) {this.beanFactory.onSuppressedException(cause);}throw ex;}throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Could not resolve matching constructor " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");}else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous constructor matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousConstructors);}//将解析出来的工厂方法和入参缓存,设置到 RootBeanDefinition 中,因为整个过程比较复杂,避免再次解析if (explicitArgs == null && argsHolderToUse != null) {argsHolderToUse.storeCache(mbd, constructorToUse);}}Assert.state(argsToUse != null, "Unresolved constructor arguments");//调用这个构造方法返回一个实例对象(反射机制),并设置到 `bw` 中bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));return bw;
}
详细的步骤参考上面的工厂创建的解析,我们这里总结一下’
上**面整个过程非常复杂,这里进行简单概括:**
- 找到最匹配的构造方法
- 如果构造方法有入参,则需要注入相关对象(构造器注入,其实也是依赖注入获取到的参数)
- 调用这个构造方法(反射机制),返回一个实例对象
instantiateBean 方法
兜底方法,如果构造方法找不到(或者已经解析出来的构造方法),则直接使用默认的构造方法(或者已经解析出来的构造方法),返回一个实例对象
AbstractAutowireCapableBeanFactory
// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {try {Object beanInstance;final BeanFactory parent = this;// 安全模式if (System.getSecurityManager() != null) {beanInstance = AccessController.doPrivileged((PrivilegedAction) () ->// 获得 InstantiationStrategy 对象,并使用它,创建 Bean 对象getInstantiationStrategy().instantiate(mbd, beanName, parent),getAccessControlContext());}else {// 获得 InstantiationStrategy 对象,并使用它,创建 Bean 对象beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);}BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);}
}
可以看到会通过 CglibSubclassingInstantiationStrategy#instantiate(…) 方法创建一个实例对象,该方法如下
SimpleInstantiationStrategy
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {// Don't override the class with CGLIB if no overrides.//没有 MethodOverride 对象,也就是没有需要覆盖或替换的方法,则直接使用反射机制进行实例化即可if (!bd.hasMethodOverrides()) {Constructor constructorToUse;synchronized (bd.constructorArgumentLock) {//尝试从 RootBeanDefinition 获得已经解析出来的构造方法 `resolvedConstructorOrFactoryMethod`constructorToUse = (Constructor) bd.resolvedConstructorOrFactoryMethod;//没有解析出来的构造方法,则获取默认的构造方法if (constructorToUse == null) {final Class clazz = bd.getBeanClass();// 如果是接口,抛出 BeanInstantiationException 异常if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {// 从 clazz 中,获得构造方法if (System.getSecurityManager() != null) { // 安全模式constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor>) clazz::getDeclaredConstructor);}else {constructorToUse = clazz.getDeclaredConstructor();}// 标记 resolvedConstructorOrFactoryMethod 属性bd.resolvedConstructorOrFactoryMethod = constructorToUse;}catch (Throwable ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}}//通过这个构造方法实例化一个对象(反射机制)return BeanUtils.instantiateClass(constructorToUse);}//否则,通过 CGLIB 生成一个子类对象else {// Must generate CGLIB subclass.return instantiateWithMethodInjection(bd, beanName, owner);}
}
过程大致如下:
- 没有 MethodOverride 对象,也就是没有需要覆盖或替换的方法,则直接使用反射机制进行实例化即可
- 尝试从 RootBeanDefinition 获得已经解析出来的构造方法 resolvedConstructorOrFactoryMethod
- 没有解析出来的构造方法,则获取默认的构造方法
- 通过这个构造方法实例化一个对象(反射机制)
- 否则,通过 CGLIB 生成一个子类对象
到这我们就介绍完了Bean的创建,基础知识参考:Spring 的基本介绍_长安不及十里的博客-CSDN博客
下面回到doCreatBean方法中
2.4 Bean的前置处理
AbstractAutowireCapableBeanFactory
// Allow post-processors to modify the merged bean definition.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;}}
找到所有实现了MergedBeanDefinitionPostProcessor的实例 ,然后访问其postProcessMergedBeanDefinition()方法。这里主要是为了后面填充Bean属性做一些前置准备。
由于篇幅原因我们下篇文章详细解决下面的流程
相关文章:

Spring源码分析(八)CreateBean与DoCreateBean
写目录 一 CreateBean二 doCreateBean2.1 BeanWrapper2.2 缓存删除2.3 实例化Bean2.3.1 Supplier创建对象2.3.2 工厂创建对象2.3.3 构造器创建实例无参构造构造器依赖注入Setter的依赖注入autowireConstructor实例化instantiateBean 方法 2.4 Bean的前置处理 官网:H…...

iSCSI存储服务器
目录 一、ISCSI是什么? 二、ISCSI产生背景 三、存储分类 四、ISCSI架构 五、ISCSI存储服务搭建案例 一、ISCSI是什么? ISCSI名为互联网小型计算机系统接口又称为IP-SAN,是一种新的远程存储技术,提供存储服务的目标服务器默认使用的…...

信息技术02--初/高中--分类选择题(377道题与解析)
文章目录 第一章 办公软件 1-96第二章 信息技术基础 1-41第三章 计算机系统基础 1-28第四章 多媒体技术 1-115第五章 计算机网络技术 1-50第六章 信息安全 1-3第七章 算法与程序简介 1-13第八章 数据结构 1-2第九章 数据库技术 1-20第十章 练习 1-9 第一章 办公软件 1-96 1、某…...
java --- 枚举类
目录 一、枚举类 二、创建枚举类 2.1、JDK5.0之前创建 2.2、JDK5.0使用enum创建 三、枚举类常用方法 四、枚举类实现接口 一、枚举类 枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。 二、创建枚举类 …...

nvm和volta对node版本控制的区别
前言——我们做前端开发的都会需要node.js环境,我们直接安装指定的版本可以么?可以,只不过在需要换版本的时候还得卸载重新装。那有工具可以帮助我们不用卸载就更改node版本么?有啊,nvm就可以。那又有没有什么工具不用…...
高斯消元解线性方程组
思路: (1)模拟线性代数解方程组办法,在此讨论正方形方程组求解。 (2)考虑几个问题: 数据存储:采用double数组存储。判断是否为零,由于double计算存在误差,…...
【linux命令讲解大全】032.介绍 Linux 中的 rcp 命令:简化主机间文件复制操作
文章目录 rcp补充说明语法选项参数实例rcp命令使用条件 从零学 python rcp 使在两台Linux主机之间的文件复制操作更简单 补充说明 rcp命令使在两台Linux主机之间的文件复制操作更简单。通过适当的配置,在两台Linux主机之间复制文件而无需输入密码,就像…...

Mysql索引、事务与存储引擎 (事务、MySQL 存储引擎)
事务 一、事务的概念: ①事务是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行。 ②事务是一个不可分割的工…...

Doris(六)--通过 Canal 同步数据到 Doris 中
pre 开启 mysql Binlog 网上有众多方法,自行百度。 查询是否成功,在 mysql 客户端输入 show BINARY LOGS; 出现如下提示,即表示 big log 正常开启。 1,下载 canal 服务端 传送门 注意:下载 canal.deployer-xxx …...

快手Java一面,全是基础
现在已经到了面试招聘比较火热的时候,准备面试的过程中,一定要多看面经,多自测! 今天分享的是一位贵州大学的同学分享的快手一面面经。 快手一面主要会问一些基础问题,也就是比较简单且容易准备的常规八股࿰…...

未来芯片设计领域的药明康德——青芯如何在N个项目间游走平衡
总部位于上海张江的青芯半导体(CyanSemi),ASIC定制设计是其核心业务之一。 青芯在单纯的设计服务维度之上,打造了从设计到生产的一套完整ASIC定制业务,不仅做芯片设计,还提供封装、测试服务,也…...
【跟小嘉学 Rust 编程】十九、高级特性
系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...

pandas由入门到精通-数据清洗-缺失值处理
pandas-02-数据清洗&预处理 A.缺失值处理1. Pandas缺失值判断2. 缺失值过滤2.1 Series.dropna()2.2 DataFrame.dropna()3. 缺失值填充3.1 值填充3.2 向前/向后填充文中用S代指Series,用Df代指DataFrame 数据清洗是处理大型复杂情况数据必不可少的步骤,这里总结一些数据清…...
Redis 教程 - 主从复制
Redis 教程 - 主从复制 Redis 支持主从复制(Master-Slave Replication),通过主从复制可以将一个 Redis 服务器(主节点)的数据复制到其他 Redis 服务器(从节点),以实现数据的冗余备份…...

[递归] 子集 全排列和组合问题
1.1 子集I 思路可以简单概括为 二叉树,每一次分叉要么选择一个元素,要么选择空,总共有n次,因此到n1进行保存结果,返回。像这样: #include <cstdio> #include <vector> #include <algorithm&…...

ELK安装、部署、调试(四)KAFKA消息队列的安装和部署
1.简介 Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通…...

半导体晶片机器视觉测量及MARK点视觉定位
半导体晶片机器视觉测量及MARK点视觉定位 客户的需求: 检测内容: SMT行业晶片位置角度与PCB板Mark点位置的测试测量 检测要求: 精度0.04mm,移动速度100mm/s 视觉可行性分析: 对样品进行了光学实验,并进行图像处理,…...

ranger无法同步用户问题解决
1.首先就是定位日志,日志目录 cd /var/log/ranger/usersync 定位到问题报错如下: LdapDeltaUserGroupBuilder.getUsers() failed with exception:java.naming.AuthticationExceptiom :[LDAP:error code 49 - Invalid Credentials]:remaing name ‘ouPeople,dc*.dccom’ 解决办法…...

使用通信顺序进程(CSP)模型的 Go 语言通道
在并发编程中,许多编程语言采用共享内存/状态模型。然而,Go 通过实现 通信顺序进程(CSP)模型来区别于众多。在CSP中,程序由不共享状态的并行进程组成;相反,它们通过通道进行通信和同步操作。因此…...

VPN网关
阿里云VPN网关(VPN Gateway,简称VPN)是一款基于Internet,通过加密通道将企业数据中心、办公网或终端与专有网络(VPC) 安全可靠连接起来的服务。 VPN网关提供IPsec-VPN和SSL-VPN两种。 网络连接方式应用场景IPsec-VPN支持在企业本地数据中心、企业办公网…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...

链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...
shell脚本质数判断
shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数)shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数) 思路: 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...
使用python进行图像处理—图像滤波(5)
图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值,以达到平滑(去噪)、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算,…...
MyBatis-Plus 常用条件构造方法
1.常用条件方法 方法 说明eq等于 ne不等于 <>gt大于 >ge大于等于 >lt小于 <le小于等于 <betweenBETWEEN 值1 AND 值2notBetweenNOT BETWEEN 值1 AND 值2likeLIKE %值%notLikeNOT LIKE %值%likeLeftLIKE %值likeRightLIKE 值%isNull字段 IS NULLisNotNull字段…...