Параметризация фабричных методов JAXB с помощью класса для создания - PullRequest
3 голосов
/ 06 ноября 2011

Согласно документации методы фабрики JAXB не имеют аргументов. Существует ли реализация JAXB, позволяющая мне создать фабричный метод, который получает в качестве параметра класс объекта, который мне нужно создать? Бывает, что все мои объекты JAXB следуют одному и тому же шаблону создания (конкретному инструментарию байт-кода), поэтому я хотел бы инкапсулировать это в один единственный метод фабрики, имеющий в качестве параметра класс объекта JAXB для создания, избегая таким образом создание различных фабричных методов для каждого класса JAXB, которые в основном делают одно и то же.

Я нашел кого-то, кто задавал тот же вопрос на форуме OTN: https://forums.oracle.com/forums/thread.jspa?messageID=9969927#9969927,, но пока не было предложено реального ответа.

Спасибо за любую помощь

1 Ответ

2 голосов
/ 08 ноября 2011

В настоящее время это невозможно при использовании стандартных API JAXB. Я ввел следующий запрос на улучшение, чтобы добавить это поведение в EclipseLink JAXB (MOXy) :

МОКСИ Специальное решение

Вы можете использовать расширение @XmlCustomizer в EclipseLink JAXB (MOXy) , чтобы настроить создание экземпляров объектов. Этот механизм используется для настройки основных метаданных MOXy.

CommonFactory

import java.util.Date;

public class CommonFactory {

    public static Object create(Class<?> clazz) {
        if(Foo.class == clazz) {
            return new Foo(new Date());
        } else if(Bar.class == clazz) {
            return new Bar(new Date());
        }
        return null;
    }

}

Foo.class

Класс Foo обычно аннотируется, за исключением того, что мы будем использовать аннотацию @XmlCustomizer, чтобы указать DescriptorCustomizer, который мы будем использовать для настройки метаданных MOXy.

import java.util.Date;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlCustomizer;

@XmlRootElement
@XmlType(factoryClass=CommonFactory.class, factoryMethod="create")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(FactoryCustomizer.class)
public class Foo {

    private Date creationDate;
    private Bar bar;

    // Non-default constructor
    public Foo(Date creationDate) {
        this.creationDate = creationDate;
    }

}

Бар

Снова мы будем использовать аннотацию @XmlCustomizer для ссылки на то же DescriptorCustomizer, что и в классе Foo.

import java.util.Date;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlCustomizer;

@XmlType(factoryClass=CommonFactory.class, factoryMethod="create")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(FactoryCustomizer.class)
public class Bar {

    private Date creationDate;

    // Non-default constructor
    public Bar(Date creationDate) {
        this.creationDate = creationDate;
    }

}

FactoryCustomizer

MOXy имеет концепцию InstantiationPolicy для создания новых объектов. В этом примере мы заменим собственный экземпляр InstantiationPolicy, который может использовать параметризованные фабричные методы:

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.descriptors.InstantiationPolicy;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.sessions.AbstractSession;

public class FactoryCustomizer implements DescriptorCustomizer{

    @Override
    public void customize(ClassDescriptor descriptor) throws Exception {
        descriptor.setInstantiationPolicy(new MyInstantiationPolicy(descriptor));
    }

    private static class MyInstantiationPolicy extends InstantiationPolicy {

        public MyInstantiationPolicy(ClassDescriptor descriptor) {
            InstantiationPolicy defaultInstantiationPolicy = descriptor.getInstantiationPolicy();
            this.factoryClassName = defaultInstantiationPolicy.getFactoryClassName();
            this.factoryClass = defaultInstantiationPolicy.getFactoryClass();
            this.methodName = defaultInstantiationPolicy.getMethodName();
        }

        @Override
        public void initialize(AbstractSession session) throws DescriptorException {
            super.initialize(session);
        }

        @Override
        protected void initializeMethod() throws DescriptorException {
            Class<?>[] methodParameterTypes = new Class[] {Class.class};
            try {
                this.method = PrivilegedAccessHelper.getMethod(factoryClass, methodName, methodParameterTypes, true);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public Object buildNewInstance() throws DescriptorException {
            Object[] parameters = new Object[] {this.descriptor.getJavaClass()};
            try {
                return PrivilegedAccessHelper.invokeMethod(method, factory, parameters);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }

}

Демо

import java.io.StringReader;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Foo.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Foo foo = (Foo) unmarshaller.unmarshal(new StringReader("<foo><bar/></foo>"));

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(foo, System.out);
    }

}

выход

<?xml version="1.0" encoding="UTF-8"?>
<foo>
   <creationDate>2011-11-08T12:35:43.198</creationDate>
   <bar>
      <creationDate>2011-11-08T12:35:43.198</creationDate>
   </bar>
</foo>

Для получения дополнительной информации

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...