Spring与设计模式---代理模式

背景

spring的配置文件一般有两种类型:DTD或者XSD类型.配置文件中配置了bean和bean依赖关系的定义,加载配置文件并解析文件中的bean定义是为容器注册和获取bean的准备工作.Spring将配置文件看成一个资源(Resource),首先会将改Resource进行编码,判断资源的验证模式validationMode(即是属于DTD验证模式还是XSD验证模式),根据对应的模式加载和解析配置文件得到Document文件.对于xml的解析交给了SAX来负责.

在解析XML文件过程中,SAX首先会读取改文档上的声明,根据声明寻找相应的DTD定义,以便对文档进行验证.默认寻找方式是从网络上下载相应的DTD声明,并进行验证.下载过程比较漫长也受网络的影响,所以spring在项目中提供了DTD声明文件(如 spring-beans-2.0.dtd). EntityResolver类的作用就是项目本身提供一个如何寻找DTD声明的方法,即有程序来实现寻找DTD声明过程,将获取到的DTD文件交给SAX,避免网络的寻找.

Spring中的EntityResolver采用了代理模式来实现,下面我会详细的介绍源码中的该代理模式设计.

源码中的设计

结构

源码实现

在spring源码设计中,

1
2
3
4
5
6
//定义了一个公共的 EntityResolver 接口
public interface EntityResolver {
public abstract InputSource resolveEntity (String publicId,
String systemId)
throws SAXException, IOException;
}

具体实现类,即DTD和XSD的声明寻找方式:
DTD声明的寻找方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class BeansDtdResolver implements EntityResolver {
private static final String DTD_EXTENSION = ".dtd";
private static final String DTD_FILENAME = "spring-beans-2.0";
private static final String DTD_NAME = "spring-beans";
private static final Log logger = LogFactory.getLog(BeansDtdResolver.class);
@Override
public InputSource resolveEntity(String publicId, String systemId) throws IOException {
//实现了寻找DTD声明,并返回该声明的InputSource
...

        return null;
}

@Override
public String toString() {
return "EntityResolver for spring-beans DTD";
}
}

XSD声明的寻找方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class PluggableSchemaResolver implements EntityResolver {
//省略属性声明
...

public PluggableSchemaResolver(ClassLoader classLoader) {
this.classLoader = classLoader;
this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION;
}
public PluggableSchemaResolver(ClassLoader classLoader, String schemaMappingsLocation) {
Assert.hasText(schemaMappingsLocation, "'schemaMappingsLocation' must not be empty");
this.classLoader = classLoader;
this.schemaMappingsLocation = schemaMappingsLocation;
}

@Override
public InputSource resolveEntity(String publicId, String systemId) throws IOException {
//实现了寻找DTD声明,并返回该声明的InputSource 具体代码省略
...
return null;
}
//被resolveEntity 方法所调用.
private Map<String, String> getSchemaMappings() {
//省略具体实现
....
}
@Override
public String toString() {
return "EntityResolver using mappings " + getSchemaMappings();
}

}

采用代理模式来进行解析,在代理内部根据验证模式选择具体的代理对象(EntityResolver)来完成具体的寻找过程.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class DelegatingEntityResolver implements EntityResolver {
/** Suffix for DTD files */
public static final String DTD_SUFFIX = ".dtd";
/** Suffix for schema definition files */
public static final String XSD_SUFFIX = ".xsd";
private final EntityResolver dtdResolver;
private final EntityResolver schemaResolver;
public DelegatingEntityResolver(ClassLoader classLoader) {
this.dtdResolver = new BeansDtdResolver();//定义默认的dtdResolver
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
public DelegatingEntityResolver(EntityResolver dtdResolver, EntityResolver schemaResolver) {
Assert.notNull(dtdResolver, "'dtdResolver' is required");
Assert.notNull(schemaResolver, "'schemaResolver' is required");
this.dtdResolver = dtdResolver;
this.schemaResolver = schemaResolver;
}
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId != null) {
if (systemId.endsWith(DTD_SUFFIX)) {
//dtd解析方法
return this.dtdResolver.resolveEntity(publicId, systemId);
}
else if (systemId.endsWith(XSD_SUFFIX)) {
//调用META-INF/Spring.schemas解析
return this.schemaResolver.resolveEntity(publicId, systemId);
}
}
return null;
}
@Override
public String toString() {
return "EntityResolver delegating " + XSD_SUFFIX + " to " + this.schemaResolver +
" and " + DTD_SUFFIX + " to " + this.dtdResolver;
}
}
0%