ソースコード解析:SpringはどのようにIOCコンテナにBeanを登録するのですか?
前文
Springといえば、IOCやDIなどの概念を思い浮かべますよね。これはSpringのコアとなる考え方で、Springフレームワークを使ったことがある人なら誰でも知っている概念ですが、Springが具体的にどのようにIOCを実装しているかというと、残念ながらSpringのソースコードを見ないと答えは見つかりません。私の考えでは、2つの問題を把握できれば、Springの全体像を把握することができます。
- IOCレジストリへのBeanの登録はどのように行われるのですか?
- BeanはどのようにしてIOCコンテナから出るのか?
簡単な例
簡単な例から始めましょう。基本的にSpringを学ぶ最初の段階では、xmlの設定から始めます。Springマネジメントに渡す必要のあるクラスをxmlファイルに設定すれば、オブジェクトの作成は放っておいても大丈夫です。以下はコードを見てみましょう。
1. まずUserクラスを定義します。
@Data
public class User {
private String userName;
private String password;
}
非常にシンプルなJavaBean
2. xml設定の追加
<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd& quot;>
<bean id="userBean" class="org.kxg.springDemo.User">
<property name="userName" value="jack" />
<property name="password" value="123456" />
</bean>
</beans>
3. 設定を読み込んで実行
public class XmlBeanTest {
public static void main(String[] args){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
User user = (User) applicationContext.getBean("userBean");
System.out.println(user.getUserName());
}
}
この例はとてもシンプルで、Spring の HelloWorld の良い入門書となるでしょう。
上記のコードからわかるように、まず bean.xml で設定を読み込み、applicationContext から User オブジェクトを取得することができるので、User オブジェクトを IOC コンテナに登録するステップがあるはずです。
ソースコードを一緒に見て、BeanがどのようにSpring IOCコンテナに登録されるかを見てみましょう。
ソースコードのパース
ClassPathXmlApplicationContextから開始する。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException { this(configLocations)
this(configLocations, true, null);
}
ClassPathXmlApplicationContext のコンストラクタ・メソッドで、xml 設定ファイル(単一または複数の設定ファイル)へのパスを渡すところから始めましょう。
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//set the incoming xml configuration location information to configLocations
setConfigLocations(configLocations);
if (refresh) {
//core method
refresh();
}
}
ここでは、我々はconfigLocationsに設定された設定ファイルを渡すと、最もコアなSpringのメソッドの1つrefresh()を呼び出すことがわかります、このメソッドは、コンテナの開始のすべてのコンテンツが含まれて、私たちは春のソースコードを学ぶためのエントリポイントですので、あなたは限り、このメソッドの内容を勉強すればと言うことができる、春の枠組みのメソッドのため、あなたは全く新しい理解を持って、このメソッドの内側を見てみましょう。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Pay attention to this method
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
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;
throw ex; }
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
refresh()メソッドは内容が豊富で、個々のメソッド名から何をするのかがおおよそわかります。ここでは、ビーン登録の処理に着目して
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
この行は、ビーン登録ロジックを行うbeanFactoryを取得するためのものです。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
ここで呼ばれているメソッドはAbstractRefreshableApplicationContextクラスのrefreshBeanFactory()メソッドですが、Springのソースコードを見ていると、同じメソッドが複数のサブクラスで実装されている場合があるので、現在どのサブクラスをインスタンス化しているかを区別することに注意する必要があるのです
protected final void refreshBeanFactory() throws BeansException {. if (hasBeanFactory()) { (ifの) destroyBeans(); closeBeanFactory()。 } try { // BeanFactoryを作成する DefaultListableBeanFactory beanFactory = createBeanFactory()。 beanFactory.setSerializationId(getId())を実行します。 customizeBeanFactory(beanFactory); // ここでBeanの読み込みが行われる loadBeanDefinitions(beanFactory)。 同期(this.beanFactoryMonitor) {。 this.beanFactory = beanFactory; } } catch (IOExceptionのex) {。 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex)をスローします。 }xml 設定を使用しているため、AbstractXmlApplicationContext 抽象クラス内の loadBeanDefinitions メソッドを呼び出しています。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Construct an XmlBeanDefinitionReader to read the beans configured in the xml XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the XmlBeanDefinitionReader beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //initialize the XmlBeanDefinitionReader initBeanDefinitionReader(beanDefinitionReader); //load the bean loadBeanDefinitions(beanDefinitionReader); } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources ! = null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations ! = null) { reader.loadBeanDefinitions(configLocations); } }
@Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; for (String location : locations) { count += loadBeanDefinitions(locations); } return count; } public int loadBeanDefinitions(String location, @Nullable Set
actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int count = loadBeanDefinitions(resources); if (actualResources ! = null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } 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); int count = loadBeanDefinitions(resource); if (actualResources ! = null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } } ロードの方法は2種類あり、それぞれ呼ばれるメソッドが異なるので、configLocationsを渡します。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } Set
currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } 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()); } // Do load BeanDefinations 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(); } } } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // Construct the Document structure of xml and parse the DOM structure Document doc = doLoadDocument(inputSource, resource); //registerBeanDefinition int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } } 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; }
この中のメインメソッドはloadBeanDefinitions()であり、次のメソッドに移ります。
間にいくつかの簡単な呼び出しが省略されています
protected void doRegisterBeanDefinitions(Element root) { // Any nested
elements will cause recursion in this method. // order to propagate and preservedefault-* attributes correctly, // keep track of the current (parent) delegate, which may be null. // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // This behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. see SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); //Conduct a BeanDefinition conversion to convert the DOM structure object to a BeanDefinition parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; } protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //Spring default element conversion parseDefaultElement(ele, delegate); } else { //parse the custom element in xml delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder ! = null) { bdHolder = delegate.declareBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); // Register the BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases ! = null) { for (String alias : aliases) for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } // DefaultListableBeanFactory public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition ! = null) { if (!isAllowDefinitionMap.get(beanName)) if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } //put the beanDefinition into the beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { // Put the beanDefinition into the beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); List
updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { // Still in startup registration phase // Put the beanDefinition into the beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition ! = null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } 上記のコードからわかるように、SpringはxmlのDOM構造をパースして、IOCコンテナに登録しています。
@Component public class AnnotionConfig { @Bean(name = "userBean") public User getUserBean(){ User user = new User(); user.setUserName("Lucy"); return user; } } public class AnnotionBeanTest { public static void main(String[] args){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("org.kxg.springDemo"); User user = (User) applicationContext.getBean("userBean"); System.out.println(user.getUserName()); } }
このメソッドは、BeanDefinitionを登録するBeanDefinitionDocumentReaderを構築し、今回登録されたBeanの数を返します。
public AnnotationConfigApplicationContext(String... basePackages) { this(); // Mainly the scan method completes the registration of the bean scan(basePackages); // again to this method, there is no very familiar ~ ~ ~ refresh(); }
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); //Scan packages for bean registration doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); } //ClassPathBeanDefinitionScanner protected Set
doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { //scan the annotated classes under the package and convert them to beanDefinitions Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //register the beanDefinition registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; } public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases ! = null) { for (String alias : aliases) for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
xmlのデフォルトの設定要素には、import、alias、bean、beanがあり、これらは最もよく使われるものでもあるので、beanの変換に焦点を当ててみましょう。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder ! = null) { bdHolder = delegate.declareBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); // Register the BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases ! = null) { for (String alias : aliases) for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
// DefaultListableBeanFactory public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition ! = null) { if (!isAllowDefinitionMap.get(beanName)) if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } //put the beanDefinition into the beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { // Put the beanDefinition into the beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); List
updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { // Still in startup registration phase // Put the beanDefinition into the beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition ! = null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } ソースコードはここに続きますが、全体のプロセスは基本的に明確で、最終的な beanDefinition は beanDefinitionMap に格納され、キーは bean の名前、値は beanDefinition オブジェクトです。
プライベート最終マップ <文字列
beanDefinitionMapはConcurrentHashMapなので、実質的にBeanはMapに登録されることになります。
上記で何度もbeanDefinitionの話をしてきましたが、最終的にコンテナに登録されるのは同じオブジェクトなので、具体的には何なのか?
<ブロッククオートBeanDefinitionはBeanインスタンスを記述し、プロパティ値を持つ。
コンストラクタの引数値、および以下は具体的な実装の一覧である。
コメントを見ると、BeanDefinitionは属性値、コンストラクタ、さらにいくつかの情報を持つBeanのインスタンスであることがわかります(半キー英語、正しい訳かどうかわからないけど〜)。
BeanDefinitionはBeanの抽象化です。なぜなら、設定ファイルにはさまざまな種類のBeanがあり、BeanDefinitionはBeanのパブリックプロパティを抽象化したもので、その多くはBeanDefinitionでxml設定内のBeanの構成プロパティを記述するために使用されるからです
では、全体の流れをまとめると以下のようになります。
- xml設定ファイルをconfigLocationsに登録する。
- refresh()を呼び出してContext全体を更新し、Context全体を効果的に起動させます。
- ビーンの読み込みは、構成ファイルを読み込み、DOMオブジェクトに解析します。 DOMオブジェクトをbeanDefinitionに変換する
- beanDefinitionをbeanDefinitionMapに格納し、Bean全体の登録を完了させる。
わからない人は、これに対する処理をさかのぼって見てみると、全体の流れがよくわかると思います
アノテーションを使ったビーン登録
先ほどBean登録のためのxml設定ファイルの話をしましたが、xml設定はSpringの初期には一般的な設定方法でした。今は基本的にほとんどのシナリオでアノテーションの使用が推奨され、特にSpringBoot時代の到来で、さらに全面的にアノテーションの使用が促進されています。ここでは、Bean登録のアノテーション方法、または簡単な例から始めて見ましょう。
@Component
public class AnnotionConfig {
@Bean(name = "userBean")
public User getUserBean(){
User user = new User();
user.setUserName("Lucy");
return user;
}
}
public class AnnotionBeanTest {
public static void main(String[] args){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("org.kxg.springDemo");
User user = (User) applicationContext.getBean("userBean");
System.out.println(user.getUserName());
}
}
ここではAnnotationConfigApplicationContextを使用していますが、これもパッケージ名を渡すと、パッケージ下のSpringアノテーションを自動的にスキャンして、コンテナに登録するコンテナ実装となっています
public AnnotationConfigApplicationContext(String... basePackages) {
this();
// Mainly the scan method completes the registration of the bean
scan(basePackages);
// again to this method, there is no very familiar ~ ~ ~
refresh();
}
Bean登録のためのアノテーションアプローチに注目してみましょう。
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
//Scan packages for bean registration
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
//ClassPathBeanDefinitionScanner
protected Set
doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set
beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
//scan the annotated classes under the package and convert them to beanDefinitions
Set
candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//register the beanDefinition
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases ! = null) { for (String alias : aliases)
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
この方法を見て、見覚えはありませんか?
このメソッドは、ビーン登録のxmlメソッドでも呼び出されているので、処理は同じです。アノテーションメソッドとxml設定メソッドは、ビーンの記述が異なるだけで、本質的な違いはありません。
もちろん、プロセス全体を通して、多くの細部は無視され、主要なフローだけが見られる。
ソースコードを読むとき、特にSpringのような汎用フレームワークでは、汎用性や拡張性のために"around"でコードを書くので、あまり細部にこだわると、自分自身が細かい実装にとらわれやすくなってしまいます。最初にソースコードを見るときは、あまり細部にこだわらず、機能のメインラインは一通り読んで、大原則を知ってから、一つ一つ分解していくことをお勧めします〜。
<ブロッククオート <ブロッククオートもしあなたが私と一緒に学びたい、技術が世界を変えると信じているなら、[Java Heaven]の公開番号に注目してください、私は定期的に私の学習結果を共有し、まずあなたにそれをプッシュします
関連
-
[解決済み】クライアントが送信したリクエストは構文的に正しくありません -Spring MVC + JDBC Template
-
[解決済み] 一意なビーンによる春の自動配線。Spring は一致する Bean が 1 つであると予想していたが、2 つ見つかった
-
[解決済み] ResourceBundleViewResolverでviews.propertiesが必要な理由
-
[解決済み] <mvc:default-servlet-handler />の必要性と用途は何ですか?
-
[解決済み] SpringBootです。以下の候補からメインクラスを1つも見つけることができません。
-
[解決済み] org.hibernate.AnnotationException: エンティティに識別子が指定されていません - たとえそれが
-
[解決済み] ApplicationContext(アノテーション付き)の読み込みに失敗しました。
-
FunctionService' タイプのビーンが見つかりませんでした。
-
SpringBoot の例外です。クラスパスリソースに定義された名前 'entityManagerFactory'を持つビーンの作成エラー
-
互換性のない型.Found:'int',required:'java.lang.String'.
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] org.springframework.web.servlet.PageNotFound noHandlerFound URIを持つHTTPリクエストのマッピングが見つからない。
-
[解決済み】自動配線された依存関係のインジェクションに失敗しました。
-
[解決済み] Spring Hibernate - 現在のスレッドのトランザクション同期セッションを取得できませんでした。
-
[解決済み] Spring boot - マネージドタイプではありません。
-
[解決済み] Spring Batchのステップスコープの仕組み
-
[解決済み] mvc:annotation-driven はバインドされていません。
-
[解決済み] Junit with Spring - TestContextManager [ERROR] TestExecutionListenerを許可する際に例外をキャッチしました。
-
[解決済み] CommandLineRunnerの実行に失敗しました - Spring Batch
-
java.sql.SQLException を解決します。ユーザー 'root'@'localhost' (パスワード: YES を使用) のためのアクセスが拒否されました。
-
自動配線された依存関係のインジェクションに失敗する; 自動インジェクションに失敗する問題