Spring IOC source code analysis (2)

Black hole code 2021-01-14 16:28:37
spring ioc source code analysis


Contains a lot of source code , It's recommended to watch it horizontally

Next post :Spring IOC The source code parsing ( On )

Instantiation Bean

The last step created BeanFactory, And will BeanDefinition Registered to BeanFactory Medium ConcurrentHashMap It's in . And take BeanName by key,BeanFactory by value. So now we have Bean Definition , But there are no examples , It's not built Bean Dependencies between . We know , Building dependencies is IOC It's an important task for us , How can we let go of . So where was it made ? stay finishBeanFactoryInitialization(beanFactory) In the method , The method is defined as follows :

AbstractApplicationContext

Code block

Java

/**
 * Finish the initialization of this context's bean factory,
 * initializing all remaining singleton beans.
 */
 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
 // Initialize conversion service for this context.
 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
 beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
 beanFactory.setConversionService(
 beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
 }

 // Register a default embedded value resolver if no bean post-processor
 // (such as a PropertyPlaceholderConfigurer bean) registered any before:
 // at this point, primarily for resolution in annotation attribute values.
 if (!beanFactory.hasEmbeddedValueResolver()) {
 beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
 }

 // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
 String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
 for (String weaverAwareName : weaverAwareNames) {
 getBean(weaverAwareName);
 }

 // Stop using the temporary ClassLoader for type matching.
 beanFactory.setTempClassLoader(null);

 // Allow for caching all bean definition metadata, not expecting further changes.
 beanFactory.freezeConfiguration();

 // Instantiate all remaining (non-lazy-init) singletons.
 beanFactory.preInstantiateSingletons();
 }

DefaultListableBeanFactory

An important step in this approach is : beanFactory.preInstantiateSingletons(), We need to look at the implementation of this method :

Code block

Java

@Override
 public void preInstantiateSingletons() throws BeansException {
 if (logger.isDebugEnabled()) {
 logger.debug("Pre-instantiating singletons in " + this);
 }

 // Iterate over a copy to allow for init methods which in turn register new bean definitions.
 // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

 // Trigger initialization of all non-lazy singleton beans...
 for (String beanName : beanNames) {
 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
 if (isFactoryBean(beanName)) {
 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
 if (bean instanceof FactoryBean) {
 final FactoryBean<?> factory = (FactoryBean<?>) bean;
 boolean isEagerInit;
 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
 isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
 ((SmartFactoryBean<?>) factory)::isEagerInit,
 getAccessControlContext());
 }
 else {
 isEagerInit = (factory instanceof SmartFactoryBean &&
 ((SmartFactoryBean<?>) factory).isEagerInit());
 }
 if (isEagerInit) {
 getBean(beanName);
 }
 }
 }
 else {
 getBean(beanName);
 }
 }
 }

 // Trigger post-initialization callback for all applicable beans...
 for (String beanName : beanNames) {
 Object singletonInstance = getSingleton(beanName);
 if (singletonInstance instanceof SmartInitializingSingleton) {
 final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
 if (System.getSecurityManager() != null) {
 AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
 smartSingleton.afterSingletonsInstantiated();
 return null;
 }, getAccessControlContext());
 }
 else {
 smartSingleton.afterSingletonsInstantiated();
 }
 }
 }
 }

The method first loops through all BeanNames, And call getBean(beanName) Method , The approach is actually to create bean And recursively build dependencies .getBean(beanName) The method will eventually be called doGetBean(name, null, null, false), Let's go into the method and see

/**

* Return an instance, which may be shared or independent, of the specified bean.

* @param name the name of the bean to retrieve

* @param requiredType the required type of the bean to retrieve

* @param args arguments to use when creating a bean instance using explicit arguments

* (only applied when creating a new instance as opposed to retrieving an existing one)

* @param typeCheckOnly whether the instance is obtained for a type check,

* not for actual use

* @return an instance of the bean

* @throws BeansException if the bean could not be created

*/

@SuppressWarnings("unchecked")

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,

@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

final String beanName = transformedBeanName(name);

Object bean;

// Eagerly check singleton cache for manually registered singletons.

Object sharedInstance = getSingleton(beanName);

if (sharedInstance != null && args == null) {

if (logger.isDebugEnabled()) {

if (isSingletonCurrentlyInCreation(beanName)) {

logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +

"' that is not fully initialized yet - a consequence of a circular reference");

}

else {

logger.debug("Returning cached instance of singleton bean '" + beanName + "'");

}

}

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

}

else {

// Fail if we're already creating this bean instance:

// We're assumably within a circular reference.

if (isPrototypeCurrentlyInCreation(beanName)) {

throw new BeanCurrentlyInCreationException(beanName);

}

// Check if bean definition exists in this factory.

BeanFactory parentBeanFactory = getParentBeanFactory();

if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {

// Not found -> check parent.

String nameToLookup = originalBeanName(name);

if (parentBeanFactory instanceof AbstractBeanFactory) {

return ((AbstractBeanFactory) parentBeanFactory).doGetBean(

nameToLookup, requiredType, args, typeCheckOnly);

}

else if (args != null) {

// Delegation to parent with explicit args.

return (T) parentBeanFactory.getBean(nameToLookup, args);

}

else {

// No args -> delegate to standard getBean method.

return parentBeanFactory.getBean(nameToLookup, requiredType);

}

}

if (!typeCheckOnly) {

markBeanAsCreated(beanName);

}

try {

final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

checkMergedBeanDefinition(mbd, beanName, args);

// Guarantee initialization of beans that the current bean depends on.

String[] dependsOn = mbd.getDependsOn();

if (dependsOn != null) {

for (String dep : dependsOn) {

if (isDependent(beanName, dep)) {

throw new BeanCreationException(mbd.getResourceDescription(), beanName,

"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");

}

registerDependentBean(dep, beanName);

try {

getBean(dep);

}

catch (NoSuchBeanDefinitionException ex) {

throw new BeanCreationException(mbd.getResourceDescription(), beanName,

"'" + beanName + "' depends on missing bean '" + dep + "'", ex);

}

}

}

// Create bean instance.

if (mbd.isSingleton()) {

sharedInstance = getSingleton(beanName, () -> {

try {

return createBean(beanName, mbd, args);

}

catch (BeansException ex) {

// Explicitly remove instance from singleton cache: It might have been put there

// eagerly by the creation process, to allow for circular reference resolution.

// Also remove any beans that received a temporary reference to the bean.

destroySingleton(beanName);

throw ex;

}

});

bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

}

else if (mbd.isPrototype()) {

// It's a prototype -> create a new instance.

Object prototypeInstance = null;

try {

beforePrototypeCreation(beanName);

prototypeInstance = createBean(beanName, mbd, args);

}

finally {

afterPrototypeCreation(beanName);

}

bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);

}

else {

String scopeName = mbd.getScope();

final Scope scope = this.scopes.get(scopeName);

if (scope == null) {

throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");

}

try {

Object scopedInstance = scope.get(beanName, () -> {

beforePrototypeCreation(beanName);

try {

return createBean(beanName, mbd, args);

}

finally {

afterPrototypeCreation(beanName);

}

});

bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);

}

catch (IllegalStateException ex) {

throw new BeanCreationException(beanName,

"Scope '" + scopeName + "' is not active for the current thread; consider " +

"defining a scoped proxy for this bean if you intend to refer to it from a singleton",

ex);

}

}

}

catch (BeansException ex) {

cleanupAfterBeanCreationFailure(beanName);

throw ex;

}

}

// Check if required type matches the type of the actual bean instance.

if (requiredType != null && !requiredType.isInstance(bean)) {

try {

T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);

if (convertedBean == null) {

throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());

}

return convertedBean;

}

catch (TypeMismatchException ex) {

if (logger.isDebugEnabled()) {

logger.debug("Failed to convert bean '" + name + "' to required type '" +

ClassUtils.getQualifiedName(requiredType) + "'", ex);

}

throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());

}

}

return (T) bean;

}

It's a long way , Select only part of the source code for analysis :

Code block

Java

// Guarantee initialization of beans that the current bean depends on.
 String[] dependsOn = mbd.getDependsOn();
 if (dependsOn != null) {
 for (String dep : dependsOn) {
 if (isDependent(beanName, dep)) {
 throw new BeanCreationException(mbd.getResourceDescription(), beanName,
 "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
 }
 registerDependentBean(dep, beanName);
 try {
 getBean(dep);
 }
 catch (NoSuchBeanDefinitionException ex) {
 throw new BeanCreationException(mbd.getResourceDescription(), beanName,
 "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
 }
 }
 }

 // Create bean instance.
 if (mbd.isSingleton()) {
 sharedInstance = getSingleton(beanName, () -> {
 try {
 return createBean(beanName, mbd, args);
 }
 catch (BeansException ex) {
 // Explicitly remove instance from singleton cache: It might have been put there
 // eagerly by the creation process, to allow for circular reference resolution.
 // Also remove any beans that received a temporary reference to the bean.
 destroySingleton(beanName);
 throw ex;
 }
 });
 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
 }

You can see , This method first gets the dependencies mbd.getDependsOn();, Take the dependent BeanName Recursively call getBean Method , Until the call getSingleton Method returns a dependency bean, and getSingleton The parameters of the method are createBean() Method returns an instance of .createBean() Is in AbstractAutowireCapableBeanFactory Implemented in .

AbstractAutowireCapableBeanFactory

createBean Method realization :

Code block

Java

/**
 * Central method of this class: creates a bean instance,
 * populates the bean instance, applies post-processors, etc.
 * @see #doCreateBean
 */
 @Override
 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;

 // Make sure bean class is actually resolved at this point, and
 // clone the bean definition in case of a dynamically resolved Class
 // which cannot be stored in the shared merged bean definition.
 Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
 if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
 mbdToUse = new RootBeanDefinition(mbd);
 mbdToUse.setBeanClass(resolvedClass);
 }

 // Prepare method overrides.
 try {
 mbdToUse.prepareMethodOverrides();
 }
 catch (BeanDefinitionValidationException ex) {
 throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
 beanName, "Validation of method overrides failed", ex);
 }

 try {
 // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
 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 {
 Object beanInstance = doCreateBean(beanName, mbdToUse, args);
 if (logger.isDebugEnabled()) {
 logger.debug("Finished creating instance of bean '" + beanName + "'");
 }
 return beanInstance;
 }
 catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
 // A previously detected exception with proper bean creation context already,
 // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
 throw ex;
 }
 catch (Throwable ex) {
 throw new BeanCreationException(
 mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
 }
 }
from createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) The implementation of the method can be seen , This method is called internally doCreateBean Method ,doCreateBean The method is implemented as follows :

/**

* Actually create the specified bean. Pre-creation processing has already happened

* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.

* <p>Differentiates between default bean instantiation, use of a

* factory method, and autowiring a constructor.

* @param beanName the name of the bean

* @param mbd the merged bean definition for the bean

* @param args explicit arguments to use for constructor or factory method invocation

* @return a new instance of the bean

* @throws BeanCreationException if the bean could not be created

* @see #instantiateBean

* @see #instantiateUsingFactoryMethod

* @see #autowireConstructor

*/

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)

throws BeanCreationException {

// Instantiate the bean.

BeanWrapper instanceWrapper = null;

if (mbd.isSingleton()) {

instanceWrapper = 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;

}

// 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;

}

}

// Eagerly cache singletons to be able to resolve circular references

// even when triggered by lifecycle interfaces like BeanFactoryAware.

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");

}

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

}

// Initialize the bean instance.

Object exposedObject = bean;

try {

populateBean(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) {

Object earlySingletonReference = getSingleton(beanName, false);

if (earlySingletonReference != null) {

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.");

}

}

}

}

// Register bean as disposable.

try {

registerDisposableBeanIfNecessary(beanName, bean, mbd);

}

catch (BeanDefinitionValidationException ex) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);

}

return exposedObject;

}

It's a long way , Just pick two important lines of code to explain :

  1. instanceWrapper = createBeanInstance(beanName, mbd, args) Create examples .
  2. populateBean(beanName, mbd, instanceWrapper) , This method is used to fill in Bean, This method can be said to be the place where dependency injection occurs .

createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) The method is as follows :

/**

* Create a new instance for the specified bean, using an appropriate instantiation strategy:

* factory method, constructor autowiring, or simple instantiation.

* @param beanName the name of the bean

* @param mbd the bean definition for the bean

* @param args explicit arguments to use for constructor or factory method invocation

* @return a BeanWrapper for the new instance

* @see #obtainFromSupplier

* @see #instantiateUsingFactoryMethod

* @see #autowireConstructor

* @see #instantiateBean

*/

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {

// Make sure bean class is actually resolved at this point.

Class<?> beanClass = resolveBeanClass(mbd, beanName);

if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {

throw new BeanCreationException(mbd.getResourceDescription(), beanName,

"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());

}

Supplier<?> instanceSupplier = mbd.getInstanceSupplier();

if (instanceSupplier != null) {

return obtainFromSupplier(instanceSupplier, beanName);

}

if (mbd.getFactoryMethodName() != null) {

return instantiateUsingFactoryMethod(beanName, mbd, args);

}

// Shortcut when re-creating the same bean...

boolean resolved = false;

boolean autowireNecessary = false;

if (args == null) {

synchronized (mbd.constructorArgumentLock) {

if (mbd.resolvedConstructorOrFactoryMethod != null) {

resolved = true;

autowireNecessary = mbd.constructorArgumentsResolved;

}

}

}

if (resolved) {

if (autowireNecessary) {

return autowireConstructor(beanName, mbd, null, null);

}

else {

return instantiateBean(beanName, mbd);

}

}

// Need to determine the constructor...

Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

if (ctors != null ||

mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||

mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {

return autowireConstructor(beanName, mbd, ctors, args);

}

// No special handling: simply use no-arg constructor.

return instantiateBean(beanName, mbd);

}

Look at the last line of code , call instantiateBean(beanName, mbd) Method , Let's look at the implementation of this method :

/**

* Instantiate the given bean using its default constructor.

* @param beanName the name of the bean

* @param mbd the bean definition for the bean

* @return a BeanWrapper for the new instance

*/

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {

try {

Object beanInstance;

final BeanFactory parent = this;

if (System.getSecurityManager() != null) {

beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->

getInstantiationStrategy().instantiate(mbd, beanName, parent),

getAccessControlContext());

}

else {

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);

}

}

instantiateBean(final String beanName, final RootBeanDefinition mbd) The core logic of the method is beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent), carry BeanName ,RootBeanDefinition , The target of strategy is SimpleInstantiationStrategy, This method calls static methods internally BeanUtils.instantiateClass(constructorToUse), The implementation of this method is as follows :

/**

* Convenience method to instantiate a class using the given constructor.

* <p>Note that this method tries to set the constructor accessible if given a

* non-accessible (that is, non-public) constructor, and supports Kotlin classes

* with optional parameters and default values.

* @param ctor the constructor to instantiate

* @param args the constructor arguments to apply (use {@code null} for an unspecified

* parameter if needed for Kotlin classes with optional parameters and default values)

* @return the new instance

* @throws BeanInstantiationException if the bean cannot be instantiated

* @see Constructor#newInstance

*/

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {

Assert.notNull(ctor, "Constructor must not be null");

try {

ReflectionUtils.makeAccessible(ctor);

return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?

KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));

}

catch (InstantiationException ex) {

throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);

}

catch (IllegalAccessException ex) {

throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);

}

catch (IllegalArgumentException ex) {

throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);

}

catch (InvocationTargetException ex) {

throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());

}

}

And then call Constructor Of newInstance Method , That is to say, the instance is finally created with reflection .PS: This method will determine whether it is Kotlin type . If not , Call the instance method of the constructor .

Come here , Our instance has been created . But the dependency of our instance has not been set yet , We were just doCreateBean The method is concerned with the second 2 Line code :

populateBean(beanName, mbd, instanceWrapper) , This method is used to fill in Bean, This method can be said to be the place where dependency injection occurs . It's still coming AbstractAutowireCapableBeanFactory Class populateBean() Method implementation .

AbstractAutowireCapableBeanFactory

/**

* Populate the bean instance in the given BeanWrapper with the property values

* from the bean definition.

* @param beanName the name of the bean

* @param mbd the bean definition for the bean

* @param bw the BeanWrapper with bean instance

*/

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

if (bw == null) {

if (mbd.hasPropertyValues()) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");

}

else {

// Skip property population phase for null instance.

return;

}

}

// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the

// state of the bean before properties are set. This can be used, for example,

// to support styles of field injection.

boolean continueWithPropertyPopulation = true;

if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

for (BeanPostProcessor bp : getBeanPostProcessors()) {

if (bp instanceof InstantiationAwareBeanPostProcessor) {

InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;

if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {

continueWithPropertyPopulation = false;

break;

}

}

}

}

if (!continueWithPropertyPopulation) {

return;

}

PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||

mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {

MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

// Add property values based on autowire by name if applicable.

if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {

autowireByName(beanName, mbd, bw, newPvs);

}

// Add property values based on autowire by type if applicable.

if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {

autowireByType(beanName, mbd, bw, newPvs);

}

pvs = newPvs;

}

boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();

boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

if (hasInstAwareBpps || needsDepCheck) {

if (pvs == null) {

pvs = mbd.getPropertyValues();

}

PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);

if (hasInstAwareBpps) {

for (BeanPostProcessor bp : getBeanPostProcessors()) {

if (bp instanceof InstantiationAwareBeanPostProcessor) {

InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;

pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);

if (pvs == null) {

return;

}

}

}

}

if (needsDepCheck) {

checkDependencies(beanName, mbd, filteredPds, pvs);

}

}

if (pvs != null) {

applyPropertyValues(beanName, mbd, bw, pvs);

}

}

The core logic of the whole method is PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); This line of code . That is to get the bean All attributes of , That is, we configure property Elements . Finally, execute applyPropertyValues(beanName, mbd, bw, pvs) Method .applyPropertyValues The method is implemented as follows :

/**

* Apply the given property values, resolving any runtime references

* to other beans in this bean factory. Must use deep copy, so we

* don't permanently modify this property.

* @param beanName the bean name passed for better exception information

* @param mbd the merged bean definition

* @param bw the BeanWrapper wrapping the target object

* @param pvs the new property values

*/

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {

if (pvs.isEmpty()) {

return;

}

if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {

((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());

}

MutablePropertyValues mpvs = null;

List<PropertyValue> original;

if (pvs instanceof MutablePropertyValues) {

mpvs = (MutablePropertyValues) pvs;

if (mpvs.isConverted()) {

// Shortcut: use the pre-converted values as-is.

try {

bw.setPropertyValues(mpvs);

return;

}

catch (BeansException ex) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, "Error setting property values", ex);

}

}

original = mpvs.getPropertyValueList();

}

else {

original = Arrays.asList(pvs.getPropertyValues());

}

TypeConverter converter = getCustomTypeConverter();

if (converter == null) {

converter = bw;

}

BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

// Create a deep copy, resolving any references for values.

List<PropertyValue> deepCopy = new ArrayList<>(original.size());

boolean resolveNecessary = false;

for (PropertyValue pv : original) {

if (pv.isConverted()) {

deepCopy.add(pv);

}

else {

String propertyName = pv.getName();

Object originalValue = pv.getValue();

Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

Object convertedValue = resolvedValue;

boolean convertible = bw.isWritableProperty(propertyName) &&

!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);

if (convertible) {

convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);

}

// Possibly store converted value in merged bean definition,

// in order to avoid re-conversion for every created bean instance.

if (resolvedValue == originalValue) {

if (convertible) {

pv.setConvertedValue(convertedValue);

}

deepCopy.add(pv);

}

else if (convertible && originalValue instanceof TypedStringValue &&

!((TypedStringValue) originalValue).isDynamic() &&

!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {

pv.setConvertedValue(convertedValue);

deepCopy.add(pv);

}

else {

resolveNecessary = true;

deepCopy.add(new PropertyValue(pv, convertedValue));

}

}

}

if (mpvs != null && !resolveNecessary) {

mpvs.setConverted();

}

// Set our (possibly massaged) deep copy.

try {

bw.setPropertyValues(new MutablePropertyValues(deepCopy));

}

catch (BeansException ex) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, "Error setting property values", ex);

}

}

Key code Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); The way is to get property Corresponding value . See the specific calling method BeanDefinitionValueResolver.resolveValueIfNecessary The implementation is as follows :

/**

* Given a PropertyValue, return a value, resolving any references to other

* beans in the factory if necessary. The value could be:

* <li>A BeanDefinition, which leads to the creation of a corresponding

* new bean instance. Singleton flags and names of such "inner beans"

* are always ignored: Inner beans are anonymous prototypes.

* <li>A RuntimeBeanReference, which must be resolved.

* <li>A ManagedList. This is a special collection that may contain

* RuntimeBeanReferences or Collections that will need to be resolved.

* <li>A ManagedSet. May also contain RuntimeBeanReferences or

* Collections that will need to be resolved.

* <li>A ManagedMap. In this case the value may be a RuntimeBeanReference

* or Collection that will need to be resolved.

* <li>An ordinary object or {@code null}, in which case it's left alone.

* @param argName the name of the argument that the value is defined for

* @param value the value object to resolve

* @return the resolved object

*/

@Nullable

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {

// We must check each value to see whether it requires a runtime reference

// to another bean to be resolved.

if (value instanceof RuntimeBeanReference) {

RuntimeBeanReference ref = (RuntimeBeanReference) value;

return resolveReference(argName, ref);

}

else if (value instanceof RuntimeBeanNameReference) {

String refName = ((RuntimeBeanNameReference) value).getBeanName();

refName = String.valueOf(doEvaluate(refName));

if (!this.beanFactory.containsBean(refName)) {

throw new BeanDefinitionStoreException(

"Invalid bean name '" + refName + "' in bean reference for " + argName);

}

return refName;

}

else if (value instanceof BeanDefinitionHolder) {

// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.

BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;

return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());

}

else if (value instanceof BeanDefinition) {

// Resolve plain BeanDefinition, without contained name: use dummy name.

BeanDefinition bd = (BeanDefinition) value;

String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +

ObjectUtils.getIdentityHexString(bd);

return resolveInnerBean(argName, innerBeanName, bd);

}

else if (value instanceof ManagedArray) {

// May need to resolve contained runtime references.

ManagedArray array = (ManagedArray) value;

Class<?> elementType = array.resolvedElementType;

if (elementType == null) {

String elementTypeName = array.getElementTypeName();

if (StringUtils.hasText(elementTypeName)) {

try {

elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());

array.resolvedElementType = elementType;

}

catch (Throwable ex) {

// Improve the message by showing the context.

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Error resolving array type for " + argName, ex);

}

}

else {

elementType = Object.class;

}

}

return resolveManagedArray(argName, (List<?>) value, elementType);

}

else if (value instanceof ManagedList) {

// May need to resolve contained runtime references.

return resolveManagedList(argName, (List<?>) value);

}

else if (value instanceof ManagedSet) {

// May need to resolve contained runtime references.

return resolveManagedSet(argName, (Set<?>) value);

}

else if (value instanceof ManagedMap) {

// May need to resolve contained runtime references.

return resolveManagedMap(argName, (Map<?, ?>) value);

}

else if (value instanceof ManagedProperties) {

Properties original = (Properties) value;

Properties copy = new Properties();

original.forEach((propKey, propValue) -> {

if (propKey instanceof TypedStringValue) {

propKey = evaluate((TypedStringValue) propKey);

}

if (propValue instanceof TypedStringValue) {

propValue = evaluate((TypedStringValue) propValue);

}

if (propKey == null || propValue == null) {

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Error converting Properties key/value pair for " + argName + ": resolved to null");

}

copy.put(propKey, propValue);

});

return copy;

}

else if (value instanceof TypedStringValue) {

// Convert value to target type here.

TypedStringValue typedStringValue = (TypedStringValue) value;

Object valueObject = evaluate(typedStringValue);

try {

Class<?> resolvedTargetType = resolveTargetType(typedStringValue);

if (resolvedTargetType != null) {

return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);

}

else {

return valueObject;

}

}

catch (Throwable ex) {

// Improve the message by showing the context.

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Error converting typed String value for " + argName, ex);

}

}

else if (value instanceof NullBean) {

return null;

}

else {

return evaluate(value);

}

}

The core approach :resolveReference(argName, ref); Look at the implementation of this method :

/**

* Resolve a reference to another bean in the factory.

*/

@Nullable

private Object resolveReference(Object argName, RuntimeBeanReference ref) {

try {

Object bean;

String refName = ref.getBeanName();

refName = String.valueOf(doEvaluate(refName));

if (ref.isToParent()) {

if (this.beanFactory.getParentBeanFactory() == null) {

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Can't resolve reference to bean '" + refName +

"' in parent factory: no parent factory available");

}

bean = this.beanFactory.getParentBeanFactory().getBean(refName);

}

else {

bean = this.beanFactory.getBean(refName);

this.beanFactory.registerDependentBean(refName, this.beanName);

}

if (bean instanceof NullBean) {

bean = null;

}

return bean;

}

catch (BeansException ex) {

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);

}

}

There's a familiar line of code :bean = this.beanFactory.getBean(refName), Yes , This is where recursion happens . This method takes the property name and gets the instance from the container . Through the above operation, we can find Bean Rely on the Bean.

We go back to AbstractAutowireCapableBeanFactory.applyPropertyValues Method . here deepCopy The set already has a value . And then see bw.setPropertyValues(new MutablePropertyValues(deepCopy)); This line of code , This method is called AbstractPropertyAccessor.setPropertyValues Method to complete the injection

@Override

public void setPropertyValues(PropertyValues pvs) throws BeansException {

setPropertyValues(pvs, false, false);

}

The final method called is AbstractPropertyAccessor.setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)

AbstractPropertyAccessor

@Override

public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)

throws BeansException {

List<PropertyAccessException> propertyAccessExceptions = null;

List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?

((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));

for (PropertyValue pv : propertyValues) {

try {

// This method may throw any BeansException, which won't be caught

// here, if there is a critical failure such as no matching field.

// We can attempt to deal only with less serious exceptions.

setPropertyValue(pv);

}

catch (NotWritablePropertyException ex) {

if (!ignoreUnknown) {

throw ex;

}

// Otherwise, just ignore it and continue...

}

catch (NullValueInNestedPathException ex) {

if (!ignoreInvalid) {

throw ex;

}

// Otherwise, just ignore it and continue...

}

catch (PropertyAccessException ex) {

if (propertyAccessExceptions == null) {

propertyAccessExceptions = new ArrayList<>();

}

propertyAccessExceptions.add(ex);

}

}

// If we encountered individual exceptions, throw the composite exception.

if (propertyAccessExceptions != null) {

PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);

throw new PropertyBatchUpdateException(paeArray);

}

}

This method loops through the list of elements , Cyclic invocation setPropertyValue(PropertyValue pv) Method , This method will eventually call nestedPa.setPropertyValue(tokens, pv) Method

@Override

public void setPropertyValue(PropertyValue pv) throws BeansException {

setPropertyValue(pv.getName(), pv.getValue());

}

setPropertyValue The final realization is in AbstractNestablePropertyAccessor in :

AbstractNestablePropertyAccessor

@Override

public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {

AbstractNestablePropertyAccessor nestedPa;

try {

nestedPa = getPropertyAccessorForPropertyPath(propertyName);

}

catch (NotReadablePropertyException ex) {

throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,

"Nested property in path '" + propertyName + "' does not exist", ex);

}

PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));

nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));

}

Last line of code nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value)); Take a look at the method implementation :

protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {

if (tokens.keys != null) {

processKeyedProperty(tokens, pv);

}

else {

processLocalProperty(tokens, pv);

}

}

Look at the last line of code processLocalProperty(tokens, pv); Method implementation :

private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {

PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);

if (ph == null || !ph.isWritable()) {

if (pv.isOptional()) {

if (logger.isDebugEnabled()) {

logger.debug("Ignoring optional value for property '" + tokens.actualName +

"' - property not found on bean class [" + getRootClass().getName() + "]");

}

return;

}

else {

throw createNotWritablePropertyException(tokens.canonicalName);

}

}

Object oldValue = null;

try {

Object originalValue = pv.getValue();

Object valueToApply = originalValue;

if (!Boolean.FALSE.equals(pv.conversionNecessary)) {

if (pv.isConverted()) {

valueToApply = pv.getConvertedValue();

}

else {

if (isExtractOldValueForEditor() && ph.isReadable()) {

try {

oldValue = ph.getValue();

}

catch (Exception ex) {

if (ex instanceof PrivilegedActionException) {

ex = ((PrivilegedActionException) ex).getException();

}

if (logger.isDebugEnabled()) {

logger.debug("Could not read previous value of property '" +

this.nestedPath + tokens.canonicalName + "'", ex);

}

}

}

valueToApply = convertForProperty(

tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());

}

pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);

}

ph.setValue(valueToApply);

}

catch (TypeMismatchException ex) {

throw ex;

}

catch (InvocationTargetException ex) {

PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(

getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());

if (ex.getTargetException() instanceof ClassCastException) {

throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());

}

else {

Throwable cause = ex.getTargetException();

if (cause instanceof UndeclaredThrowableException) {

// May happen e.g. with Groovy-generated methods

cause = cause.getCause();

}

throw new MethodInvocationException(propertyChangeEvent, cause);

}

}

catch (Exception ex) {

PropertyChangeEvent pce = new PropertyChangeEvent(

getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());

throw new MethodInvocationException(pce, ex);

}

}

The method finally calls ph.setValue(valueToApply) Method , That is to say BeanWrapperImpl.setValue() Method , finally , We're going to see the reflection , Seeing the reflection shows that it's over .

@Override

public void setValue(final @Nullable Object value) throws Exception {

final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?

((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :

this.pd.getWriteMethod());

if (System.getSecurityManager() != null) {

AccessController.doPrivileged((PrivilegedAction<Object>) () -> {

ReflectionUtils.makeAccessible(writeMethod);

return null;

});

try {

AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->

writeMethod.invoke(getWrappedInstance(), value), acc);

}

catch (PrivilegedActionException ex) {

throw ex.getException();

}

}

else {

ReflectionUtils.makeAccessible(writeMethod);

writeMethod.invoke(getWrappedInstance(), value);

}

}

This method is the last step , We'll find it when we see it set Method , And then call Method Of invoke Method , Complete attribute Injection .

We analyze it from the source level IOC The initialization process , I see IOC The underlying principle of , Let's summarize : Spring Of Bean In fact, the memory state is BeanDefinition, stay Bean In the process of creation and dependency injection of , Need basis BeanDefinition To complete dependency injection recursively , From the code we analyzed, we can see that , These recursions are based on getBean() For the entrance , A recursion is to find what is needed in the context system Bean And the creation of Bean Recursive call to , the other one Bean When it comes to dependency injection , Call the container's getBean Method , Get the current dependency Bean, It also triggers dependence on Bean The creation and injection of . In the face of Bean When dependency injection is performed on properties of , Parsing is also a recursive process , such , According to dependency , Layer by layer Bean The creation and injection of , Until the final completion of the current Bean The creation of , With this top floor Bean The creation of and the completion of its attribute dependency injection , Means the current Bean The injection of the whole dependency chain is completed .

To sum up IOC The initialization process :

  1. resources (Resource) location ;
  2. BeanDefinition Loading and BeanFactory Construction .
  3. Want to IOC Containers (BeanFactory) register BeanDefinition.
  4. according to lazy-init Property initialization Bean Instance and dependency injection .

This article is from WeChat official account. - The snail flying in the fallen leaves (A_GallopingSnail) , author : Running snails

The source and reprint of the original text are detailed in the text , If there is any infringement , Please contact the yunjia_community@tencent.com Delete .

Original publication time : 2018-08-24

Participation of this paper Tencent cloud media sharing plan , You are welcome to join us , share .

版权声明
本文为[Black hole code]所创,转载请带上原文链接,感谢
https://javamana.com/2021/01/20210114162621275z.html

  1. To what extent can I go out to work?
  2. Java 使用拦截器无限转发/重定向无限循环/重定向次数过多报错(StackOverflowError) 解决方案
  3. Implementation of rocketmq message sending based on JMeter
  4. How to choose the ticket grabbing app in the Spring Festival? We have measured
  5. Implementation of rocketmq message sending based on JMeter
  6. My programmer's Road: self study java
  7. My programmer's Road: self study java
  8. All in one, one article talks about the use of virtual machine VirtualBox and Linux
  9. All in one, one article talks about the use of virtual machine VirtualBox and Linux
  10. Java 使用拦截器无限转发/重定向无限循环/重定向次数过多报错(StackOverflowError) 解决方案
  11. [Java training project] Java ID number recognition system
  12. How does serverless deal with the resource supply demand of k8s in the offline scenario
  13. Detailed explanation of HBase basic principle
  14. Explain the function of thread pool and how to use it in Java
  15. Kubernetes official java client 8: fluent style
  16. 010_MySQL
  17. Vibrant special purchases for the Spring Festival tiktok section, hundreds of good things to make the year more rich flavor.
  18. 010_MySQL
  19. Of the 4 million docker images, 51% have high-risk vulnerabilities
  20. Rocketmq CPP client visual studio 2019 compilation
  21. Rocketmq CPP client visual studio 2019 compilation
  22. Usage of data custom attribute in jquery
  23. Common decompression in Linux
  24. Upload large files in Java
  25. Sentry (v20.12.1) k8s cloud native architecture exploration, sentry for JavaScript manual capture event basic usage
  26. Sentry (v20.12.1) k8s cloud native architecture exploration, sentry for JavaScript manual capture event basic usage
  27. Docker + MySQL Cluster + read / write separation + MYCAT Management + vertical sub database + load balancing
  28. Docker + MySQL Cluster + read / write separation + MYCAT Management + vertical sub database + load balancing
  29. Java use interceptor infinite forwarding / redirection infinite loop / redirection times too many error (stack overflow error) solution
  30. Java use interceptor infinite forwarding / redirection infinite loop / redirection times too many error (stack overflow error) solution
  31. 010_ MySQL
  32. 010_ MySQL
  33. Fast integration of imsdk and Huawei offline push
  34. 消息队列之RabbitMQ
  35. Rabbitmq of message queue
  36. 初学java进制转换方面补充学习
  37. Learn java base conversion supplementary learning
  38. 了解一下RPC,为何诞生RPC,和HTTP有什么不同?
  39. 了解一下RPC,为何诞生RPC,和HTTP有什么不同?
  40. 初学java进制转换方面补充学习
  41. Learn about RPC, why RPC was born, and what's the difference between RPC and HTTP?
  42. Learn about RPC, why RPC was born, and what's the difference between RPC and HTTP?
  43. Learn java base conversion supplementary learning
  44. JDBC测试连接数据库
  45. JDBC test connection database
  46. 大厂面试官竟然这么爱问Kafka,一连八个Kafka问题把我问蒙了?
  47. The interviewers of big factories love to ask Kafka so much. I'm blinded by eight Kafka questions in a row?
  48. 安卓开发和java开发有什么区别!2021年BATJ30套大厂Android经典高频面试题,面试必问
  49. Spring Security OAuth2.0認證授權四:分散式系統認證授權
  50. What's the difference between Android development and java development! 2021 batj30 Android classic high frequency interview questions
  51. Spring security oauth2.0 authentication and authorization 4: distributed system authentication and authorization
  52. Java微服务 vs Go微服务,究竟谁更强!?
  53. 大厂面试官竟然这么爱问Kafka,一连八个Kafka问题把我问蒙了?
  54. Who is stronger, Java microservice vs go microservice!?
  55. Java微服务 vs Go微服务,究竟谁更强!?
  56. The interviewers of big factories love to ask Kafka so much. I'm blinded by eight Kafka questions in a row?
  57. Who is stronger, Java microservice vs go microservice!?
  58. springboot异常处理之404
  59. Spring boot exception handling 404
  60. Spring Boot Security 国际化 多语言 i18n 趟过巨坑