Jaxb2Marshaller doesn't need to depend on ResourceLoaderAware

Issue: SPR-10512
(cherry picked from commit 255eab5)
This commit is contained in:
Juergen Hoeller 2013-05-03 17:23:24 +02:00
parent 7cbc115da6
commit 0df1630ab9
3 changed files with 38 additions and 57 deletions

View File

@ -369,8 +369,7 @@ project("spring-oxm") {
dependencies { dependencies {
compile(project(":spring-beans")) compile(project(":spring-beans"))
compile(project(":spring-core")) compile(project(":spring-core"))
optional(project(":spring-context")) // for Jaxb2Marshaller testCompile(project(":spring-context"))
compile("commons-lang:commons-lang:2.5")
optional("com.thoughtworks.xstream:xstream:1.3.1") optional("com.thoughtworks.xstream:xstream:1.3.1")
optional("org.jibx:jibx-run:1.2.3") optional("org.jibx:jibx-run:1.2.3")
optional("org.apache.xmlbeans:xmlbeans:2.4.0") optional("org.apache.xmlbeans:xmlbeans:2.4.0")

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,10 +25,8 @@ import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlType;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
@ -42,62 +40,54 @@ import org.springframework.util.ClassUtils;
* Helper class for {@link Jaxb2Marshaller} that scans given packages for classes marked with JAXB2 annotations. * Helper class for {@link Jaxb2Marshaller} that scans given packages for classes marked with JAXB2 annotations.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Juergen Hoeller
* @author David Harrigan * @author David Harrigan
* @since 3.1.1
* @see #scanPackages() * @see #scanPackages()
*/ */
class ClassPathJaxb2TypeScanner { class ClassPathJaxb2TypeScanner {
private static final String RESOURCE_PATTERN = "/**/*.class"; private static final String RESOURCE_PATTERN = "/**/*.class";
private final TypeFilter[] jaxb2TypeFilters = private static final TypeFilter[] JAXB2_TYPE_FILTERS = new TypeFilter[] {
new TypeFilter[]{new AnnotationTypeFilter(XmlRootElement.class, false), new AnnotationTypeFilter(XmlRootElement.class, false), new AnnotationTypeFilter(XmlType.class, false),
new AnnotationTypeFilter(XmlType.class, false), new AnnotationTypeFilter(XmlSeeAlso.class, false), new AnnotationTypeFilter(XmlSeeAlso.class, false), new AnnotationTypeFilter(XmlEnum.class, false)};
new AnnotationTypeFilter(XmlEnum.class, false)};
private final ResourcePatternResolver resourcePatternResolver;
private final String[] packagesToScan; private final String[] packagesToScan;
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private List<Class<?>> jaxb2Classes = new ArrayList<Class<?>>(); public ClassPathJaxb2TypeScanner(ClassLoader classLoader, String... packagesToScan) {
/** Constructs a new {@code ClassPathJaxb2TypeScanner} for the given packages. */
ClassPathJaxb2TypeScanner(String[] packagesToScan) {
Assert.notEmpty(packagesToScan, "'packagesToScan' must not be empty"); Assert.notEmpty(packagesToScan, "'packagesToScan' must not be empty");
this.resourcePatternResolver = new PathMatchingResourcePatternResolver(classLoader);
this.packagesToScan = packagesToScan; this.packagesToScan = packagesToScan;
} }
void setResourceLoader(ResourceLoader resourceLoader) {
if (resourceLoader != null) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
}
}
/** Returns the JAXB2 classes found in the specified packages. */
Class<?>[] getJaxb2Classes() {
return jaxb2Classes.toArray(new Class<?>[jaxb2Classes.size()]);
}
/** /**
* Scans the packages for classes marked with JAXB2 annotations. * Scan the packages for classes marked with JAXB2 annotations.
*
* @throws UncategorizedMappingException in case of errors * @throws UncategorizedMappingException in case of errors
*/ */
void scanPackages() throws UncategorizedMappingException { public Class<?>[] scanPackages() throws UncategorizedMappingException {
try { try {
for (String packageToScan : packagesToScan) { List<Class<?>> jaxb2Classes = new ArrayList<Class<?>>();
for (String packageToScan : this.packagesToScan) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(packageToScan) + RESOURCE_PATTERN; ClassUtils.convertClassNameToResourcePath(packageToScan) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern); Resource[] resources = this.resourcePatternResolver.getResources(pattern);
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
for (Resource resource : resources) { for (Resource resource : resources) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (isJaxb2Class(metadataReader, metadataReaderFactory)) { if (isJaxb2Class(metadataReader, metadataReaderFactory)) {
String className = metadataReader.getClassMetadata().getClassName(); String className = metadataReader.getClassMetadata().getClassName();
Class<?> jaxb2AnnotatedClass = resourcePatternResolver.getClassLoader().loadClass(className); Class<?> jaxb2AnnotatedClass = this.resourcePatternResolver.getClassLoader().loadClass(className);
jaxb2Classes.add(jaxb2AnnotatedClass); jaxb2Classes.add(jaxb2AnnotatedClass);
} }
} }
} }
return jaxb2Classes.toArray(new Class<?>[jaxb2Classes.size()]);
} }
catch (IOException ex) { catch (IOException ex) {
throw new UncategorizedMappingException("Failed to scan classpath for unlisted classes", ex); throw new UncategorizedMappingException("Failed to scan classpath for unlisted classes", ex);
@ -107,8 +97,8 @@ class ClassPathJaxb2TypeScanner {
} }
} }
private boolean isJaxb2Class(MetadataReader reader, MetadataReaderFactory factory) throws IOException { protected boolean isJaxb2Class(MetadataReader reader, MetadataReaderFactory factory) throws IOException {
for (TypeFilter filter : jaxb2TypeFilters) { for (TypeFilter filter : JAXB2_TYPE_FILTERS) {
if (filter.match(reader, factory)) { if (filter.match(reader, factory)) {
return true; return true;
} }
@ -116,5 +106,4 @@ class ClassPathJaxb2TypeScanner {
return false; return false;
} }
} }

View File

@ -75,7 +75,6 @@ import org.xml.sax.helpers.XMLReaderFactory;
import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.JdkVersion; import org.springframework.core.JdkVersion;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ -119,9 +118,8 @@ import org.springframework.util.xml.StaxUtils;
* @see #setUnmarshallerListener(javax.xml.bind.Unmarshaller.Listener) * @see #setUnmarshallerListener(javax.xml.bind.Unmarshaller.Listener)
* @see #setAdapters(XmlAdapter[]) * @see #setAdapters(XmlAdapter[])
*/ */
public class Jaxb2Marshaller public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller,
implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller, BeanClassLoaderAware, BeanClassLoaderAware, InitializingBean {
ResourceLoaderAware, InitializingBean {
private static final String CID = "cid:"; private static final String CID = "cid:";
@ -177,8 +175,8 @@ public class Jaxb2Marshaller
/** /**
* Set multiple JAXB context paths. The given array of context paths is converted to a * Set multiple JAXB context paths. The given array of context paths gets
* colon-delimited string, as supported by JAXB. * converted to a colon-delimited string, as supported by JAXB.
*/ */
public void setContextPaths(String... contextPaths) { public void setContextPaths(String... contextPaths) {
Assert.notEmpty(contextPaths, "'contextPaths' must not be empty"); Assert.notEmpty(contextPaths, "'contextPaths' must not be empty");
@ -187,8 +185,8 @@ public class Jaxb2Marshaller
/** /**
* Set a JAXB context path. * Set a JAXB context path.
* <p>Setting this property, {@link #setClassesToBeBound "classesToBeBound"}, or * <p>Setting either this property, {@link #setClassesToBeBound "classesToBeBound"}
* {@link #setPackagesToScan "packagesToScan"} is required. * or {@link #setPackagesToScan "packagesToScan"} is required.
*/ */
public void setContextPath(String contextPath) { public void setContextPath(String contextPath) {
Assert.hasText(contextPath, "'contextPath' must not be null"); Assert.hasText(contextPath, "'contextPath' must not be null");
@ -204,8 +202,8 @@ public class Jaxb2Marshaller
/** /**
* Set the list of Java classes to be recognized by a newly created JAXBContext. * Set the list of Java classes to be recognized by a newly created JAXBContext.
* <p>Setting this property, {@link #setContextPath "contextPath"}, or * <p>Setting either this property, {@link #setContextPath "contextPath"}
* {@link #setPackagesToScan "packagesToScan"} is required. * or {@link #setPackagesToScan "packagesToScan"} is required.
*/ */
public void setClassesToBeBound(Class<?>... classesToBeBound) { public void setClassesToBeBound(Class<?>... classesToBeBound) {
Assert.notEmpty(classesToBeBound, "'classesToBeBound' must not be empty"); Assert.notEmpty(classesToBeBound, "'classesToBeBound' must not be empty");
@ -220,10 +218,11 @@ public class Jaxb2Marshaller
} }
/** /**
* Set the packages to search using Spring-based scanning for classes with JAXB2 annotations in the classpath. * Set the packages to search for classes with JAXB2 annotations in the classpath.
* <p>Setting this property, {@link #setContextPath "contextPath"}, or * This is using a Spring-bases search and therefore analogous to Spring's component-scan
* {@link #setClassesToBeBound "classesToBeBound"} is required. This is analogous to Spring's component-scan feature * feature ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
* ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}). * <p>Setting either this property, {@link #setContextPath "contextPath"}
* or {@link #setClassesToBeBound "classesToBeBound"} is required.
*/ */
public void setPackagesToScan(String[] packagesToScan) { public void setPackagesToScan(String[] packagesToScan) {
this.packagesToScan = packagesToScan; this.packagesToScan = packagesToScan;
@ -390,12 +389,8 @@ public class Jaxb2Marshaller
this.beanClassLoader = classLoader; this.beanClassLoader = classLoader;
} }
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void afterPropertiesSet() throws Exception {
public final void afterPropertiesSet() throws Exception {
boolean hasContextPath = StringUtils.hasLength(this.contextPath); boolean hasContextPath = StringUtils.hasLength(this.contextPath);
boolean hasClassesToBeBound = !ObjectUtils.isEmpty(this.classesToBeBound); boolean hasClassesToBeBound = !ObjectUtils.isEmpty(this.classesToBeBound);
boolean hasPackagesToScan = !ObjectUtils.isEmpty(this.packagesToScan); boolean hasPackagesToScan = !ObjectUtils.isEmpty(this.packagesToScan);
@ -487,10 +482,8 @@ public class Jaxb2Marshaller
logger.info("Creating JAXBContext by scanning packages [" + logger.info("Creating JAXBContext by scanning packages [" +
StringUtils.arrayToCommaDelimitedString(this.packagesToScan) + "]"); StringUtils.arrayToCommaDelimitedString(this.packagesToScan) + "]");
} }
ClassPathJaxb2TypeScanner scanner = new ClassPathJaxb2TypeScanner(this.packagesToScan); ClassPathJaxb2TypeScanner scanner = new ClassPathJaxb2TypeScanner(this.beanClassLoader, this.packagesToScan);
scanner.setResourceLoader(this.resourceLoader); Class<?>[] jaxb2Classes = scanner.scanPackages();
scanner.scanPackages();
Class<?>[] jaxb2Classes = scanner.getJaxb2Classes();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Found JAXB2 classes: [" + StringUtils.arrayToCommaDelimitedString(jaxb2Classes) + "]"); logger.debug("Found JAXB2 classes: [" + StringUtils.arrayToCommaDelimitedString(jaxb2Classes) + "]");
} }