Minden programozó életében egyszer elérkezik a pont, amikor valamilyen előre egyeztetett séma alapján forrás-álloményokat, azaz működő Java osztályokat kell generálnia. Jelenleg is számtalan eszközzel tudunk forrást generálni, gondoljunk csak az adatbázisból létrejövő entitásokra, vagy egy WSDL alapján generált osztály-struktúrára, sőt a legtöbb IDE alapértelmezetten segítséget nyújt ezen a területen, képes konstruktort, getter-setter metódusokat, stb készíteni pár kattintással. Ha saját magunknak szeretnénk készíteni egy forrás-generátort, természetesen arra is megvan a lehetőség. A JAXB-nek van egy al-projektje, a CodeModel, aminek segítségével megoldható a probléma, ha van elég kitartásunk megérteni a mikéntjét, ugyanis dokumentálva mondhatni egyáltalán nincs az eszköz. Rövid írásom célja, hogy ízelítőt adjon a CodeModel lehetőségeiből, ezért kézenfekvőnek tűnik, hogy ismerkedés gyanánt készítsünk egy olyan generátort, ami átadott paraméter-lista alapján összeállít egy DTO osztályt. Először is szükségünk lesz a jaxb-xjc csomagra, ugyanis ez tartalmazza a szükséges osztályokat. Magam részéről a DTO típusú osztály-generátort egy saját osztályba csomagoltam.
import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JDocComment; import com.sun.codemodel.JExpr; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import java.io.Serializable; import java.util.Map; /** * @author mhmxs */ public final class DTO { private final String name; private final Map<String, Class> parameters; public DTO(String name, Map<String, Class> parameters) { this.name = name; this.parameters = parameters; } public JCodeModel generateSource() throws JClassAlreadyExistsException { JCodeModel codeModel = new JCodeModel(); //define class header JDefinedClass clazz = codeModel._class(name); clazz._implements(Serializable.class); JAnnotationUse annotation = clazz.annotate(SuppressWarnings.class); annotation.param("value", "serial"); //Add Java-doc to class header JDocComment jDocComment = clazz.javadoc(); jDocComment.add("Simple DTO class : " + name + "\n"); jDocComment.add("@author mhmxs"); //Create constructor JMethod constr = clazz.constructor(JMod.PUBLIC); //Generate getter and setter methods for all parameters JMethod method; for(String param : parameters.keySet()) { //Add parameter to class declaration clazz.field(JMod.PRIVATE, parameters.get(param), param); //Add parameter to constructor constr.param(parameters.get(param), param); constr.body().directStatement("this." + param + " = " + param + ";"); String methodName = param.replaceFirst(String.valueOf(param.charAt(0)), String.valueOf(param.charAt(0)).toUpperCase()); //Create getter method method = clazz.method(JMod.PUBLIC, parameters.get(param), "get" + methodName); method.body()._return(JExpr._this().ref(param)); //Create setter method method = clazz.method(JMod.PUBLIC, Void.TYPE, "set" + methodName); method.param(parameters.get(param), param); method.body().directStatement("this." + param + " = " + param + ";"); } return codeModel; } }Az osztály-t az alábbi módon tudjuk meghívni.
Map<String, Class> parameters = new HashMap<String, Class>(); parameters.put("parameter", String.class); parameters.put("parameter2", Integer.class); DTO dto = new DTO("a.b.Clazz", parameters); OutputStream out = new FileOutputStream(new File("/a/b/Clazz.java")); dto.generateSource().build( new SingleStreamCodeWriter(out)); out.close();Végül az eredmény.
package a.b; import java.io.Serializable; /** * Simple DTO class : a.b.Clazz * @author mhmxs * */ @SuppressWarnings("serial") public class Clazz implements Serializable { private String parameter; private Integer parameter2; public Clazz(String parameter, Integer parameter2) { this.parameter = parameter; this.parameter2 = parameter2; } public String getParameter() { return this.parameter; } public void setParameter(String parameter) { this.parameter = parameter; } public Integer getParameter2() { return this.parameter2; } public void setParameter2(Integer parameter2) { this.parameter2 = parameter2; } }
Erdemes lehet a generateSource -nak parameterezhetoen megadni, hogy esetleg generaljon Singleton-hoz szukseges metodusokat is.
VálaszTörlésMeg mondjuk a neveket is lehet bonyolitani, az underscore-t tartalmazo lokalis neveket camelCase metodusnevekke alakitani, meg ilyen hasonlo viccek. De ez persze mar messze vezet.
Igazság szerint egy rakás dolgot lehetne még, de a példában inkább arra törekedtem, hogy minél több funkciót bemutassak minél egyszerűbben, és az általad említett dolgok igazából nem igényelnek "többletet" funkció használatot. Talán egy dolog maradt le, amit a Singletonos résszel be lehetett volna mutatni, hogy a JMod.VALAMI_KONSTANS-okat össze kell adni ha többfélét is szeretnénk, tehát egy static public metódus az (JMod.PUBLIC + JMod.STATIC)-ként adható meg.
VálaszTörlés