java - Spring注入(inject)XML未编码实体

标签 java spring dependency-injection jaxb

我正在开发一个 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/

相关文章:

java - 当实现类是强制性的并绑定(bind)到接口(interface)契约时,如何使用 Java 中的接口(interface)实现松散耦合?

java - 使用异常进行通信

java - 如何修改netbeans中的toString()方法(java)

java - 主要垃圾收集是否需要 JVM 之外的内存?

java - 从 spring Controller 发布到不同的 URL

java - 如何获取Spring bean的依赖关系?

spring - 我可以使用@Value 注释在 ServletContextListener 中获取系统属性吗

c# - 连续记录方法、调用和退出时间

Python 在任何脚本之前运行

javascript - AngularJS 1.6 应用程序错误 : turning items pagination into a service does not work