2012-06-14 21 views
12

Estoy utilizando JAXB para crear objetos Java a partir de un archivo XSD. Estoy creando envolturas inmutables para ocultar objetos generados por JAXB (anteriormente estaba actualizar objetos JAXB para implementar la interfaz inmutable e interfaz de volver a cliente. Pero di cuenta de que es malo para cambiar de auto genera clases, por lo tanto, el uso de envolturas)Creación de objetos inmutables utilizandoJAXB

Actualmente estoy devolver estas envolturas inmutables a la aplicación del cliente. ¿Hay alguna opción para que las clases generadas automáticamente sean inmutables y evite el trabajo adicional de crear envoltorios inmutables? Cualquier otro enfoque es recomendado.

  • Gracias

Respuesta

-2

Se puede crear un proxy para sus granos justo antes de devolverlas al cliente. Necesitará javassist para crear Proxies a partir de clases (crear proxies a partir de interfaces se puede hacer con Java SE directamente).

Luego, puede lanzar una excepción si se invocan los métodos que comienzan con "set".

Aquí es una clase reutilizable con un método que puede envolver "ninguna" 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; 
} 
} 

Y aquí es un ejemplo de código. Deje empleado sea el bean:

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

Y aquí un caso de prueba que demuestra que se puede crear un proxy para un POJO, utilizar sus captadores, pero no puede utilizar su setter

@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

Esta es la peor idea que la humanidad haya concebido. ¿Cómo va a beneficiar esto a alguien? ¿Quién quiere clases que inesperadamente lanzan excepciones de tiempo de ejecución? Lo que el cartel original quería eran clases inmutables por diseño, no por FUD. –

+0

Estoy de acuerdo con usted. Ocultando a los setters estáticamente sería una mejor solución. Mi solución intenta crear envoltorios sin cambiar los beans generados y sin necesidad de escribir más código. Además, se podría haber visto tan mala idea en la API estándar de Java: http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util. lista), o en el Google API Colecciones: https://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/ImmutableList.html#set(int, E) Ambos arrojando excepciones de tiempo de ejecución. – lipido

1

Sobre la base de esta entrada del blog http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html por Blaise Doughan (que sabe mucho sobre JAXB), parece que no hay soporte nativo para los objetos inmutables, por lo que sus objetos envolventes son necesarios.

+0

En realidad, dice que necesita un adaptador (que es una construcción JAXB). Sí, necesitas crear tres clases (inmutables, mutables y adaptadores) pero aún puedes lograr eso. –

+0

Claro, pero esas tres clases son la misma cantidad de esfuerzo que escribir las envolturas. – artbristol

+0

me opongo su estado de "no hay soporte nativo para los objetos inmutables" y no el hecho de que el esfuerzo es equivalente :) –

2

JAXB puede trabajar con constructores/métodos no públicos, por lo que el único enfoque viable sería tener el constructor y los instaladores no-arg protegidos, terminando con objetos "pseudo-inmutables".

Elijo este enfoque cada vez que escribo manualmente las clases anotadas por JAXB, pero puede verificar si esto también es posible para el código generado.

23

a partir de JSR-133 (dependencia de Java 1.5) puede usar la reflexión para establecer variables finales no inicializadas. por lo que puede iniciar null en el constructor privado y usar JAXB + inmutable limpiamente sin ningún XMLAdapter.

ejemplo de https://test.kuali.org/svn/rice/sandbox/immutable-jaxb/, consiguió esto de un comentario en el blog de Blaise 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

Esto debe marcarse como la mejor respuesta, nos ahorra al escribir una clase de adaptador y una clase mapeada para cada objeto inmutable, una solución muy liviana. ¡Gracias! –