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




Вы можете реализовать свой собственный загрузчик ресурсов на основе этой статьи: Spicy Spring: создайте свой собственный ResourceLoader. Это требует некоторых предположений:
JAXB, которые позволяют десериализацию.JaxbContext, используя данный список классов.POJOimport 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);
}
}
}
Простая реализация unmarshaller, где вам нужно создать 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. Вышеупомянутый класс имеет простую задачу по передаче класса из десериализатора в 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();
}
}
Если определение ресурса начинается с Spring, давайте попробуем с этим справиться. В противном случае отложите реализацию по умолчанию. Поддерживаются только 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.
Предположим, у вас есть файл 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, и я хочу внедрить ее, но, видимо, мне придется реализовать это самостоятельно.