Springの初期化とXML解析の実現
前言
Springは何ですか?これはアプリケーションのフレームワークであり、アプリケーションの開発に強力なサポートを提供します。これもbean容器であり、beanオブジェクトのライフサイクル全体を管理し、beanオブジェクトの様々な存在状態を維持します。例えば、beanオブジェクトの実用化、廃棄、beanの単一のインスタンスと複数のインスタンス状態などです。
SpringはJavaの発展史において無視できない存在として、彼が改めてJavaを定義したと言っても過言ではない。それは機能が強くて、日常の開発にとても便利です。表面は簡単なものほど、背後は複雑です。
この章からSpringのソースコードを一緒に分析します。どうやって実現するかを見てください。よく使われるのはIOC、Annotation、AOP、事務などの機能です。
1、Springの入り口
私達のプロジェクトにはweb.xmlが必要です。Springのモニターが定義されています。
context InitializedはSpring初期化の入口方法です。
Springにはもう一つの入り口があります。org.springframe ebork.web.servlet.DisprpatServletという親子の器の関係で、最終的には同じ方法org.springframe ework.com.support.AbstractAppration Cont.freshに呼び出されます。
2、初期化
Springの初期化の第一歩は、設定ファイルをロードし、設定項目を解析することです。
ok、私達はXml WebAppleicationConteext類のloadBenDefinitions方法に来ます。
3、解析XML配置
まず、設定ファイルを一つのResourceオブジェクトにパッケージし、Resourceオブジェクトの入力ストリームを取得し、InputSourceオブジェクトに変換し、最後にDoccumentオブジェクトに解析します。下のコードはメイン部分だけ残しています。
前回はSpringがappication Contact.xmlという設定ファイルをDcumentオブジェクトに解析したことを見ました。次はキーステップです。まずソースを見てください
まず、カスタム解析方法delegate.parseCustomelement(ele)に位置付けます。
最終的にorg.springframe ext.com.annotationComponentScann DefinitionParse(Element elent,PaserContxt parserContext)を呼び出しましたが、これはどうやって呼びますか?話が面白いです。
まずSpringの中の配置ファイルを見に行きます。/META-INF/spring.handers
http://www.spring frame ewark.org/schema/context=org.springframe ewark.com ntenfig.conspace Handler
http://www.spring frame ework.org/schema/je=org.springframe ework.ejb.com fig.Jee Namespace Handler
http://www.sprigframe ewark.org/schema/lang=org.sprigframe ewark.scripting.co nfig.LangNamespace Handler
http://www.springframe ework.org/schema/task=org.springframe ewark.scheduling.com fig.Task Namespace Handler
http://www.springframe ework.org/schema/cache=org.springframe ewark.cache.com nfig.namespace Handler
この中には異なるラベルの処理類が配置されています。例えばcontextラベル処理類はConttext Namespace Handlerです。そして反射実例化という処理類を通して、そのinit()方法を呼び出します。init()の方法の中には処理類が登録されています。その中に私達が興味を持っているcomponent-scanがあります。
この中のポイントはデフォルトのフィルタを登録したことです。use-default-filtersは、デフォルト値はtrueであり、この属性の値がfalseに設定されている場合、一部のコメントは追加されません。次のスキャン時にはBeanに登録できません。
ドシュanは三つのステップに分けられます。 findCandendidateComponentsは、パケット経路のすべてのclassファイルをスキャンし、Component注釈があるクラスをフィルタリングし、BeanDefinitionオブジェクトに変換し、LinkdHashSetに参加します。 は、前のステップで戻ってきたLinked HashSetをループし、setLazyInit、setSchopeなどの基本的な属性を設定します。 はBenDefinitionオブジェクトを登録し、Map容器にbeanNameとBenDefinitionをキャッシュし、ListにbeanNameを加入する。
Springのプロファイルにはcontext:annotationn-configが配置されていますが、context:component-scanを配置したらconfigを再設定する必要はありません。component-scanを解析する時にすでにデフォルトでannotationn-figのサポートを追加しました。IOCの時は@Autowiredなどのコメントをサポートすることができませんでした。
beanタグの解析は、デフォルトの処理方法です。
beanタグのIDを取得し、beanNameをidとして付与し、別名を設定します。AbstractBeanDefinitionオブジェクトを新規作成し、反射によってbeanClassを設定し、property属性名と値を解析します。
XML構成のビームであろうと、component-scanスキャンにより登録されたBeanであろうと、それらは最終的には同一のルートで、BeanDefinitionオブジェクトに変換されます。このBeanオブジェクトの属性と方法を記録しています。最後に容器に登録して、インスタンス化とIOCを待っています。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。
Springは何ですか?これはアプリケーションのフレームワークであり、アプリケーションの開発に強力なサポートを提供します。これもbean容器であり、beanオブジェクトのライフサイクル全体を管理し、beanオブジェクトの様々な存在状態を維持します。例えば、beanオブジェクトの実用化、廃棄、beanの単一のインスタンスと複数のインスタンス状態などです。
SpringはJavaの発展史において無視できない存在として、彼が改めてJavaを定義したと言っても過言ではない。それは機能が強くて、日常の開発にとても便利です。表面は簡単なものほど、背後は複雑です。
この章からSpringのソースコードを一緒に分析します。どうやって実現するかを見てください。よく使われるのはIOC、Annotation、AOP、事務などの機能です。
1、Springの入り口
私達のプロジェクトにはweb.xmlが必要です。Springのモニターが定義されています。
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Conteext Loader Listener類を見にきました。Servlet Contact Listenerインターフェースが実現されているのが見えます。context InitializedはSpring初期化の入口方法です。
Springにはもう一つの入り口があります。org.springframe ebork.web.servlet.DisprpatServletという親子の器の関係で、最終的には同じ方法org.springframe ework.com.support.AbstractAppration Cont.freshに呼び出されます。
2、初期化
Springの初期化の第一歩は、設定ファイルをロードし、設定項目を解析することです。
ok、私達はXml WebAppleicationConteext類のloadBenDefinitions方法に来ます。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
configLocationsは構成ファイルであることが分かります。筆者のプロジェクトでは、プロファイルは一つしかありません。名前はappication Contect.xmlです。次のステップはこのプロファイルをloadBenDefinitionsという方法で解析します。3、解析XML配置
まず、設定ファイルを一つのResourceオブジェクトにパッケージし、Resourceオブジェクトの入力ストリームを取得し、InputSourceオブジェクトに変換し、最後にDoccumentオブジェクトに解析します。下のコードはメイン部分だけ残しています。
public int loadBeanDefinitions(String location, Set<Resource> actualResources)
throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
// location -applicationContext.xml, Resource
Resource[] resources=resourceLoader).getResources(location);
// resources JDK InputSource , Document
InputStream inputStream = resources.getInputStream();
InputSource inputSource = new InputSource(inputStream);
Document doc = doLoadDocument(inputSource, resource);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
}
appication Contect.xmlプロファイルをDockmentオブジェクトに解析しました。Rootノードの情報は以下の通りです。
[
[#text:],
[context:component-scan: null],
[#text:],
[bean: null],
[#text:],
[bean: null],
[#text:],
[bean: null],
[#text:],
[bean: null],
[#text:],
[#comment:
viewClass:JstlView JSP JSTL
prefix suffix: , hello,
jsp “WEB-INF/jsp/hello.jsp”],
[#text:],
[bean: null],
[#text: ]
]
4、ビーン情報をロードする前回はSpringがappication Contact.xmlという設定ファイルをDcumentオブジェクトに解析したことを見ました。次はキーステップです。まずソースを見てください
// 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;
// 。
// (import、alias、bean、beans)
// (context:component-scan)
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
4.1 component-scanの解析まず、カスタム解析方法delegate.parseCustomelement(ele)に位置付けます。
最終的にorg.springframe ext.com.annotationComponentScann DefinitionParse(Element elent,PaserContxt parserContext)を呼び出しましたが、これはどうやって呼びますか?話が面白いです。
まずSpringの中の配置ファイルを見に行きます。/META-INF/spring.handers
http://www.spring frame ewark.org/schema/context=org.springframe ewark.com ntenfig.conspace Handler
http://www.spring frame ework.org/schema/je=org.springframe ework.ejb.com fig.Jee Namespace Handler
http://www.sprigframe ewark.org/schema/lang=org.sprigframe ewark.scripting.co nfig.LangNamespace Handler
http://www.springframe ework.org/schema/task=org.springframe ewark.scheduling.com fig.Task Namespace Handler
http://www.springframe ework.org/schema/cache=org.springframe ewark.cache.com nfig.namespace Handler
この中には異なるラベルの処理類が配置されています。例えばcontextラベル処理類はConttext Namespace Handlerです。そして反射実例化という処理類を通して、そのinit()方法を呼び出します。init()の方法の中には処理類が登録されています。その中に私達が興味を持っているcomponent-scanがあります。
public NamespaceHandler resolve(String namespaceUri) {
//handlerMappings loadAllProperties(), Spring
Map<String, Object> 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 {
// context:component-scan
// className org.springframework.context.config.ContextNamespaceHandler
// , ContextNamespaceHandler, init
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
NamespaceHandler namespaceHandler = BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
}
}
public void init() {
registerBeanDefinitionParser("annotation-config",
new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan",
new ComponentScanBeanDefinitionParser());
//...
}
最終的にSpringは、component-scanというラベルを通じて、ComponentScanDefinitionPaser類を手に入れて、そのparse()メソッドを呼び出すことができます。
public BeanDefinition parse(Element element, ParserContext parserContext) {
// , base-package="com.viewscenes.netsupervisor"
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().
resolvePlaceholders(basePackage);
// ,
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
/**
* configureScanner 。
* scanner.doScan
* registerComponents registerComponents
*
* @return
*/
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
4.1.1 configreScanner配置スキャナこの中のポイントはデフォルトのフィルタを登録したことです。use-default-filtersは、デフォルト値はtrueであり、この属性の値がfalseに設定されている場合、一部のコメントは追加されません。次のスキャン時にはBeanに登録できません。
protected void registerDefaultFilters() {
// use-default-filters, false。
// Component、ManagedBean、Named
if (useDefaultFilters) {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)),
false));
logger.debug("JSR-250 'javax.annotation.ManagedBean'
found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
//...
}
}
4.1.2 doScanスキャンドシュanは三つのステップに分けられます。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
//findCandidateComponents class , Component , BeanDefinition 。
// ,Component @Component,
//@Controller、@Service、@Repository, @Component。
// Component 。
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.
resolveScopeMetadata(candidate);
// ,
candidate.setScope(scopeMetadata.getScopeName());
candidate.setxxx(scopeMetadata.getxxxName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//registerBeanDefinition BeanDefinition,
//this.beanDefinitionMap.put(beanName, beanDefinition);
//this.beanDefinitionNames.add(beanName);
registerBeanDefinition(definitionHolder, this.registry);
}
}
return beanDefinitions;
}
最後に方法全体で返したのは、beanDefinitionオブジェクトのSet集合であり、二つのControllerを例にしている。
[
Generic bean: class [com.viewscenes.netsupervisor.controller.IndexController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-7.0.78\webapps\springmvc_dubbo_producer\WEB-INF\classes\com\viewscenes
etsupervisor\controller\IndexController.class],
Generic bean: class [com.viewscenes.netsupervisor.controller.UserController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-7.0.78\webapps\springmvc_dubbo_producer\WEB-INF\classes\com\viewscenes
etsupervisor\controller\UserController.class]
]
4.1.3 annotationn-configへのサポートSpringのプロファイルにはcontext:annotationn-configが配置されていますが、context:component-scanを配置したらconfigを再設定する必要はありません。component-scanを解析する時にすでにデフォルトでannotationn-figのサポートを追加しました。IOCの時は@Autowiredなどのコメントをサポートすることができませんでした。
protected void registerComponents(XmlReaderContext readerContext,
Set<BeanDefinitionHolder> beanDefinitions, Element element) {
boolean annotationConfig = true;
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
if (annotationConfig) { // annotation-config
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
......
}
}
4.2 beanラベルの解析beanタグの解析は、デフォルトの処理方法です。
beanタグのIDを取得し、beanNameをidとして付与し、別名を設定します。AbstractBeanDefinitionオブジェクトを新規作成し、反射によってbeanClassを設定し、property属性名と値を解析します。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// bean_id
String id = ele.getAttribute(ID_ATTRIBUTE);
String beanName = id;
AbstractBeanDefinition beanDefinition =
parseBeanDefinitionElement(ele, beanName, containingBean);
String[] aliasesArray = StringUtils.toStringArray(aliases);
// beanName、class BeanDefinition
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele,
String beanName, BeanDefinition containingBean) {
String className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
try {
// className setBeanClass setBeanClassName
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// setScope、setLazyInit、setAutowireMode...
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// property <bean><property name="id" value="1001"></property></bean>
parsePropertyElements(ele, bd);
return bd;
}
return null;
}
BeanDefinitionオブジェクトを登録して、component-scanスキャンのbeanと登録します。オブジェクトを容器に充填します。XML構成のビームであろうと、component-scanスキャンにより登録されたBeanであろうと、それらは最終的には同一のルートで、BeanDefinitionオブジェクトに変換されます。このBeanオブジェクトの属性と方法を記録しています。最後に容器に登録して、インスタンス化とIOCを待っています。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。