面试-spring-各种bean实例化过程

导读

1、Cglib有多种实例化对象的方式,一个是通过反射拿到构造方法,创建实例,第二个是通过Cglib创建代理对象

下面通过几个我们开发中接触比较多的例子,讲述下bean的实例化过程

一、带有@Configuration注解类实例化

Spring在实例化带了@Configuration注解类,会通过Cglib来给类创建代理类,注意只是创建代理类,可以看返回值的类型是Class<?>,当生成代理类的类型,再通过反射来创建代理对象

下面是核心代码逻辑

ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
//参数configClass就是带了@Configuration注解类的类型,或者说目标类的类型,只不过这个目标类是一个配置类

上面enhancedClass就是创建出来代理类的Class,看下enhancer.enhance方法做了什么

class ConfigurationClassEnhancer {
	public Class<?> enhance(Class<?> configClass, ClassLoader classLoader) {
		Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
		return enhancedClass;
	}	
}

通过Cglib的Enhancer创建代理对象

private Enhancer newEnhancer(Class<?> superclass, ClassLoader classLoader) {
	Enhancer enhancer = new Enhancer();
	enhancer.setSuperclass(superclass);	//代理目标类,或者说被代理类
	enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});   //设置代理类接口类型
	enhancer.setUseFactory(false);
	enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
	enhancer.setCallbackFilter(CALLBACK_FILTER);
	enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());		//设置回调类型
	//enhancer.setCallbacks(...)  //设置回调对象
	return enhancer;
}

Enhancer就是Cglib最重要的类,通过继承代理类的方式创建代理对象,里面需要包含3个重要信息

1、Superclass 父类的类型,也就是目标类
2、Interfaces 接口类型,也就是EnhancedConfiguration
3、CallbackTypes 设置回调类,可以是多个,回调类都要是MethodInterceptor的实现类

private Class<?> createClass(Enhancer enhancer) {
	Class<?> subclass = enhancer.createClass();
	
	Enhancer.registerStaticCallbacks(subclass, CALLBACKS);//不理解为什么还要注册静态回调
	return subclass;
}

整个代理类的Class主要是通过Enhancer创建出来的。

二、实例化@Component、@Service

原理:知道Bean的Class,通过反射执行构造方法,然后得到实例化的对象

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
	final BeanFactory parent = this;
	//创建实例对象,注意这种是没有走代理的,下面会单独介绍@Service走动态代理方式
	Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
	
	BeanWrapper bw = new BeanWrapperImpl(beanInstance);
	initBeanWrapper(bw);
	return bw;
}
instantiate(mbd, beanName, parent) //创建实例对象,详细代码如下

上面有个getInstantiationStrategy()是用来得到CglibSubclassingInstantiationStrategy对象,怎么看着像是用Cglib代理了Service,在仔细看下去

虽然是通过CglibSubclassingInstantiationStrategy对象调用instantiate对象,但是CglibSubclassingInstantiationStrategy继承了SimpleInstantiationStrategy,而instantiate是在SimpleInstantiationStrategy里面实现的。

public class SimpleInstantiationStrategy{

	//注意里面会有判断是否是接口:注意如果这个Bd是接口是会报错的
	@Override
	public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
		//判断Db是否有重写的方法,不是判断接口是否有重写的方法,别搞错了
		if (bd.getMethodOverrides().isEmpty()) {
			Constructor<?> constructorToUse;
			Class<?> clazz = bd.getBeanClass();
			//注意这里是判断目标类是否是接口,这里有个注意的点,@Service做作用在类上不是接口上所以,
			//所以service这种Db进到这里,这里if判断是不成立
			if (clazz.isInterface()) {
				throw new BeanInstantiationException(clazz, "Specified class is an interface");
			}

			synchronized (bd.constructorArgumentLock) {
				........

				final Class<?> clazz = bd.getBeanClass();
				constructorToUse =	clazz.getDeclaredConstructor((Class[]) null);//通过反射拿到无参的构造方法
			}
			//通过构造方法调用newInstance的方法,实例化这个对象
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}
}
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
	ReflectionUtils.makeAccessible(ctor);
	return ctor.newInstance(args);//实例化对象
}

三、实例化@Bean

实例化带有@Bean方法的时候处理流程
1、因为@Bean方法是写在@Configuration注解修饰的类里面,所以在实例化@Bean的方法时,@Configuration修饰的类的代理对象已经创建好了
2、@Configuration代理类是Cglib创建的,所以他的父类就是目标类,既然知道父类的类型,就可以通过反射拿到这个类的所有方法
3、通过遍历所有方法,调用方法的invoke方法就可以执行@Bean的方法,通过返回值拿到需要注入到Spring容器的Bean

比如我们要把BaiDuCom这个Bean存入Spring容器中

@Configuration
public class MyBatisConfig {
    @Bean
    public BaiDuCom baiDuCom(){
        BaiDuCom baiDuCom = new BaiDuCom();

        return baiDuCom;
    }
}

和其他Bean创建一样,都是在执行createBean方法来创建Bean

sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
	@Override
	public Object getObject() throws BeansException {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			destroySingleton(beanName);
			throw ex;
		}
	}
});

1、调用doCreateBean创建@Bean方法名,需要创建的Bean

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

在这里插入图片描述

protected BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, Object[] explicitArgs) {
	return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}

//最终走到这里

public BeanWrapper instantiateUsingFactoryMethod(final String beanName, final RootBeanDefinition mbd, final Object[] explicitArgs) {
	
	//mbd是@Configuration标注的类,但是name是该类下面的@Bean的name

	String factoryBeanName = mbd.getFactoryBeanName();

	//通过beanName拿到FactoryBean,注意这是FactoryBean不是BeanFactory,这里还是代理类
	//factoryBean是代理类,因为是Cglib代理,所以该类的父类就是目标类
	Object factoryBean = this.beanFactory.getBean(factoryBeanName);
	

	//获取代理类的类型
	Class<?> factoryClass = factoryBean.getClass();

	//factoryClass是代理,然后通过getUserClass方法拿到该类的父类,也就是目标类
	factoryClass = ClassUtils.getUserClass(factoryClass);

	//获取目标类的所有方法
	Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);


	//遍历上面所有的方法找到目标方法,也就是加了@Bean方法的name,且该目标方法是非静态方法
	List<Method> candidateSet = new ArrayList<Method>();
	for (Method candidate : rawCandidates) {
		if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
			candidateSet.add(candidate);
		}
	}


	从上面的candidateSet集合拿到对应的目标方法
	Method factoryMethodToUse = candidateSet.get(....);通过遍历拿到具体的Method

	//通过Method执行目标方法,目标方法会返回需要注入到Spring容器的Bean
	Object beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);
}
@Override
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner,Object factoryBean, final Method factoryMethod, Object... args) {
	//拿到方法通过invoke执行方法,来得到实例对象,注入到容器中,关于Method.invoke可以参考这偏文章
	return factoryMethod.invoke(factoryBean, args);
}

factoryMethod.invoke也就是执行上面baiDuCom()方法来返回实例对象到容器中

疑问:当我们通过如下方式去执行@Bean方法,Spring是如何保障前后拿到都是同一个Bean

//假设我们通过下面代码注入Bean,然后通过myBatisConfig执行带有@Bean的方法
@Autowired
private MyBatisConfig myBatisConfig;
//com.xiaour.spring.boot.config.MyBatisConfig$$EnhancerBySpringCGLIB$$e5cd6e19 
//因为@Configuration走的是Cglib代理,所以这个实例对象就是Cglib创建的

而且在创建Cglib代理的时候,我们是会设置回调类的,当执行代理类的方法时,是会走下面类的回调方法

new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor()
NoOp.INSTANCE

下面是某一个回调方法

//执行@Bean方法时,会走到设置的回调方法里面,因为启动的时候,Bean已经被加入到Spring的单利缓存里面去了,也就是一级缓存,所以每次在使用的时候也就通过单利缓存去取
private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
	@Override
	public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,MethodProxy cglibMethodProxy) throws Throwable {
		return obtainBeanInstanceFromFactory(beanMethod, beanMethodArgs, beanFactory, beanName);
	}	
}
private Object obtainBeanInstanceFromFactory(Method beanMethod, Object[] beanMethodArgs,ConfigurableBeanFactory beanFactory, String beanName) {
	Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName));
	return beanInstance
}
//从一级缓存拿到单利的Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

注意:就算是@Configuration内部去通过this的方式调用内部方法,也是会走到回调方法的,这就是Cglib和反射所带来的区别

四、@Mapper实例化

Mapper在Spring的是以MapperFactoryBean的类型存在

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信仰_273993243

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值