SPRING07_自动装配如何加强、@Autowired注解debug分析、总结

news2024/9/23 11:12:52

文章目录

  • ①. Spring启动一行代码:
  • ②. ApplicationContex增强功能
  • ③. 自动装配如何装配进来
  • ④. @Autowired自动注入细节xml版
  • ⑤. @Autowired注解版分析
  • ⑥. 总结一下

①. Spring启动一行代码:

  • ①. 创建一个IOC容器,传入容器的xml配置文件,Spring在整个创建容器的过程中全部都准备好了,接下来就可以拿来用了
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

在这里插入图片描述

  • ②.按照上面的Spring的架构原理图,ResourceReader先来加载Xml配置资源文件,加载来以后交给这些BeanDefinitionReaderBean的读取器来进行读取,这些BeanDefinitionReaderBean的读取器最终再交给BeanDefinitionParserDelegate解析器,来解析Xml配置资源文件中的每一个标签,如果Xml配置资源文件的标签里面是bean标签,说明是要生成一个组件,会为这个bean标签解析成一个BeanDefinition,这个BeanDefinitionbean定义信息就是造飞机的图纸,图纸最终进入档案馆BeanDefinitionRegistry,BeanDefinitionRegistry是一个接口,而真正的档案馆实现叫DefaultListableBeanFactory,DefaultListableBeanFactory档案馆里面最核心的关键点就在于beanDefinitionMap
//所有bean定义信息的集合,按照名字以及对应BeanDefinition关系都保存好了
// 这也解释了spring架构原理里面,spring解析来的所有资源都会存在这个档案馆,这个档案馆就是一个beanDefinitionMap
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
  • ③. 接下来就会看到源码里面会有一个for循环,从方案管里面获取bean定义的名字,挨个进行遍历,来进行获取和创建,这就是创建bean的过程
// 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) {
				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);
		}
	}
}
  • ④. 整个bean又存储在DefaultSingletonBeanRegistry默认的单实例bean的注册中心,DefaultListableBeanFactory的最终父类还是DefaultSingletonBeanRegistry默认的单实例bean的注册中心,这也是默认的单实例bean中心
    所有的单实例Bean都会在DefaultSingletonBeanRegistry这个里面
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
  • ⑤. DefaultSingletonBeanRegistry的具体哪个里面呢
  1. 整个Spring的设计都是模板方法模式,父类里面定义了很多方法属性,子类不断的去进行继承,子类来进行功能的增强。在DefaultSingletonBeanRegistry里面其中还有一个关键环节:singletonObjects
//缓存所有单实例bean对象,单例对象池,这也就是ioc容器-单例池
//Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  1. 每一个对象,都有一个key(String),按照这个key(String)能够查到这个value(object)对象,这个对象就是单例对象池SingletonObjects.这个单例池(SingletonObjects)最终相当于按照图纸要造飞机一个真实的对象,造的飞机就在这个DefaultSingletonBeanRegistry,DefaultSingletonBeanRegistry是飞机的仓库,而DefaultSingletonBeanRegistry这个飞机仓库是DefaultListableBeanFactory这个的一个父类,Spring的底层只会用到DefaultListableBeanFactory这一个类,但这个DefaultListableBeanFactory一个类既是bean定义信息图纸的存储中心,又是DefaultSingletonBeanRegistry飞机的存储中心
  2. 在Spring底层,可以把单例对象池SingletonObjects称为容器,单例对象池SingletonObjects这里面是最常用的,所有的单实例都在单例对象池SingletonObjects这里面放着,所以这就是一个IOC容器
  • ⑥. IOC容器其实在底层就是一个map - singletonObjects,把这些一大堆的组件保存起来,以后若要用,就直接从这个map - singletonObjects进行获取即可,这种设计模式叫享元模式(属于结构型模式)
    享元模式:就是有一些东西后来要经常用,就可以把它池化,放在一个池里面,以后要用到的时候,直接返回它的元对象,享元就是返回元对象,所以这个享元模式就是一个很典型的模式,Spring最终把这些单例的东西放在这个SingletonObjects里面,以后谁要用就返回给谁,这也叫享元模式

  • ⑦. 面试问,IOC容器是个啥?就说是一个享元模式实现的一个单例对象池singletonObjects,原来IOC容器的这个底层最应该关注的其实就是DefaultListableBeanFactory,这个DefaultListableBeanFactory有bean的定义信息,包括有整个Bean的真正实例信息等存在这里

②. ApplicationContex增强功能

  • ①. 一个组件实现了ApplicationContextAware,就只需要准备一个ApplicationContext的Set方法。然后就会利用回调把ioc容器传入过来,这一块就相当于对这个组件的功能增强
/**
 * Aware接口:装配spring底层的一些组件也就是bean工厂里面的东西
 */
@Component
public class Person implements ApplicationContextAware {

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		//利用回调机制,把ioc容器传进来
		this.context = applicationContext;
	}
}
  • ②. 一个Person为什么能把ApplicationContex这个ioc容器当为自己的参数传入进来?发现整个探索原理的过程中,Aware是什么时候创建过来的

  • ③. 把档案馆里面每一个bean的名字获取到,然后每一个bean挨个创建

  • ④. 创建的过程中就开始创建相应的对象,在整个相应对象创建完,初始化完了以后,就会执行一个东西叫BeanPostProcessor后置处理器这个东西也叫ApplicationContextAwareProcessor,ApplicationContextAwareProcessor实现了这个BeanPostProcessor后置处理器。

class ApplicationContextAwareProcessor implements BeanPostProcessor {
  • ⑤. 初始化完以后,BeanPostProcessor后置处理器就会有一个执行所有这个处理器的回调,这个处理器执行回调的时候,就会判断当前正在初始化的这个对象,是不是ApplicationContextAware接口

  • ⑥. 如果是这个ApplicationContextAware接口,它就把这个相应的对象,转成这个ApplicationContextAware接口,利用多态调用

  • ⑦. 这个相应的对象转成ApplicationContextAware接口以后,就会调用ApplicationContextAware接口的setApplicationContextApplicationContext applicationContext方法,把ioc容器传进去,提供使用就行了,所以这就相当于自动注入了一个。不过这个对象是通过实现接口的方式进行自动注入的

  • ⑧. 自动注入的这个东西的功能是由BeanPostProcessor后置处理器来进行完成的

③. 自动装配如何装配进来

  • ①. 在Person里面把Car进行自动装配过来,在setCar方法的上面,标注一个@Autowired,相当于setCar方法就会被调用
/**
 * Aware接口:装配spring底层的一些组件也就是bean工厂里面的东西
 */
@Component
public class Person implements ApplicationContextAware,MessageSourceAware {

//	@Autowired
	ApplicationContext context ;  //可以要到ioc容器

	public ApplicationContext getContext() {
		return context;
	}

	public Person() {
		System.out.println("person创建....");
	}

	private String name;

	private Cat cat;

	@Autowired
	public void setCat(Cat cat) {
		this.cat = cat;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	//去容器中找,但是容器中的这个组件,是一个多实例的,所以一旦去了容器中找,
	// 容器一看这个组件是多实例的,容器就应该给cat再创建一个实例
	//@Lookup一定要放在get的方法上,因为@Bean的方式注册到容器中,那么@Lookup这个注解就不能用的
	@Lookup
	public Cat getCat() {
		return cat;
	}
	
	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\'' +
				'}';
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		//利用回调机制,把ioc容器传进来
		this.context = applicationContext;
	}
}
  • ②. 在如下的三个位置打断点:Person默认的无参构造、setCat方法里面、setApplicationContext方法里面
    在这里插入图片描述
  • ③. 可以看到,来到了这个Person对象的创建的构造器
public Person() {
		System.out.println("person创建....");
	}
  • ④. 放行后,到了setApplicationContext这个方法。这个方法是因为Person实现了ApplicationContextAware接口执行的,而它整个的执行逻辑就是这样:
  1. 想要创建ioc容器,ioc容器就得调用它自己的三参数的构造方法,然后在关键一步就得刷新它整个容器(refresh) new ClassPathXmlApplicationContext->refresh()
  2. refresh刷新容器的整个过程其实就是挨个进行创建组件,完成初始化,工厂的初始化就是把里面的所有东西都创建好 – finishBeanFactoryInitialization
  3. 创建所有非懒加载的单实例bean—preInstantiateSingletons
//(1). 想要创建ioc容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("xxx.xml");

//(2). ioc的三参构造方法
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

(3). 刷新工厂
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
		throws BeansException {

	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		// 刷新工厂
		refresh();
	}
}

(4). 完成Bean工厂初始化
refresh方法里面:
	// Instantiate all remaining (non-lazy-init) singletons.
	// 完成bean工厂的初始化,所以对象创建是在bean工厂初始化完成的时候干的事
	finishBeanFactoryInitialization(beanFactory);

(5). 初始化所有的非懒加载的单实例bean
finishBeanFactoryInitialization方法里面:
	// Instantiate all remaining (non-lazy-init) singletons.
	//初始化所有的非懒加载的单实例bean
	beanFactory.preInstantiateSingletons();
  1. 创建的过程就是如果你是工厂bean则执行下边逻辑isFactoryBean下面的逻辑,否则就执行getBean进行普通的单实例非懒加载bean的创建。下面为具体的细节:
preInstantiateSingletons下面的代码

// 从档案馆里面获取到所有的bean名称
List<String> beanNames = new ArrayList<String>(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()) {
			// 如果是FactoryBean,就执行下边逻辑
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceof FactoryBean) {
					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 {
				// 若不是FactoryBean,就执行下面这段代码,普通的单实例非懒加载bean的创建
				// Spring的底层bean第一次没有初始化,也是调用getBean这个方法来对bean进行初始化的
				getBean(beanName);
			}
		}
	}
        
//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------

// getBean()相当于当前要初始化一个普通bean,那初始化这个普通bean就会调用doGetBean来执行这个getBean()方法,这就是一个典型的模板模式
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}
	
// doGetBean里面有一个关键的环节叫创建bean的实例
protected <T> T doGetBean(
	final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
	throws BeansException {
		.....
		
		// 创建Bean实例 - Create bean instance.
		if (mbd.isSingleton()) {
			// bean实例它就会调用getSingleton方法
			// getSingleton方法获取单例最核心的一点就是给这个方法里面传当前bean的名字,还传了一个lamdb表达式,而这个getSingleton方法接收的Lambda表达式叫ObjectFactory对象工厂
			// 这个lamdb表达式相当于定义了一个函数式的接口,相当于Lambda表达式里面的内容就是getObject方法的方法体
			sharedInstance = getSingleton(beanName,  () -> {
					try {
						return createBean(beanName, mbd, args);  //创建bean对象的实例
					}
			});
		}

                        
@FunctionalInterface
public interface ObjectFactory<T> {

	/**
	 * Return an instance (possibly shared or independent)
	 * of the object managed by this factory.
	 * @return the resulting instance
	 * @throws BeansException in case of creation errors
	 */
	T getObject() throws BeansException;

}
 
                  
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        ........
		//这里会调用lamda表达式的内容
		singletonObject = singletonFactory.getObject();
}
  1. 代码核心部分解释
    5.1. createBean的官方注释也说明了,它是提供一个AbstractBeanFactory的实现,也就是这是一个抽象工厂的模板方法,所以这个模板方法模式在Spring底层也用的非常多
    5.2. 它去调用真正的创建beancreateBean的方法,创建beancreateBean的方法是利用doCreateBean方法进行创建的,把bean的名字和bean的定义信息还把一些要用参数传了过来,但此时还没有bean的实例的
    5.3. 整个bean的创建就有一个叫createBeanInstance的方法,这里它想要创建bean实例对象的时候就继续往下走,进入了createBeanInstance这个方法里面
    5.4. 前面进行一大堆判断以后,主要就是为了拿到一个构造器ctors,然后前面若已经有构造器了,它可以用构造器进行自动注入autowireConstructor
    5.5. 若没有这个构造器自动注入autowireConstructor就默认使用无参构造器instantiateBean:初始化bean为当前组件创建对象
AbstractAutowireCapableBeanFactory类下的

//---------------------------------------------------------------------
// Implementation of relevant AbstractBeanFactory template methods
//---------------------------------------------------------------------
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
	......
	try {
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		if (logger.isTraceEnabled()) {
			logger.trace("Finished creating instance of bean '" + beanName + "'");
		}
		return beanInstance;
	}
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {
				......
		if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);//创建bean的实例
	}
}

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	.....
	// Preferred constructors for default construction?
	ctors = mbd.getPreferredConstructors();
	if (ctors != null) {
		return autowireConstructor(beanName, mbd, ctors, null);
	}
	// 默认使用无参构造器为当前组件创建对象 No special handling: simply use no-arg constructor.
	return instantiateBean(beanName, mbd);
  1. 代码核心部分解释
    6.1. 拿到初始化getInstantiationStrategy策略,就调用初始化instantiate方法
    6.2. 初始化instantiate方法,就利用BeanUtils工具使用初始化策略来开始初始化,所以看Spring源码的底层,可以发现非常多的一些工具类。利用工具类将其进行赋值处理
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
	try {
		Object beanInstance;
		final BeanFactory parent = this;
		if (System.getSecurityManager() != null) {
			beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
				public Object run() {
					// 这个初始化策略可以传入很多种初始化策略,一种是SimplelnstantiationStrategy使用JDK反射策略,另外一种是CglibSubclassingInstantiationStrategy使用Cglib创建一个代理对象策略
					// 拿到初始化getInstantiationStrategy策略,就调用初始化instantiate方法
					return 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);
	}
}

public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
	....
	
	return BeanUtils.instantiateClass(constructorToUse);
	}
	else {
		// Must generate CGLIB subclass.
		return instantiateWithMethodInjection(beanDefinition, beanName, owner);
	}
}
  • ⑦. 经过上面① - ⑥最终相应的对象就创建好了,这就是对象创建,这是利用了初始化策略创建的bean实例。
    相应的对象创建完了以后,那些组件是什么时候进来的?比如setApplicationContext是什么时候进来的?
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
	//利用回调机制,把ioc容器传进来
	this.context = applicationContext;
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		try {
			// 把当前bean里面的属性等该复制的复制,这个Bean的移民也就是牵扯到自动装配环节
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				// 初始化bean
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}
  • ⑦. 自动装配环节就是在populateBean这个方法里面,就是属性的赋值,Spring在什么时候搞定的
    所有的bean都要进行以上这么一个大过程-每一个bean无非它要获取这个组件,获取的时候没有的时候就创建,创建完了对象有了接下来就属性赋值,属性赋值完了,就进行各种增强的环节

  • ⑧. 继续来分析:什么时候发生了给相应属性进行赋值?

  1. 对象创建完以后,自然而然要属性赋值的操作,属性赋值就在populateBean这个方法里面
  2. populateBean这个方法前面封装了一个PropertyValues,就是从bean的定义信息里面拿到所有的属性键值对,而且这个PropertyValues还是个迭代器模式
  3. 相当于把当前bean里面有多少个属性,可以利用迭代器进行迭代出来,所以Spring里面又有一个迭代器模式叫PropertyValues,而每一个PropertyValues指的是属性的键值对,键就是属性名,值就是属性值
  4. 把深拷贝deepCopy这个里面的name,value信息拿来给bw,bw就相当于封装好了所有反射逻辑,所以至此这个bw方法一调用,那么底层就是各种反射进行设置值,把pvs这个相当于要赋值的这个玩意要给javabean里面设置过去,如果很多属性的话就是遍历赋值,所以这就是属性赋值过程
// (1). Initialize the bean instance.
Object exposedObject = bean;
try {
	// 给创建好的对象每个属性进行赋值
	populateBean(beanName, mbd, instanceWrapper);
	// 初始化bean
	exposedObject = initializeBean(beanName, exposedObject, mbd);
}

(2). populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	....
	// PropertyValues指的是一个属性的键值对,键是属性名,value是属性值
	PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
	....
	if (pvs != null) {
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
}

(3). applyPropertyValues
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
	....
	// 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);
	}

(4). 
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
		throws BeansException {
		for (PropertyValue pv : propertyValues) {
			setPropertyValue(pv);
		}

在这里插入图片描述

  • ⑨. 属性赋值了以后接下来还有一个叫初始化beaninitializeBean
(1). populateBean
try {
	// 给创建好的对象每个属性进行赋值
	populateBean(beanName, mbd, instanceWrapper);
	// 初始化bean
	exposedObject = initializeBean(beanName, exposedObject, mbd);
}

(2). applyBeanPostProcessorsBeforeInitialization
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}
}

(3). postProcessBeforeInitialization
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
		throws BeansException {

	Object result = existingBean;
	for (BeanPostProcessor processor : getBeanPostProcessors()) {
		// 执行后置处理器逻辑
		Object current = processor.postProcessBeforeInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}

(4).invokeAwareInterfaces
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
			bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
			bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
			bean instanceof ApplicationStartupAware)) {
		return bean;
	}

	AccessControlContext acc = null;

	if (System.getSecurityManager() != null) {
		acc = this.applicationContext.getBeanFactory().getAccessControlContext();
	}

	if (acc != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareInterfaces(bean);
			return null;
		}, acc);
	}
	else {
		// 执行对应的处理器逻辑
		invokeAwareInterfaces(bean);
	}

	return bean;
}

(5). 执行对应的处理器逻辑
private void invokeAwareInterfaces(Object bean) {
	if (bean instanceof EnvironmentAware) {
		((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
	}
	if (bean instanceof EmbeddedValueResolverAware) {
		((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
	}
	if (bean instanceof ResourceLoaderAware) {
		((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
	}
	if (bean instanceof ApplicationEventPublisherAware) {
		((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
	}
	if (bean instanceof MessageSourceAware) {
		((MessageSourceAware) bean).setMessageSource(this.applicationContext);
	}
	if (bean instanceof ApplicationStartupAware) {
		((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
	}
	if (bean instanceof ApplicationContextAware) {
		((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
	}
}

④. @Autowired自动注入细节xml版

理解过程即可

  • ①. 坏境搭建、断点打在setCat方法上面
	<bean class="com.xiaozhi.bean.Cat" id="cat">
		<property name="name" value="TOMCAT"></property>
	</bean>

在这里插入图片描述

  • ②. 依旧还是那一套原理:还是创建IOC容器,IOC容器创建就调用三参数的构造器,然后要开始刷新工厂,要把所有里面的单实例组件都准备好,创建所有的非懒加载的单实例bean,然后调用getBean(beanName);,也就是每一个组件在容器底层都是调用getBean出来的,然后getBean里面继续doGetBean,然后继续调用lamda表达式的方法,然后前面已经创建好bean对象了…
    在这里插入图片描述

  • ③. 这里通过debug可以看到对应的属性值

在这里插入图片描述

  • ④. 然后在这个环节就挨个遍历,给每一个属性进行赋值。

在这里插入图片描述

  • ⑤, 当前cat要进行赋值,不过这个cat的值,已经拿到了,因为自动装配,它从容器中把这个值找到就行了。
    调用writeMethod.invoke方法属性进行构造,最终还是利用反射调用setCat方法进行赋值
    在这里插入图片描述

⑤. @Autowired注解版分析

  • ①. 断点打在注解的方法里面
    在这里插入图片描述

  • ②. 原来的那一套流程,IOC创建,初始化所有单实例,getBean等一系列操作

  • ③. 来到populateBean(beanName, mbd, instanceWrapper);给创建好的对象属性每个进行赋值
    在这里插入图片描述

  • ④. getBeanPostProcessorCache,相当于把所有的后置处理器的缓存拿来,相当于拿一些后置处理器。其中正在用的这个后置处理器叫AutowiredAnnotationBeanPostProcessor(自动装配注解的bean后置处理器)
    如果是自动装配功能,还会使用一个后置处理器AutowiredAnnotationBeanPostProcessor
    AutowiredAnnotationBeanPostProcessor后置处理器只不过接口叫InstantiationAwareBeanPostProcessor。
    在这里插入图片描述

  • ⑤. AutowiredAnnotationBeanPostProcessor这个后置处理器有了这个对象了,那么这个后置处理器拦截这个对象里面的加了注解的属性,给这些属性进行赋值,所以这也就是增强的拦截功能,在Spring的底层会发现非常多的这些后置处理器,在各个阶段来进行功能的增强


	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		// 找到自动装配的元信息
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}
  • ⑥. 找哪些属性或者方法上面使用了自动注入的注解
    找所有属性的@Autowired和@Value,@Inject注解的
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
	// Fall back to class name as cache key, for backwards compatibility with custom callers.
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// Quick check on the concurrent map first, with minimal locking.
	InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
	if (InjectionMetadata.needsRefresh(metadata, clazz)) {
		synchronized (this.injectionMetadataCache) {
			metadata = this.injectionMetadataCache.get(cacheKey);
			if (InjectionMetadata.needsRefresh(metadata, clazz)) {
				if (metadata != null) {
					metadata.clear(pvs);
				}
				// 下面是分析当前类方法或者属性有没有标注@Autowired等自动赋值的注解
				metadata = buildAutowiringMetadata(clazz);
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	return metadata;
}
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
	if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
		return InjectionMetadata.EMPTY;
	}

	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	Class<?> targetClass = clazz;

	do {
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
		// (1). 找所有属性中标注了Autowired注解
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			MergedAnnotation<?> ann = findAutowiredAnnotation(field);
			if (ann != null) {
				if (Modifier.isStatic(field.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static fields: " + field);
					}
					return;
				}
				boolean required = determineRequiredStatus(ann);
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});

		// (2). 拿到所有的方法,看看有没有@Autowired注解
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
			if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
				return;
			}
			MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
			if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
				if (Modifier.isStatic(method.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static methods: " + method);
					}
					return;
				}
				if (method.getParameterCount() == 0) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation should only be used on methods with parameters: " +
								method);
					}
				}
				boolean required = determineRequiredStatus(ann);
				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
				currElements.add(new AutowiredMethodElement(method, required, pd));
			}
		});

		// 所有准备好的元素返回给我们
		elements.addAll(0, currElements);
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return InjectionMetadata.forElements(elements, clazz);
}
@Nullable
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
	MergedAnnotations annotations = MergedAnnotations.from(ao);
	for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
		MergedAnnotation<?> annotation = annotations.get(type);
		if (annotation.isPresent()) {
			return annotation;
		}
	}
	return null;
}
public AutowiredAnnotationBeanPostProcessor() {
	this.autowiredAnnotationTypes.add(Autowired.class);
	this.autowiredAnnotationTypes.add(Value.class);
	try {
		this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
				ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
		logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}
  • ⑦. 就是说它先找属性,再找方法看有没有标注@Autowired注解的。只要找到了就加入到元素里面,最终所有都找到以后,把所有的这些元素elements进行返回,一返回以后,就相当于要@Autowired自动装配的东西
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Collection<InjectedElement> checkedElements = this.checkedElements;
	Collection<InjectedElement> elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
		for (InjectedElement element : elementsToIterate) {
			element.inject(target, beanName, pvs);
		}
	}
}
  • ⑧. 最后利用反射赋值就来到了setCat方法
    在这里插入图片描述

⑥. 总结一下

Spring的底层通过后置增强机制来完成很多功能,所有后来的核心功能,比如切面,比如事务,比如代理,日志等一大堆,一个注解就能够搞定的事情,这到底它是怎么完成的?所以要分析Spring的一个功能的时候,主要分析两点就行了:
1. 这个功能Spring在启动的时候注入了什么组件?比如想做事务相关,Spring在启动就会准备一个事务的TransactionManager事务管理器
2. 这个功能牵扯到的组件在什么位置被什么后置增强器增强成了什么样子

这是复习回顾顺便研究了一下BeanPostProcessor后置处理器整个强大之处,BeanPostProcessor翻译过来就是bean的后置处理器,它就是在一个bean的初始化前后进行的功能增强的作用,但是这个BeanPostProcessor处理器有很多的子接口,所有这些个后置处理器何时进行了增强,就是Spring源码之后置处理器的接口关系和Spring源码之工厂的后置处理&配置类处理以及Spring源码之bean后置处理器的创建过程研究的重点,如果把这些研究好了,那么在后来分析Spring的任何功能也就非常简单了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2034105.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker安装达梦数据库详细教程

达梦数据库(DM,Dameng Database)是中国自主研发的关系型数据库管理系统。它由武汉达梦数据库有限公司开发,最早可以追溯到1982年,至今已有几十年的发展历史。达梦数据库在中国市场上具有较高的知名度和市场占有率,特别是在政府、金融、电信、能源等行业有广泛的应用。 自…

深度学习——神经网络(neural network)详解(一). 带手算步骤,步骤清晰0基础可看

深度学习——神经网络&#xff08;neural network&#xff09;详解&#xff08;一&#xff09;. 带手算步骤&#xff0c;步骤清晰0基础可看 我将以最简单&#xff0c;基础的形式说明神经网络的训练过程。 搭配以下文章进行学习&#xff1a; 深度学习——卷积神经网络&#xf…

Day18 Linux系统编程学习--文件

文件 (file) 是程序设计中一个重要的概念。所谓“文件”一般指存储在外部介质上数据的集合。C语言把文件看作是一个字符&#xff08;字节&#xff09;的序列&#xff0c;即由一个一个字符&#xff08;字节&#xff09;的数据顺序组成。根据数据的组织形式&#xff0c;可分为 AS…

【森气随笔】python绘图找不同,揭秘不同函数绘图差异。

【森气随笔】python绘图找不同&#xff0c;揭秘不同函数绘图差异。 准备了两组图片&#xff0c;运用了不同绘图函数绘制。然而&#xff0c;令人无语的是&#xff0c;有人竟直言不讳地表示难以察觉其中的差别。非常好奇&#xff0c;是差异太小还是不愿意承认呢&#xff1f;感兴趣…

Linux-服务器硬件及RAID配置实验

系列文章目录 提示&#xff1a;仅用于个人学习&#xff0c;进行查漏补缺使用。 1.Linux介绍、目录结构、文件基本属性、Shell 2.Linux常用命令 3.Linux文件管理 4.Linux 命令安装(rpm、install) 5.Linux账号管理 6.Linux文件/目录权限管理 7.Linux磁盘管理/文件系统 8.Linu…

利用shell脚本一键查询ceph中bucket桶的占用大小

在 Ceph 对象存储中&#xff08;例如使用 RADOS Gateway 提供的 Swift 或 S3 接口&#xff09;&#xff0c;你可能需要了解某个桶&#xff08;bucket&#xff09;的占用大小。 以下是如何在 Ceph 中查看桶的占用大小的方法&#xff1a; 1. 使用 radosgw-admin 工具 radosgw-a…

2024最新整理Python基础知识点汇总(可下载)期末复习必备!

前言 由于篇幅限制&#xff0c;我把所有的Python基础知识点和实战代码全部打包上传至CSDN官方认证的微信上&#xff0c;需要的同学可以自取&#xff01;下载保存在你自己的电脑上&#xff08;保证100%免费&#xff09; 1 变量和简单数据类型 变量命名格式&#xff1a;变量名 …

Linux-Shell管道命令及脚本调试-06

上一章我们讲了一半的管道命令,今天把剩下的讲完 1、管道命令 字符转换命令 tr, col, join, paste, expand 1.1 tr 一种可将字符进行替换、压缩、删除&#xff0c;可以将一组字符转换成另一组字符 格式; tr [-parameter] [string1] [string2] 参数&#xff1a; 参数说…

vs2019 QtConcurrent多线程使用方法

QtConcurrent::run(xxx) 1.打开QT Project Setting-》点击Qt Modules 2.头文件包含&#xff1a; #include <QtConcurrent/QtConcurrent> 3.使用方法&#xff1a; QFuture<void> future1 QtConcurrent::run(this, &auto_pack_line_demo::UpdateVisionComm)…

漏洞复现-Apache Kafka Clients JNDI注入漏洞 (CVE-2023-25194)

1.漏洞描述 Apache Kafka 是一个开源分布式事件流平台&#xff0c;被数千家公司用于高性能数据管道、流分析、数据集成和任务关键型应用程序。 在版本3.3.2及以前&#xff0c;Apache Kafka clients中存在一处JNDI注入漏洞。如果攻击者在连接的时候可以控制属性sasl.jaas.conf…

今是科技携手福瑞莱,共筑环境微生物检测技术创新与发展

近日&#xff0c;成都今是科技有限公司&#xff08;以下简称“今是科技”&#xff09;与福瑞莱环保科技&#xff08;深圳&#xff09;股份有限公司&#xff08;以下简称“福瑞莱”&#xff09;正式宣布达成深度战略合作。此次合作旨在将双方的优势资源与技术力量相结合&#xf…

再见,Midjourney | FLUX 彻底改变了 AI 图像游戏

Flux 刚发布一周&#xff0c;大家都疯了&#xff01; 因为真的是分不清是AI还是真实啊&#x1f3f4;‍☠️ Flux生成 Flux生成 FLUX 彻底改变了 AI 图像游戏。 02 黑深林 Black Forest Labs由Stable Diffusion模型的原班人马创立&#xff0c;旨在开发并开源高质量的图像和…

无字母数字_$ webshell之命令执行

题解分析&#xff1a; 代码案例 当然&#xff0c;这道题的限制&#xff1a; webshell长度不超过35位 不包含字母数字&#xff0c;还不能包含$和_ 所以&#xff0c;如何解决这个问题&#xff1f; shell下可以利用.来执行任意脚本 Linux文件名支持用glob通配符代替 第一点.…

Java语言程序设计基础篇_编程练习题**16.19(控制一组风扇)

**16.19&#xff08;控制一组风扇&#xff09; 编写一个程序&#xff0c;在一组中显示三个风扇&#xff0c;有控制按钮来启动和停止整组风扇&#xff0c;如图16-44所示。 习题分析 要完成这道题目&#xff0c;需要将16.18中的代码变成一个自定义面板(继承自BorderPane)&#…

考研概率论如何复习最高效?能拿满分

概率论跟哪写老师的课程&#xff1f; 推荐三个老师&#xff1a; 喻老&#xff1a;基础讲的很好 喻老的线性代数课在今年已经非常有名&#xff0c;但其实他讲授的概率论课程同样十分出色。喻老的课程特点在于讲解非常细致&#xff0c;特别适合基础较为薄弱的学生。此外&#…

MySQL练手题——case when ... then ...

一、准备工作 Create table If Not Exists Seat (id int, student varchar(255)); Truncate table Seat; insert into Seat (id, student) values (1, Abbot); insert into Seat (id, student) values (2, Doris); insert into Seat (id, student) values (3, Emerson); inser…

spark3.3.4 上使用 pyspark 跑 python 任务版本不一致问题解决

问题描述 在 spark 上跑 python 任务最常见的异常就是下面的版本不一致问题了&#xff1a; RuntimeError: Python in worker has different version 3.7 than that in driver 3.6, PySpark cannot run with different minor versions. Please check environment variables PY…

PLM软件选型攻略:10款推荐工具全面解析

本篇文章中提到的工具包括&#xff1a;PingCode、Worktile、云效、目标圈&#xff08;Goal Circle&#xff09;、Mavenlink、SAP PLM、Basecamp、Scoro、明道云、Airtable。 在现代企业管理中&#xff0c;选择合适的PLM&#xff08;产品生命周期管理&#xff09;系统对提升产品…

pytorch下载慢,如何下载到本地再去安装,本地安装pytorch

有时候按部就班的用指令去安装pytorch&#xff0c;网上很慢&#xff0c;并且往往最后可能还没有安装成功。 本次&#xff0c;介绍一下如何将这个文件先下载到本地&#xff0c;然后在去安装。 至于如何安装pytorch&#xff0c;先看一下我之前写的 深度学习环境-------pytorch…

计算机毕业设计 家电销售展示平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…