Вы можете реализовать свой собственный загрузчик ресурсов на основе этой статьи: 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
. Выше класса есть простая работа по переводу класса из десериализатора в Spring
боб. Вы можете попытаться найти лучшую реализацию или улучшить ее, если это необходимо.
Шаг 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>
Вы можете добавить его в контекст 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
классу, но это является недостатком этого решения.