Spring вводит неупорядоченный XML-объект

Я работаю над приложением Spring, и я хотел бы знать, могу ли я каким-либо образом указать в своей конфигурации путь к XML-файлу, автоматически преобразовывая его в объект Java через JAXB (хотя я могу рассмотреть другие библиотеки) а затем ввести его в боб.

Поиск в Google дает разные результаты, но они больше похожи на внедрение marshaller/unmarshaller в ваш bean-компонент, а затем выполнение работы самостоятельно (например, https://www.intertech.com/Blog/jaxb-tutorial-how-to-marshal-and-unmarshal-xml/), и я больше заинтересован в делегировании этого шаблона Spring.

Спасибо

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
367
1

Ответы 1

Вы можете реализовать свой собственный загрузчик ресурсов на основе этой статьи: Spicy Spring: создайте свой собственный 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 - создайте unmarshaller

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);
        }
    }
}

Простая реализация unmarshaller, где вам нужно создать 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. Вышеупомянутый класс имеет простую задачу по передаче класса из десериализатора в 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();
    }
}

Если определение ресурса начинается с Spring, давайте попробуем с этим справиться. В противном случае отложите реализацию по умолчанию. Поддерживаются только jaxb: ресурсы.

Шаг 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;
    }
}

Это просто копия класса регистра из статьи с некоторыми изменениями. Вероятно, можно было бы значительно улучшить последнюю версию classpath.

Шаг 5 - простое использование

Предположим, у вас есть файл Spring в папке pojos/user.xml, которая выглядит следующим образом:

<User>
    <firstName>Rick</firstName>
    <lastName>Bartez</lastName>
</User>

Вы можете вставить его в контекст resource, как показано ниже:

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);
    }
}

Немного неприятно приводить ресурс к классу Spring и экземпляр к классу ClassResource, но это недостаток этого решения.

Спасибо, я думал, что для этого в Spring наверняка будет встроенная функция, поскольку кажется вероятным сценарий, когда в каком-то приложении есть конфигурация xml, и я хочу внедрить ее, но, видимо, мне придется реализовать это самостоятельно.

Aurasphere 19.03.2019 09:16

Другие вопросы по теме