我正在开发一个 Spring 应用程序,我想知道是否有任何方法可以在我的配置中指定 XML 文件的路径,使其通过 JAXB 自动解码到 Java 对象中(我可能会考虑其他方法)库),然后将其注入(inject)到 bean 中。
Google 搜索会产生不同的结果,但它们似乎更多的是在您的 bean 中注入(inject)编码器/解码器,然后自己完成工作(如 https://www.intertech.com/Blog/jaxb-tutorial-how-to-marshal-and-unmarshal-xml/ ),我更感兴趣的是将此样板委托(delegate)给 Spring。
谢谢
最佳答案
您可以根据本文实现自定义资源加载器:Spicy Spring: Create your own ResourceLoader 。它需要一些假设:
- 您要加载的类具有
JAXB
使用的所有必需注释允许反序列化。 - 您可以构建
JaxbContext
使用给定的类列表。 - 您需要检查自己加载的类是否是您所期望的。
第 0 步 - 创建 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
应该是类似的类。
第 1 步 - 创建解码器
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
类。
第 2 步 - 创建类资源
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 。如果需要,您可以尝试找到更好的实现或改进这一实现。
第 3 步 - 创建 JAXB 资源加载器
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
支持资源。
第 4 步 - 注册 JAXB 资源加载器
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
可能会有很大改进版本。
第 5 步 - 简单使用
假设您有pojos/user.xml
文件在 resource
文件夹如下所示:
<User>
<firstName>Rick</firstName>
<lastName>Bartez</lastName>
</User>
您可以将其注入(inject)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
类,但这是该解决方案的一个缺点。
关于java - Spring注入(inject)XML未编码实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55180862/