Springソース分析-beanの解析(3)
16507 ワード
Springソース分析-beanの解析(3)
現在のバージョンSpring 4.3.8
カスタムラベルの解析
カスタムラベルの使用
多くの場合、デフォルトの設定を使うのは煩雑すぎると、解析作業は考えざるを得ない負担かもしれません.Springは拡張可能Schemeのサポートを提供しています.大体以下のステップが必要です.は、拡張が必要なコンポーネント を作成する.は、XSDファイル記述コンポーネントの内容を定義する は、XSDファイルの定義とコンポーネント定義 を解析するためのファイルを作成します.は、Namespace HandlerSupportから拡張されたハーndlerファイルを作成し、目的は、Spring容器 にコンポーネントを登録することである. Spring.handersとSpring.schemasファイルを作成します. テスト:
直接呼び出し
登録後、名前空間プロセッサは、ラベルによって異なる解像度を呼び出して解析することができます.
『Springソースの深さ解析』
現在のバージョンSpring 4.3.8
カスタムラベルの解析
カスタムラベルの使用
多くの場合、デフォルトの設定を使うのは煩雑すぎると、解析作業は考えざるを得ない負担かもしれません.Springは拡張可能Schemeのサポートを提供しています.大体以下のステップが必要です.
package io.github.binglau.bean;
import lombok.Data;
/**
* :
*/
@Data
public class User {
private String userName;
private String email;
}
package io.github.binglau;
import io.github.binglau.bean.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* :
*/
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
// Element
@Override
protected Class> getBeanClass(Element element) {
return User.class;
}
// element
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String userName = element.getAttribute("userName");
String email = element.getAttribute("email");
// BeanDefinitionBuilder , bean beanFactory
if (StringUtils.hasText(userName)) {
builder.addPropertyValue("userName", userName);
}
if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
}
}
package io.github.binglau;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* :
*/
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
# Spring.handlers
http\://www.binglau.com/schema/user=io.github.binglau.MyNamespaceHandler
# Spring.schemas
http\://www.binglau.com/schema/user.xsd=user-xsd.xsd
package io.github.binglau;
import io.github.binglau.bean.User;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
/**
* :
*/
public class BeanFactoryTest {
@Test
public void testSimpleLoad() {
BeanFactory context = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
User user = (User)context.getBean("testbean");
System.out.println(user);
}
}
/**
:
User(userName=aaa, email=bbb)
**/
カスタムラベル解析 /**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
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)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
BeanDefinitionParser
は、カスタムラベルの解析である.delegate.parseCustomElement(ele)
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
// containingBd bean, null
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//
String namespaceUri = getNamespaceURI(ele);
// NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// NamespaceHandler
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
タグの名前空間を取得直接呼び出し
BeanDefinitionParserDelegate.parseCustomElement
の方法 public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
カスタムラベルプロセッサを抽出org.w3c.dom.Node
、すなわちprivate final XmlReaderContext readerContext
new ClassPathResource("beanFactory.xml")
が初期化されると、その属性readerContext
はnamespaceHandlerResolver
の例に初期化されているので、ここでは実際にDefaultNamespaceHandlerResolver
の方法が呼び出されている. /**
* Locate the {@link NamespaceHandler} for the supplied namespace URI
* from the configured mappings.
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler}, or {@code null} if none found
*/
@Override
public NamespaceHandler resolve(String namespaceUri) {
// handler
Map handlerMappings = getHandlerMappings();
//
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
// ,
return (NamespaceHandler) handlerOrClassName;
}
else {
// ,
String className = (String) handlerOrClassName;
try {
//
Class> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
//
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// NamespaceHandler
namespaceHandler.init();
//
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}
上記の呼び出しDefaultNamespaceHandlerResolver
では、以前のカスタムラベルを参照して使用します. public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
カスタマイズされた名前空間を取得した後、馬に戻ってnamespaceHandler.init();
の登録を行い、カスタムラベルをサポートします.登録後、名前空間プロセッサは、ラベルによって異なる解像度を呼び出して解析することができます.
BeanDefinitionParser
の主な機能は、getHandlerMappings
のプロファイルを読み出し、設定ファイルをmapにキャッシュすることである. /**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map getHandlerMappings() {
//
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
// this.handlerMappingsLocation META-INF/Spring.handlers
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map handlerMappings = new ConcurrentHashMap(mappings.size());
// Properties Map handlerMappings
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}
タブ解析Spring.handlers
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
//
return findParserForElement(element, parserContext).parse(element, parserContext);
}
/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// , user, , localName user
String localName = parserContext.getDelegate().getLocalName(element);
// user ,
// registerBeanDefinitionParser("user", new UserBeanDefinitionParser())
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
パース方法に対する処理NamespaceHandlerSupport#parse
@Override
public final BeanDefinition parse(Element element, ParserContext parserContext) {
//
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// AbstractBeanDefinition BeanDefinitionHolder
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
//
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
parserContext.getReaderContext().error(ex.getMessage(), element);
return null;
}
}
return definition;
}
AbstractBeanDefinitionParser#parse
/**
* Creates a {@link BeanDefinitionBuilder} instance for the
* {@link #getBeanClass bean Class} and passes it to the
* {@link #doParse} strategy method.
* @param element the element that is to be parsed into a single BeanDefinition
* @param parserContext the object encapsulating the current state of the parsing process
* @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
* @throws IllegalStateException if the bean {@link Class} returned from
* {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}
* @see #doParse
*/
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// class, UserBeanDefinitionParser getBeanClass
Class> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
// getBeanClass getBeanClassName
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if (parserContext.isNested()) {
// scope
// Inner bean definition must receive same scope as containing bean.
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
//
builder.setLazyInit(true);
}
// doParse
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
doParse(element, builder);
}
参考書『Springソースの深さ解析』