2012-06-14 2 views
12

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

В настоящее время я возвращая эти неизменяемые обертки в клиентское приложение. Есть ли какой-либо вариант, чтобы автоматически создаваемые классы были неизменными, и это позволит избежать дополнительной работы по созданию неизменяемых оберток. Любой другой подход поощряется.

  • Благодаря

ответ

-2

Вы можете создать прокси-сервер для ваших бобов непосредственно перед возвращением их клиенту. Вам понадобится javassist для создания прокси из классов (создание прокси из интерфейсов может осуществляться с помощью Java SE напрямую).

Затем вы можете вызвать исключение, если вызываются методы, начинающиеся с «set».

Вот многоразовый класс с методом, который может обернуть «любые» POJO:

import java.lang.reflect.Method; 

import javassist.util.proxy.MethodFilter; 
import javassist.util.proxy.MethodHandler; 
import javassist.util.proxy.Proxy; 
import javassist.util.proxy.ProxyFactory; 

public class Utils { 

public static <C> C createInmutableBean(Class<C> clazz, final C instance) 
     throws InstantiationException, IllegalAccessException { 
    if (!clazz.isAssignableFrom(instance.getClass())) { 
     throw new IllegalArgumentException("given instance of class " 
       + instance.getClass() + " is not a subclass of " + clazz); 
    } 
    ProxyFactory f = new ProxyFactory(); 
    f.setSuperclass(clazz); 
    f.setFilter(new MethodFilter() { 
     public boolean isHandled(Method m) { 
      // ignore finalize() 
      return !m.getName().equals("finalize"); 
     } 
    }); 
    Class c = f.createClass(); 
    MethodHandler mi = new MethodHandler() { 
     public Object invoke(Object self, Method m, Method proceed, 
       Object[] args) throws Throwable { 
      if (m.getName().startsWith("set")) { 
       throw new RuntimeException("this bean is inmutable!"); 
      } 

      return m.invoke(instance, args); // execute the original method 
               // over the instance 
     } 
    }; 
    C proxy = (C) c.newInstance(); 

    ((Proxy) proxy).setHandler(mi); 
    return (C) proxy; 
} 
} 

А вот пример код. Пусть Сотрудник будет ваш боб:

public class Employee{ 
    private String name="John"; 
    private String surname="Smith"; 
    public String getName() { 
    return name; 
    } 
    public void setName(String name) { 
    this.name = name; 
    } 
    public String getSurname() { 
    return surname; 
    } 
    public void setSurname(String surname) { 
    this.surname = surname; 
    } 
}; 

А вот тест, показывающий, что вы можете создать прокси-сервер для POJO, использовать ее добытчиками, но вы не можете использовать его сеттеров

@Test 
public void testProxy() throws InstantiationException, IllegalAccessException{ 
    Employee aBean = new Employee(); 

    //I can modify the bean 
    aBean.setName("Obi-Wan"); 
    aBean.setSurname("Kenobi"); 

    //create the protected java bean with the generic utility 
    Employee protectedBean = Utils.createInmutableBean(Employee.class, aBean); 

    //I can read 
    System.out.println("Name: "+protectedBean.getName()); 
    System.out.println("Name: "+protectedBean.getSurname()); 

    //but I can't modify 
    try{ 
     protectedBean.setName("Luke"); 
     protectedBean.setSurname("Skywalker"); 
     throw new RuntimeException("The test should not have reached this line!"); 
    }catch(Exception e){ 
     //I should be here 
     System.out.println("The exception was expected! The bean should not be modified (exception message: "+e.getMessage()+")"); 
     assertEquals("Obi-Wan", protectedBean.getName()); 
     assertEquals("Kenobi", protectedBean.getSurname()); 
    } 
} 
+4

Это худшая идея, которую когда-либо задумывала человечество. Как именно это может принести пользу кому-либо? Кто хочет классы, которые неожиданно бросают исключения во время выполнения? То, что требовал оригинальный плакат, было неизменным классом по дизайну, а не FUD. –

+0

Я согласен с вами. Лучше было бы избавиться от сеттеров статически. Мое решение пытается создать обертки без изменения сгенерированных компонентов и без необходимости писать больше кода. Кроме того, вы могли видеть такую ​​плохую идею в стандартном Java-API: http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util. List) или в API коллекций Google: https://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/ImmutableList.html#set(int, E) Оба эти бросая исключения во время выполнения. – lipido

1

Основываясь на этом блоге http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html Блеза Doughan (кто знает много о JAXB), похоже, нет встроенной поддержки для неизменяемых объектов, так что ваши объекты оболочки необходимы.

+0

На самом деле это говорит о необходимости адаптера (который представляет собой конструкцию JAXB). Да, вам нужно создать три класса (неизменяемые, изменчивые и адаптеры), но все же вы можете это сделать. –

+0

Несомненно, но эти три класса - это то же самое усилие, что и просто написание оберток. – artbristol

+0

Я возражал против вашего заявления: «нет встроенной поддержки неизменяемых объектов», а не тот факт, что усилие эквивалентно :) –

2

JAXB может работать с непубличными конструкторами/методами, поэтому единственный возможный подход заключается в том, чтобы защищать конструкторы и сеттеры no-arg, заканчивая «псевдо-неизменяемыми» объектами.

Я выбираю этот подход каждый раз, когда вручную записываю JAXB-аннотированные классы, но вы можете проверить, возможно ли это также для сгенерированного кода.

23

от JSR-133 (зависимость от Java 1.5) вы можете использовать отражение для установки неинициализированных конечных переменных. поэтому вы можете инициализировать null в частном конструкторе и использовать JAXB + неизменно чисто без XMLAdapter.

пример из https://test.kuali.org/svn/rice/sandbox/immutable-jaxb/, получил от этого комментария в блоге Блейза http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html#comment-form_584069422380571931

package blog.immutable; 

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement(name="customer") 
@XmlAccessorType(XmlAccessType.NONE) 
public final class Customer { 

    @XmlAttribute 
    private final String name; 

    @XmlElement 
    private final Address address; 

    @SuppressWarnings("unused") 
    private Customer() { 
     this(null, null); 
    } 

    public Customer(String name, Address address) { 
     this.name = name; 
     this.address = address; 
    } 

    public String getName() { 
     return name; 
    } 

    public Address getAddress() { 
     return address; 
    } 

} 
+0

Это должно быть отмечено как лучший ответ, это избавляет нас от написания класса адаптера и сопоставленного класса для каждого неизменяемого объекта, очень легкого решения. Спасибо! –

Смежные вопросы