spring 4.0 소스 코드 분석.
32953 단어 응용 개발
오늘 은 스프링 의 IOC 를 먼저 쓰 겠 습 니 다.IOC 의 개념 은 반전 을 제어 합 니 다. 저 는 spring 이 xml 설정 을 통 해 일부 속성의 실례 화 를 원래 우리 가 프로그램 에서 한 일 을 spring 의 IOC 용기 에 맡 겼 다 는 것 을 깨 달 았 습 니 다.하지만 이것 은 가장 간단 하 다. 스프링 은 우리 에 게 다른 많은 일 을 해 주 었 다.하지만 IOC 의 가장 핵심 적 인 업무 가 바로 이것 이 라 고 생각 합 니 다.
처음에 제 가 spring 의 소스 코드 를 읽 은 것 은 다운로드 한 spring 기술 내부 pdf 자료 에 따 른 것 입 니 다.내 가 다운로드 한 것 은 spring 3.0 이 고, 이 pdf 는 2.0 이다.그리고 어 리 석 게 읽 고 모 르 는 것 을 만나면 소스 코드 와 디 버 깅 을 봅 니 다.이렇게 된 지 며칠 이 되 었 지만 아직 아무런 진전 이 없다.나중에 생각해 보 니 이렇게 연구 해 서 는 안 된다 고 생각 했 어 요.목 표를 찾 은 다음 에 목적 성 있 는 연 구 를 해 야 한다.이렇게 되면 효율 이 훨씬 높아진다.비록 내 가 이전에 연구 한 것 도 모두 문제 에 근거 하여 목적 성 있 는 연 구 였 지만 이런 것들 은 모두 좋 은 규범 이 없 었 다.그래서 집행 이 좀 복잡 합 니 다.
오늘 은 아래 두 가지 각도 에서 세분 화 하 겠 습 니 다.
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
이 중 document Loader 는 속성 이 며 초기 화 되 어 있 습 니 다.loadDocument 방법 코드 는 다음 과 같 습 니 다.
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*/
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
/**
* Create the {@link DocumentBuilderFactory} instance.
* @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}
* or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})
* @param namespaceAware whether the returned factory is to provide support for XML namespaces
* @return the JAXP DocumentBuilderFactory
* @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory
*/
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}
/**
* Create a JAXP DocumentBuilder that this bean definition reader
* will use for parsing XML documents. Can be overridden in subclasses,
* adding further initialization of the builder.
* @param factory the JAXP DocumentBuilderFactory that the DocumentBuilder
* should be created with
* @param entityResolver the SAX EntityResolver to use
* @param errorHandler the SAX ErrorHandler to use
* @return the JAXP DocumentBuilder
* @throws ParserConfigurationException if thrown by JAXP methods
*/
protected DocumentBuilder createDocumentBuilder(
DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
throws ParserConfigurationException {
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
이 를 통 해 알 수 있 듯 이 spring 해석 xml 은 dom4j 와 jdom 같은 프레임 워 크 를 사용 하지 않 고 자바 의 API 방식 을 직접 사용 합 니 다.그 중에서 DocumentBuilder Factory 를 만 들 때 vaidating 을 true 로 설정 합 니 다. 읽 을 때 xml 설정 의 정확성 을 검증 합 니 다.그 중 에는 xsd 의 모델 에 따른다.여기 에는 많은 다른 학우 들 이 이런 문 제 를 만 났 을 것 이다. 가끔 은 spring 의 프로젝트 운행 이 좋 지만 인터넷 이 끊 긴 상황 에서 오류 가 발생 했다.사실은 spring 의 검증 이 이상 한 짓 을 하고 있 습 니 다.물론 기본 적 인 상황 에서 spring 은 인터넷 에 접속 하여 검증 하지 않 을 것 입 니 다. xsd 를 그 중의 한 곳 에 두 었 을 것 입 니 다.그럼 어디 다 놓 을까요?docBuilder.setEntityResolver(entityResolver);여기 있 습 니 다.다음 entitiResolver 는 InputSource 클래스 를 되 돌려 줍 니 다.
InputSource source = super.resolveEntity(publicId, systemId);
if (source == null && systemId != null) {
String resourcePath = null;
try {
String decodedSystemId = URLDecoder.decode(systemId);
String givenUrl = new URL(decodedSystemId).toString();
String systemRootUrl = new File("").toURL().toString();
// Try relative to resource base if currently in system root.
if (givenUrl.startsWith(systemRootUrl)) {
resourcePath = givenUrl.substring(systemRootUrl.length());
}
}
souce 가 비어 있 을 때 xsd 파일 을 URL 로 읽 는 것 이 분명 합 니 다. 그 중 systemId 는 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 입 니 다.
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
기다리다그래서 인터넷 이 없 으 면 방문 하지 못 하면 실수 할 것 이다.
그 중에서 spring 의 기본 구현 은 Pluggable SchemaResolver 류 입 니 다.
private Map<String, String> getSchemaMappings() {
if (this.schemaMappings == null) {
synchronized (this) {
if (this.schemaMappings == null) {
if (logger.isDebugEnabled()) {
logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
}
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded schema mappings: " + mappings);
}
Map<String, String> schemaMappings = new ConcurrentHashMap<String, String>();
CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
this.schemaMappings = schemaMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
}
}
}
}
return this.schemaMappings;
}
이 systemId 에 대응 하 는 xsd 위 치 를 초기 화 했 습 니 다.그 중의 DEFAULTSCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas"。이것 은 정적 으로 변 하지 않 는 클래스 구성원 변수 입 니 다.그래서 spring 은 classpath 경로 의 모든 jar 아래 META - INF / spring. schemas 를 읽 습 니 다.그 중에서 org. springframework. beans - 3.0.5. RELEASE. jar 에서 spring. schemas 의 내용 은 다음 과 같다.
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
그래서 지금 은 왜 그 잘못 이 일 어 났 는 지 이해 할 수 있다.classpath 경로 에 이 파일 들 이 없 는 지 인터넷 에서 찾 았 더 니 오류 가 발생 했 습 니 다.그 중에서 도 이것 과 유사 한 부분 이 있 습 니 다. 바로 op 입 니 다. spring 은 xml 의 네 임 스페이스 에 대해 서로 다른 유형 으로 해석 할 수 있 습 니 다.읽 은 파일 은 spring. handlers 입 니 다.
이상 xml 의 검증 분석 입 니 다.
검증 이 통과 되 었 습 니 다. xml 을 어떻게 읽 는 지 에 대한 문제 입 니 다.여 기 는 DefaultBean Definition DocumentReader 클래스 의 다음 과 같은 방법 으로 끝 납 니 다.
* 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);
}
}
xml 에서 Element 의 네 임 스페이스 를 분석 하면 기본 값 과 다른 처리 방식 을 볼 수 있 습 니 다.기본 값 은 http://www.springframework.org/schema/beans, 즉 라벨 이 < bean > 인 Element 입 니 다.< op >, < tx > 등 이 라면 서로 다른 처리 가 될 것 입 니 다. 여기 서 언급 한 META - INF / spring. handlers 에서 프로필 정 보 를 읽 었 습 니 다.< op > 이 라면 다음 내용 http \: / www. springframework. org / schema / ap = org. springframework. aop. config. aop NamespaceHandler
。
op, tx (사무) 와 같은 탭 이 라면 이 방법 에 도달 할 수 있 습 니 다. Bean Definition ParserDelegate 클래스 에서 이 중 handler 는 위 에 설정 한 파일 의 AopNamespaceHandler 입 니 다.
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
여 기 는 bean 의 읽 기 에 중점 을 두 었 습 니 다.op 은 다음 에 설명 할 때 까지 기 다 려 야 op 이 자세히 설명 할 수 있 습 니 다.책 에 프로그램 이 알고리즘 + 데이터 구조 라 는 말 이 있 는 것 같 습 니 다.
예전 에 나 는 줄곧 알고리즘 을 맨 앞 에 놓 았 다.이것 은 약간 과정 을 향 한 것 처럼 때로는 비교적 빠 르 고 작은 프로그램 에 직면 하기 도 한다.그러나 프로그램 이 복잡 해 지면 데이터 구 조 를 먼저 보 는 것 이 오히려 쉽다.그래서 저 는 잠시 < bean > 을 연구 해 보 겠 습 니 다. 이 대응 하 는 유형의 데이터 구 조 는 다음 에 코드 를 보면 더욱 쉬 워 집 니 다.
/**
* Constant for the default scope name: "", equivalent to singleton status
* but to be overridden from a parent bean definition (if applicable).
*/
public static final String SCOPE_DEFAULT = "";
/**
* Constant that indicates no autowiring at all.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
/**
* Constant that indicates autowiring bean properties by name.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
/**
* Constant that indicates autowiring bean properties by type.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
/**
* Constant that indicates autowiring a constructor.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
/**
* Constant that indicates determining an appropriate autowire strategy
* through introspection of the bean class.
* @see #setAutowireMode
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* use annotation-based autowiring for clearer demarcation of autowiring needs.
*/
@Deprecated
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
/**
* Constant that indicates no dependency check at all.
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_NONE = 0;
/**
* Constant that indicates dependency checking for object references.
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_OBJECTS = 1;
/**
* Constant that indicates dependency checking for "simple" properties.
* @see #setDependencyCheck
* @see org.springframework.beans.BeanUtils#isSimpleProperty
*/
public static final int DEPENDENCY_CHECK_SIMPLE = 2;
/**
* Constant that indicates dependency checking for all properties
* (object references as well as "simple" properties).
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_ALL = 3;
private volatile Object beanClass;
private String scope = SCOPE_DEFAULT;
private boolean singleton = true;
private boolean prototype = false;
private boolean abstractFlag = false;
private boolean lazyInit = false;
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
private String[] dependsOn;
private boolean autowireCandidate = true;
private boolean primary = false;
private final Map<String, AutowireCandidateQualifier> qualifiers =
new LinkedHashMap<String, AutowireCandidateQualifier>(0);
private boolean nonPublicAccessAllowed = true;
private boolean lenientConstructorResolution = true;
private ConstructorArgumentValues constructorArgumentValues;
private MutablePropertyValues propertyValues;
private MethodOverrides methodOverrides = new MethodOverrides();
private String factoryBeanName;
private String factoryMethodName;
private String initMethodName;
private String destroyMethodName;
private boolean enforceInitMethod = true;
private boolean enforceDestroyMethod = true;
private boolean synthetic = false;
private int role = BeanDefinition.ROLE_APPLICATION;
private String description;
private Resource resource;
이것 을 보 니 < bean > 프로필 의 속성 이 아 닙 니까? lazy Init, autowireMode 등 이 맞습니다.그 중에서 도 가장 중요 한 것 은 빨간색 을 표시 하 는 부분 이다.그것 은 바로 그 중의 < property > 라벨 의 데이터 구조 입 니 다.Mutable Property Values 클래스 에 이러한 구성원 변수 가 있 습 니 다. private final List < Property Value > property ValueList;PropertyValue 의 데이터 구 조 는 다음 과 같다.
private final String name;
private final Object value;
private Object source;
private boolean optional = false;
private boolean converted = false;
private Object convertedValue;
/** Package-visible field that indicates whether conversion is necessary */
volatile Boolean conversionNecessary;
/** Package-visible field for caching the resolved property path tokens */
volatile Object resolvedTokens;
/** Package-visible field for caching the resolved PropertyDescriptor */
volatile PropertyDescriptor resolvedDescriptor;
이 건 프로 퍼티 스타일 같 아.그 중에서 name 은 name 입 니 다. Object 는 value 나 ref 입 니 다.왜 하나의 맵 으로 bean 의 속성 을 저장 하지 않 습 니까?공식 적 인 해석 은 Property Value 의 유연성 을 더 많이 사용 하고 색인 속성 등 을 최적화 하 는 방식 으로 처리 할 수 있다 는 것 이다.여기 Property Value 가 몇 개 더 생 겼 나 봐 요.
xml 의 검증 읽 기와 < bean > 태그 에 대응 하 는 데이터 구 조 를 알 게 된 후 xml 를 어떻게 읽 는 지, 이 BeanDefinition 을 어떻게 생 성 하 는 지 말 하지 않 았 습 니 다.여기 서 앞의 분석 을 생략 하고 BeanDefinition 을 생 성 하 는 것 은 BeanDefinition ParserDelegate 와 같은 것 입 니 다. 사실 spring 의 클래스 이름 은 좋 습 니 다. 이 를 보면 BeanDefinition 이 해석 한 대표 라 는 것 을 알 수 있 습 니 다. 그것 은 BeanDefinition 과 막대 한 관 계 를 가 진 것 이 분명 합 니 다.사실 모든 생 성 된 BeanDefinition 은 이런 종류 에서 생 긴 것 이다.이 방법 에서
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
클 라 스 의 속성 과 parent 의 속성 에 따라 AbstractBean Definition 클래스 를 만 드 는 것 을 볼 수 있 습 니 다.실제 제 네 릭 빈 데 피 니 션 을 만 들 었 고 제 네 릭 빈 데 피 니 션 류 는 Abstract빈 데 피 니 션 을 계승 했다.그 중에서 자신 만 의 parseBean Definition Attributes (ele, beanName, containingBean, bd) 를 호출 했 습 니 다.방법, 이 방법 은 이러한 bean 의 속성 을 실례 화 하 는 것 을 소개 합 니 다. 예 를 들 어 중요 한 scope, abstract, lazy - init, autowire, depends - on, init - method, factory - method 등 입 니 다.설정 이 없 으 면 기본 방식 입 니 다.더 중요 한 것 은 해석 값 이다.
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
값 을 분석 할 때 위의 이 방법 을 호출 합 니 다.property 라 는 탭 을 반복 해서 찾 을 수 있 습 니 다.그리고 해석/**
* Parse a property element.
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
그 중에서 하나의 속성 이 있 고 있 는 것 을 발견 하면 바로 돌아 오기 때문에 하나의 bean 이 두 개의 name 을 p1 로 설정 하면 첫 번 째 역할 을 합 니 다.그 중에서 Object val = parsePropertyValue (ele, bd, propertyName);또한 이 종류의 여러 가지 방법 으로 호출 됩 니 다. property 태그 에 value 가 직접 있 으 면 가장 간단 한 방법 입 니 다. 그렇지 않 으 면 하위 태그, list, set, map 등 이 있 을 수 있 습 니 다.독자 들 은 Bean Definition ParseDelege 류 의 상응하는 방법 을 스스로 볼 수 있다.위 에 서 는 value 를 얻 은 후 Property Value 류 를 만 드 는 것 도 볼 수 있다.그리고 AbstractBean Definition 류 getProperty Values (). addProperty Value (pv) 를 통 해 여기까지 모두 해석 되 었 습 니 다.public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in the same XML file.
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element", ele);
return null;
}
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
여기까지 Bean Definition 은 xml 를 분석 하여 생 성 되 었 습 니 다. Default Listable Bean Factory 류 에 어떻게 넣 었 습 니까?앞에서 도 말 했 듯 이 빈 데 피 니 션 은 결국 이 클래스 의 맵 대상 에 놓 인 다./** Map of bean definition objects, keyed by bean name */
private final Map
이 방법 은 매우 중요 합 니 다. 어떻게 XmlBeanDefinitionReader 를 통 해 xml 를 분석 하고 최종 적 으로 BeanDefinitionParserDelegate 에 가서 bean 라벨 을 분석 합 니까?그 중의 방식 은
XmlBeanDefinitionReader---DefaultBeanDefinitionDocumentReader--BeanDefinitionParserDelegate。
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(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));
}
}
그 중에서 DefaultBeanDefinitionDocumentReader 에서 bean 라벨 을 분석 할 때 위의 방법 을 호출 합 니 다. BeanDefinitionParserDelegate 분석 을 통 해 생 성 된 것 은 BeanDefinitionHolder 입 니 다. 실제로 세 가지 속성 이 있 습 니 다.
private final BeanDefinition beanDefinition;
private final String beanName;
private final String[] aliases;
그리고 Bean Definition ReaderUtils 에서
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 aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
그 중의 registry 는 바로 Default Listable Bean Factory 류 입 니 다. 그 는 Bean Definition Registry 를 실현 하여 이 인 터 페 이 스 를 만 들 었 습 니 다.이 인터페이스의 내용 은 아래 이다.registry 는 XmlReader Context 와 같은 종 류 를 통 해 얻 을 수 있 습 니 다.XmlReader Context 는 이름 에서 xml 을 읽 는 컨 텍스트 로 설명 합 니 다.XmlBean DefinitionReader 클래스 와 NamespaceHandler Resolver 클래스 를 포함 하 는 상하 의 역할 을 합 니 다.이 NamespaceHandler Resolver 는 태그 가 기본 이름 공간 이 아 닐 때 유용 합 니 다.
public interface BeanDefinitionRegistry extends AliasRegistry {
/**
* Register a new bean definition with this registry.
* Must support RootBeanDefinition and ChildBeanDefinition.
* @param beanName the name of the bean instance to register
* @param beanDefinition definition of the bean instance to register
* @throws BeanDefinitionStoreException if the BeanDefinition is invalid
* or if there is already a BeanDefinition for the specified bean name
* (and we are not allowed to override it)
* @see RootBeanDefinition
* @see ChildBeanDefinition
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
/**
* Remove the BeanDefinition for the given name.
* @param beanName the name of the bean instance to register
* @throws NoSuchBeanDefinitionException if there is no such bean definition
*/
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
/**
* Return the BeanDefinition for the given bean name.
* @param beanName name of the bean to find a definition for
* @return the BeanDefinition for the given name (never <code>null</code>)
* @throws NoSuchBeanDefinitionException if there is no such bean definition
*/
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
/**
* Check if this registry contains a bean definition with the given name.
* @param beanName the name of the bean to look for
* @return if this registry contains a bean definition with the given name
*/
boolean containsBeanDefinition(String beanName);
/**
* Return the names of all beans defined in this registry.
* @return the names of all beans defined in this registry,
* or an empty array if none defined
*/
String[] getBeanDefinitionNames();
/**
* Return the number of beans defined in the registry.
* @return the number of beans defined in the registry
*/
int getBeanDefinitionCount();
/**
* Determine whether the given bean name is already in use within this registry,
* i.e. whether there is a local bean or alias registered under this name.
* @param beanName the name to check
* @return whether the given bean name is already in use
*/
boolean isBeanNameInUse(String beanName);
}
이렇게 많아그래도 알 겠 어.관리 가 좀 어 지 러 웠 나 봐 요.여 기 는 다른 방식 으로사실 이런 방식 으로 spring 을 사용 할 수 있 습 니 다. 틀 리 지 않 습 니 다.이렇게 사용 하면 이해 하기 가 더욱 좋 을 것 이다.XmlBean DefinitionReader 를 만 들 때 Default Listable Bean Factory 를 가지 고 들 어 가 는 것 을 발 견 했 기 때문에 이 reader 는 항상 가지 고 다 니 는 IOC 용기 로 xml 를 분석 하면 bean 하 나 를 쉽게 해석 하고 용기 에 bean 을 넣 을 수 있 습 니 다.독자 들 은 아래 의 이 한 걸음 한 걸음 봄 을 보면 훨씬 간단명료 해 질 것 이다.
@org.junit.Test
public void testWebXmlFactory(){
IBankSecurityDao dao ;
Resource resource = new ClassPathResource("beanFactory.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
dao =(BankSecurityDaoImpl) factory.getBean("Dao");
dao.add("a");
}