我正在开发一个Spring应用程序,我想知道是否可以在配置中指定XML文件的路径,并通过JAXB将其自动解组为Java对象(我可能会考虑其他方法)库),然后将其注入到bean中。
Google搜索产生不同的结果,但它们似乎更多地是在您的bean中注入编组器/解组器,然后自己完成工作(例如https://www.intertech.com/Blog/jaxb-tutorial-how-to-marshal-and-unmarshal-xml/),我对将样板委托给Spring更感兴趣。
谢谢
答案 0 :(得分:1)
您可以根据以下文章实现自定义资源加载器:Spicy Spring: Create your own ResourceLoader。它需要一些假设:
JAXB
使用的所有必需注释,这些注释允许反序列化。JaxbContext
。POJO
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "User")
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
private String lastName;
// getters, setters, toString
}
您需要预定义将从POJO
文件加载的XML
模型。上面的示例仅显示了一个类,但对于所有其他POJO
类来说应该相似。
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
@Component
public class JaxbResourceUnmarshaller {
private JAXBContext context;
public JaxbResourceUnmarshaller() {
try {
context = JAXBContext.newInstance(User.class);
} catch (JAXBException e) {
throw new IllegalArgumentException(e);
}
}
public Object read(Resource resource) {
try {
Unmarshaller unmarshaller = context.createUnmarshaller();
return unmarshaller.unmarshal(resource.getInputStream());
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
您需要创建JAXBContext
的简单解组器实现。您需要提供所有root
类。
import org.springframework.core.io.AbstractResource;
import java.io.IOException;
import java.io.InputStream;
public class ClassResource extends AbstractResource {
private final Object instance;
public ClassResource(Object instance) {
this.instance = instance;
}
public Object getInstance() {
return instance;
}
@Override
public String getDescription() {
return "Resource for " + instance;
}
@Override
public InputStream getInputStream() throws IOException {
return null;
}
}
我找不到任何可以返回POJO
实例的特定类。上面的类很容易将类从反序列化器转移到Spring
bean。您可以尝试找到更好的实施方式,或者在需要时对此进行改进。
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
public class JaxbResourceLoader implements ResourceLoader {
private static final String DB_URL_PREFIX = "jaxb:";
private final ApplicationContext applicationContext;
private final ResourceLoader delegate;
public JaxbResourceLoader(ApplicationContext applicationContext, ResourceLoader delegate) {
this.applicationContext = applicationContext;
this.delegate = delegate;
}
@Override
public Resource getResource(String location) {
if (location.startsWith(DB_URL_PREFIX)) {
JaxbResourceUnmarshaller unmarshaller = this.applicationContext.getBean(JaxbResourceUnmarshaller.class);
String resourceName = location.replaceFirst(DB_URL_PREFIX, "");
Resource resource = applicationContext.getResource("classpath:" + resourceName);
Object instance = unmarshaller.read(resource);
return new ClassResource(instance);
}
return this.delegate.getResource(location);
}
@Override
public ClassLoader getClassLoader() {
return this.delegate.getClassLoader();
}
}
如果资源定义从jaxb:
开始,让我们尝试处理它。在其他情况下,推迟到默认实现。仅支持classpath
资源。
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.Ordered;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
@Component
public class ResourceLoaderBeanPostProcessor implements BeanPostProcessor, BeanFactoryPostProcessor, Ordered,
ResourceLoaderAware, ApplicationContextAware {
private ResourceLoader resourceLoader;
private ApplicationContext applicationContext;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.resourceLoader);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.resourceLoader = new JaxbResourceLoader(this.applicationContext, this.resourceLoader);
beanFactory.registerResolvableDependency(ResourceLoader.class, this.resourceLoader);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
这只是文章中注册类的一个副本,仅作了一些更改。最新的Spring
版本可能会大大改善。
假设您在pojos/user.xml
文件夹中有resource
个文件,如下所示:
<User>
<firstName>Rick</firstName>
<lastName>Bartez</lastName>
</User>
您可以将其注入Spring
上下文中,如下所示:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
@Configuration
public class JaxbAwareConfiguration {
@Bean
public AppOwner appOwner(ResourceLoader resourceLoader) {
ClassResource resource = (ClassResource) resourceLoader.getResource("jaxb:pojos/user.xml");
User user = (User) resource.getInstance();
return new AppOwner(user);
}
}
有点令人不快的是将资源投射到ClassResource
和实例到User
类,但这是该解决方案的缺点。