16

XSDファイルからJavaオブジェクトを作成するためにJAXBを使用しています。JAXBによって生成されたオブジェクトを隠すために不変のラッパーを作成しています(以前は、不変のインターフェイスを実装してクライアントにインターフェイスを返すようにJAXBオブジェクトを更新していました。しかし、自動生成されたクラスを変更するのは悪いことであるため、ラッパーを使用します)

現在、これらの不変のラッパーをクライアントアプリに返しています。自動生成されたクラスが不変になり、不変のラッパーを作成する余分な作業を回避するためのオプションはありますか?他のアプローチをお勧めします。

  • ありがとう
4

5 に答える 5

33

JSR-133(Java 1.5依存関係)以降、リフレクションを使用して初期化されていない最終変数を設定できます。したがって、プライベートコンストラクターでnullを初期化し、XMLAdapterなしでJAXB+不変をクリーンに使用できます。

https://test.kuali.org/svn/rice/sandbox/immutable-jaxb/の例、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;
    }

}
于 2013-02-11T20:13:14.057 に答える
10

これらのXJCコンパイラプラグインを使用して、不変のクラスを直接生成できます。

于 2014-01-20T12:03:45.373 に答える
2

JAXBは非公開のコンストラクター/メソッドで動作できるため、実行可能な唯一のアプローチは、引数なしのコンストラクターとセッターを保護し、最終的に「疑似不変」オブジェクトにすることです。

JAXB注釈付きクラスを手動で作成するたびにこのアプローチを選択しますが、生成されたコードでもこれが可能かどうかを確認できます。

于 2012-06-15T08:02:50.113 に答える
1

Blaise Doughan(JAXBについてよく知っている)によるこのブログ投稿http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.htmlに基づくと、不変オブジェクトのネイティブサポートはないようです。したがって、ラッパーオブジェクトが必要です。

于 2012-06-15T07:54:30.807 に答える
-3

Beanをクライアントに返す直前に、Beanのプロキシを作成できます。クラスからプロキシを作成するには、 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());
    }
}
于 2013-02-11T22:09:21.590 に答える