package com.yueny.fw.practice.spring;
import com.yueny.fw.practice.lock.Deadlock;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author yueny09 <deep_blue_yang@126.com>
* @Date 2019-10-21 14:10
*/
public class ApplicationMain {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext( "classpath:/config/spring-bean.xml");
Deadlock deadlock = context.getBean("deadlock", Deadlock.class);
System.out.println(deadlock.toString());
}
}
复制代码
见《Spring获取Bean的流程(二)》ClassPathXmlApplicationContext 加载器的时序图
ClassPathXmlApplicationContext 的构造器中调用类同名方法:
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
复制代码
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
// super()方法是一直到父类AbstractXmlApplicationContext --> AbstractRefreshableConfigApplicationContext --> AbstractRefreshableApplicationContext --> AbstractApplicationContext 中,将ApplicationContext的环境属性设置给本类的环境属性,包括一些profile,系统属性等.
// 执行了 getEnvironment().merge 合并操作
super(parent);
setConfigLocations(configLocations); // 调用父类方法,将xml配置文件地址名字给父类的String数组属性
if (refresh) {
refresh(); // 进行初始化,所有的逻辑其实都在这个方法里面进行
}
}
复制代码
ClassPathXmlApplicationContext中的refresh方法,实际上调用的是父类org.springframework.context.support.AbstractApplicationContext的方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 在刷新之前设置一些参数,比如设置开始时间戳,上下文是否激活的标志,输出刷新上下文的信息,验证一些必要的属性, 见 3.1
// Prepare this context for refreshing.
prepareRefresh();
// 这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
// 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
// 详情见3.2及3.2.*
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean,详情见 3.4
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
// prepareBeanFactory()方法调用之后,是一个try-catch代码块,如果有BeanException异常产生则会停止refresh并且销毁已创建的资源
try {
// 获取容器级别的后处理器,允许上下文的子类中对beanFactory进行后处理,在应用上下文内部beanFactory初始化之后可以修改beanFactory,此时所有的BeanDefinittions都已经被加载,但未被实例化,具体的实现在AbstractRefreshableWebApplicationContext
// 默认方法体是空的,主要是用来扩展beanfactory的,扩展点是在bean等配置都已经加载但还没有进行实例化的时候。
// 到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
postProcessBeanFactory(beanFactory);
// 详情见 3.5
// 调用BeanFactoryPostProcessor和BeanDefintionRegistoryPostProcessor这两个后置处理器
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// 详情见 3.5
// 注册BeanPostProcessor 后置处理器,用来拦截bean的创建,详情见 xxx
// 注意,到这里 Bean 还没初始化
registerBeanPostProcessors(beanFactory);
// 详情见 3.6
// 初始化消息源
// Initialize message source for this context.
initMessageSource();
// 初始化应用程序事件广播器,用户可以自定义一个事件广播器,如果用户没有定义,那么使用默认的事件广播器SimpleApplicationEventMulticaster
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 空方法由子类扩展,可以在实例化bean之前做一些ApplicationContext相关的操作
// Initialize other special beans in specific context subclasses.
onRefresh();
// 注册和检测事件监听器
// Check for listener beans and register them.
registerListeners();
//单例模式的bean实例化,初始化(non-lazy-init),即所有单例非懒加载Bean的调用点
// 重点,重点,重点
// 初始化所有的 singleton beans (lazy-init 的除外)
// 详见 3.7
finishBeanFactoryInitialization(beanFactory);
// applicationContext刷新完成后的处理,例如生命周期监听器的回调,广播通知等
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
复制代码
prepareRefresh();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 执行的为 ClassPathXmlApplicationContext 的父类方法 AbstractRefreshableApplicationContext
//实际刷新上下文的方法,这个方法就是实际的刷新上下文方法,其中会调用loadBeanDefinitions(beanFactory)加载配置文件中的内容到BeanDefiniton中。 详见下述代码段
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
复制代码
refreshBeanFactory(): org.springframework.context.support.AbstractRefreshableApplicationContext中的方法
protected final void refreshBeanFactory() throws BeansException {
// 如果BeanFactory存在就销毁关闭,在后续逻辑中重新创建
if (hasBeanFactory()) {
destroyBeans();
// 将BeanFactory设置为null,序列化id设置为null
closeBeanFactory();
}
try {
/**
* 创建DefaultListableBeanFactory,
* 此类是整个Bean加载的核心部分,是Spring注册加载Bean的默认实现,是整个Spring IOC的始祖.
* 对beanFactory进行设置,bean注册等操作,最后将beanFactory赋值给本类的beanFactory属性
*/
/* 我们看《BeanFactory 接口相关的继承结构》图可以看出,ConfigurableListableBeanFactory 只有一个实现类 DefaultListableBeanFactory,而且实现类 DefaultListableBeanFactory 还通过实现右边的 AbstractAutowireCapableBeanFactory 通吃了右路。
*/
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 由AbstractRefreshableApplicationContext实现,
// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
customizeBeanFactory(beanFactory);
// 加载 Bean 到 BeanFactory 中, 即Bean的注册, 此处实现类为 AbstractXmlApplicationContext,详情见 3.2.1
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
复制代码
3.2.1 AbstractXmlApplicationContext 类中的 loadBeanDefinitions(beanFactory);
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建要给beanDefinitionReader,用于读取BeanDefinition
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 然后设置环境属性以及 资源加载器ResourceLoader 为this(实际为 ClassPathXmlApplicationContext)
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 接着初始化读取器 initBeanDefinitionReader,其实这个是提供给子类覆写的
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
// 加载BeanDefiniton,主要的功能从配置文件中读取BeanDefiniton注册到注册表中。详见 3.2.2
loadBeanDefinitions(beanDefinitionReader);
}
复制代码
3.2.2 AbstractXmlApplicationContext 类中的 loadBeanDefinitions(beanDefinitionReader)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources(); // 加载资源
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations(); // 加载之前设置的xml配置文件资源
if (configLocations != null) {
// 循环加载xml文件的Bean返回Bean总个数,详见 3.2.3
reader.loadBeanDefinitions(configLocations);
}
}
复制代码
3.2.3 XmlBeanDefinitionReader 类中的 loadBeanDefinitions
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
// 获取加载器中的Resource[] 数组
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 查看loadBeanDefinitions,循环加载所有的资源,返回总数资源中的Bean
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
// 详见 3.2.4
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
复制代码
3.2.4 loadBeanDefinitions讲解
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// resourcesCurrentlyBeingLoaded 定义为 new NamedThreadLocal<Set<EncodedResource>>,
// 这里对正在解析的xml资源放入ThreadLocal中,保证只有本次线程可以访问,加载完之后再移除. 保证并发和同步
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 先添加进 threadLocal, 加载完成之后 finally 中再移除 threadLocal
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 此处 解析 Document, 并在解析之后进行注册(bean的注册方法 registerBeanDefinitions(Document doc, Resource resource),详见 详见 3.3)
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
复制代码
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
复制代码
实际上所谓的注册,就是把beanName和beanDefinition对象作为键值对放到BeanFactory对象的beanDefinitionMap。
prepareBeanFactory :准备BeanFactory,设置一些参数,比如后置处理器,
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// //设置类加载器
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
//设置表达式解析器,用来解析BeanDefiniton中的带有表达式的值,spring3增加了表达式语言的支持,默认可以使用#{bean.xxx}的形式来调用相关属性值。
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 增加了一个默认的propertyEditor,这个主要是对bean的属性等设置管理的一个工具
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 配置后置处理器,主要的作用就是在spring实例化bean的前后做一些操作。
// 添加了一个处理aware相关接口的beanPostProcessor扩展,实现了 Aware 接口的 beans 在初始化的时候,这个 processor 负责回调,
// 主要是使用beanPostProcessor的postProcessBeforeInitialization()前置处理方法实现aware相关接口的功能,
// aware接口是用来给bean注入一些资源的接口,
// 回调 ApplicationContextAware、EnvironmentAware、ResourceLoaderAware。
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//忽略自动装配的类,这些类都不能使用@Resource或者@Autowired自动装配获取对象
// 如果某个 bean 依赖于以下几个接口的实现类,在自动装配的时候忽略它们, Spring 会通过其他方式来处理这些依赖。
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
//注册可解析的自动装配类的特殊规则,如果是BeanFactory类型,则注入beanFactory对象,如果是ResourceLoader、ApplicationEventPublisher、ApplicationContext类型则注入当前对象(applicationContext对象)。
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 这里涉及到特殊的 bean,名为:loadTimeWeaver。
// 如果定义了则添加loadTimeWeaver功能的beanPostProcessor扩展,并且创建一个临时的classLoader来让其处理真正的bean。
// spring的loadTimeWeaver主要是通过 instrumentation 的动态字节码增强在装载期注入依赖。
// tips: ltw 是 AspectJ 的概念,指的是在运行期进行织入,这个和 Spring AOP 不一样,
// 感兴趣的读者请参考关于 AspectJ 的另一篇文章 https://www.javadoop.com/post/aspectj
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
/**
* 这部分首先判断是否定义了名为systemProperties的bean,如果没有则加载系统获取当前系统属性System.getProperties()并注册为一个单例bean。假如有AccessControlException权限异常则创建一个ReadOnlySystemAttributesMap对象,可以看到创建时重写了getSystemAttribute()方法,查看ReadOnlySystemAttributesMap的代码可以得知在调用get方法的时候会去调用这个方法来获取key对应的对象,当获取依旧有权限异常AccessControlException的时候则返回null。
*/
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
// 同上,此处是获得 系统环境变量
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
复制代码
在装配完成配置后执行这些后处理器,这里涉及到一些接口。 我们在开发时可以实现这些接口扩展功能,例如:InstantiationAwareBeanPostProcessor包含两个方法。一个是在实例化前调用,一个在实例化后,初始化前调用,可以用来做特殊作用,例如代理等等 DestructionAwareBeanPostProcessor在销毁前调用
MessageSource/事件监听器,初始化国际化信息资源
abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory 类
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
/*
1、转换beanName(别名转换)
平时开发中传入的参数name可能只是别名,也可能是FactoryBean,所以需要进行解析转换:
(1)消除修饰符,比如name="&test",会去除&使name="test";
(2)解决spring中alias标签的别名问题
*/
final String beanName = transformedBeanName(name);
Object bean;
//2、尝试从缓存(DefaultSingletonBeanRegistry$singletonFactories)中加载实例,如果获取不到就从singletonFactories中加载
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
//如果缓存中存在对应的bean
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 + "'");
}
}
//3、缓存渠道的bean的实例化。从缓存中获取的bean是原始状态的bean,需要在这里对bean进行bean实例化。
// 此时会进行 合并RootBeanDefinition、BeanPostProcessor进行实例前置处理、实例化、实例后置处理。
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else { // 如果缓存中没有对应bean
//4、循环依赖检查。 (构造器的循环依赖)循环依赖存在,则报错。
// @see https://blog.csdn.net/qq_36381855/article/details/79752689
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 5、如果缓存中没有数据,就会转到父类工厂去加载
//获取父工厂
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
/*
!containsBeanDefinition(beanName)就是检测如果当前加载的xml配置文件中不包含beanName所对应的
配置,就只能到parentBeanFacotory去尝试加载bean。
*/
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
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);
}
//6、存储XML配置文件的GernericBeanDefinition转换成RootBeanDefinition,即为合并父类定义。
/*
XML配置文件中读取到的bean信息是存储在GernericBeanDefinition中的,但Bean的后续处理是针
对于RootBeanDefinition的,所以需要转换后才能进行后续操作。
*/
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
//7、初始化依赖的bean
String[] dependsOn = mbd.getDependsOn();
//bean中可能依赖了其他bean属性,在初始化bean之前会先初始化这个bean所依赖的bean属性。
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
}
}
//8、创建bean
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
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, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
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 && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
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;
}
复制代码