2015-01-29 1 views
12

Ich entwickle ein Java-Web-Projekt mit Spring und Mybatis.
In der Dao-Ebene habe ich eine Super-Klasse und eine Super-Schnittstelle definiert, die alle gängigen Methoden implementiert.
Wenn also Unterklasse erstellen oder eine Schnittstelle für ein bestimmtes Modell in dao Ebene, ich brauche nur die Super-dao Klasse & Schnittstelle zu implementieren, und verließ die Klasse Körper und Interface-Körper leer. Über die Hälfte der Sub-Dao-Level-Klasse & Schnittstelle ist durch die ganze Zeit leer.Java - Vermeiden Sie create leere Sub-Klasse & Interface oder generieren Java-Quellcode-Vorlage


(Beispiel der leeren Klasse dao & Schnittstelle :)

RoleDao.java

package core.dao; 

import core.dao.base.BaseDao; 
import core.model.Role; 

public interface RoleDao extends BaseDao<Role> { 
} 

RoleDaoImpl.java

package core.dao.impl; 

import org.springframework.stereotype.Repository; 

import core.dao.RoleDao; 
import core.dao.base.BaseDaoImpl; 
import core.model.Role; 

@Repository 
public class RoleDaoImpl extends BaseDaoImpl<Role> implements RoleDao { 
} 

Meine Frage ist:

Gibt es eine gute Art und Weise zu schreiben diese leere Klasse & Schnittstelle zu vermeiden, während sie noch verwenden könnte?

Ich denke Code generator der Verwendung dieser Klassendatei zu generieren, oder verwenden Sie Java reflection solche Klasse & Schnittstelle zur Laufzeit nach Bedarf zu schaffen, aber ins Detail noch nicht erhalten.


@aktualisieren

Es scheint nicht flexibel das Ziel ohne Code zu schaffen Quelle zu erreichen, so entschied ich mich für Java-Web-Projekt einen einfachen Java-Source-Code-Generator zu schreiben.

Und ein Tool namens codemodel ist sehr geeignet, das zu tun, es ist von Sun entwickelt, und jetzt im Besitz von Oracle, denke ich.

Und ich gab eine Antwort von mir selbst mit Code, den ich schrieb, um Java-Quellcode zu generieren.

+4

Meiner Meinung nach ist es eine bessere und besser verwaltbar Lösung für die wirklichen Klassen „zu implementieren“, anstatt eine Reflexion Trick. Interessante Frage jedoch. – Magnilex

+8

Sie sind nicht leer. Sie sind * begrenzt * und das ist wichtig, also gibt es * einen gewissen Mehrwert. Es ist nicht unmöglich, diese Klassen/Instanzen auch während der Laufzeit zu erzeugen (mit einer gewissen Bytecode-Manipulationsbibliothek), aber das würde ich nicht tun. Weniger Magie, weniger Überraschungen. – lexicore

+1

Ich würde in diesem Fall keine Codegenerierung verwenden, da Ihre Anwendung in Zukunft von anderen Entwicklern gewartet wird. Frameworks werden in Zukunft aktualisiert, wahrscheinlich passt Ihre Code-Generierung nicht in eine neue Version. Die meiste Zeit sind Wartungsentwickler nicht so geschickt, sie hätten Probleme, Ihren Code zu verstehen, sie nennen ihn kompliziert und die Wartungskosten werden steigen. –

Antwort

3

Vor etwa einem Monat fragte ich mich das gleiche :) So, scheint, dass wir eine Art Lösung haben, da Sie Spring-Bibliothek verwenden. Als ich auf docs lesen:

Anstatt Code Datenzugriffsobjekte (DAO) manuell mit SqlSessionDaoSupport oder SqlSessionTemplate, Mybatis-Spring stellt eine Proxy-Fabrik: MapperFactoryBean. Mit dieser Klasse können Sie Daten Mapper-Schnittstellen direkt in Ihre Servicebohnen injizieren. Bei der Verwendung von Mapper Sie einfach sie nennen, wie Sie immer Ihre DAOs genannt haben, aber Sie werden keine DAO Implementierung codieren müssen, weil Mybatis-Spring wird für Sie einen Proxy erstellen.

Es gibt ein Beispiel auf GitHub und auch auf diesem MyBatis' page.

Ich hoffe, dass es Ihnen einige Einblicke gibt, weil es vielleicht nicht machbar ist, Ihr ganzes System zu refaktorieren, um von solch einer netten Eigenschaft begünstigt zu werden.

3

Die Repository-Klassen für die Klassen in unseren Projekten, die QueryDSL und JPA verwenden, haben nur eine Schnittstelle, aber keine Implementierung. Es beantwortet jedoch nicht die Frage, ob es möglich ist, diese Repositorys basierend auf den Entitätsklassen direkt zu generieren, obwohl es dem entspricht, was die Apt Maven Plugin erstellt, um die QEntity Klassen für die Verwendung mit QueryDSL zu erstellen.

@NoRepositoryBean 
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> { 
} 

@Repository 
public interface DummyDataRepository extends BaseRepository<DummyData, Long> { 
} 
2

Ich habe gerade einen einfachen Code-Generator für mein Projekt geschrieben.

Es ist nur eine einzelne Klasse und könnte Modell/Dao/Service/Action-Level-Code Vorlage für 1 oder mehrere Modelle in einer einzigen Ausführung generieren.

Dependence:

Es verwendet codemodel und apache commons-io lib, und es ist ein spring + springMVC Projekt.

Wie man es verwendet:

es einige Basisklasse/Schnittstelle in meinem Projekt importieren, aus dem die erzeugte Klasse erweitert/Geräte aus, so dass Sie vielleicht kann es nicht direkt ausgeführt werden. Sie können sie jedoch als leere Klasse/Schnittstelle erstellen oder sie aus der Funktion genSourceXxx() entfernen.

CodeGenerator.java:

package my.project.util; 

import my.project.dao.base.BaseDao; 
import my.project.dao.base.BaseDaoImpl; 
import my.project.model.base.BaseIdModel; 
import my.project.service.base.BaseService; 
import my.project.service.base.BaseServiceImpl; 
import my.project.web.action.base.BaseAction; 

import java.io.File; 
import java.io.FileFilter; 
import java.io.IOException; 
import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.util.HashMap; 
import java.util.Map; 

import javax.tools.JavaCompiler; 
import javax.tools.ToolProvider; 

import org.apache.commons.io.FileUtils; 
import org.apache.commons.io.filefilter.DirectoryFileFilter; 
import org.apache.commons.io.filefilter.FileFileFilter; 
import org.apache.commons.io.filefilter.FileFilterUtils; 
import org.apache.commons.io.filefilter.IOFileFilter; 
import org.apache.commons.lang3.StringUtils; 
import org.springframework.stereotype.Controller; 
import org.springframework.stereotype.Repository; 
import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Transactional; 
import org.springframework.web.bind.annotation.RequestMapping; 

import com.sun.codemodel.ClassType; 
import com.sun.codemodel.JClass; 
import com.sun.codemodel.JClassAlreadyExistsException; 
import com.sun.codemodel.JCodeModel; 
import com.sun.codemodel.JDefinedClass; 
import com.sun.codemodel.JFieldVar; 
import com.sun.codemodel.JMethod; 
import com.sun.codemodel.JMod; 

/** 
* code generator 
* 
* @author eric 
* @date Apr 10, 2015 3:32:57 PM 
*/ 
public class CodeGenerator { 
    // location of source folder 
    public static final String tmpSourceFolderBaseLocation = "/tmp/java_code/"; // tmp location for generated code, 
    public static final String actualSourceFolderBaseLocation = "/mnt/star/workplace/eclipse_j2ee_workplace/project-name/source/java/"; // actual source folder, 

    // package 
    public static final String packageSeparator = "."; 
    public static final String basePackage = "my.project"; 
    public static final String modelPackage = "model"; 
    public static final String daoPackage = "dao"; 
    public static final String daoImplPackage = "dao.impl"; 
    public static final String servicePackage = "service"; 
    public static final String serviceImplPackage = "service.impl"; 
    public static final String actionPackage = "web.action"; 

    // source file path 
    public static final String pkgPathSeparator = File.separator; 
    public static final String sourceSuffix = ".java"; 
    public static final String basePkgPath = "my/project"; 
    public static final String modelPkgPath = "model"; 
    public static final String daoPkgPath = "dao"; 
    public static final String daoImplPkgPath = "dao" + pkgPathSeparator + "impl"; 
    public static final String servicePkgPath = "service"; 
    public static final String serviceImplPkgPath = "service" + pkgPathSeparator + "impl"; 
    public static final String actionPkgPath = "web" + pkgPathSeparator + "action"; 

    // naming 
    public static final String daoSuffix = "Dao"; 
    public static final String daoImplSuffix = "DaoImpl"; 
    public static final String serviceSuffix = "Service"; 
    public static final String serviceImplSuffix = "ServiceImpl"; 
    public static final String actionSuffix = "Action"; 

    // compiler for generated source code, 
    public static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    // classloader for compiled class, 
    public static final ClassLoader cl = genCl(tmpSourceFolderBaseLocation); 

    /** 
    * compile a source file, 
    * 
    * @param sourcePath 
    * @throws MalformedURLException 
    */ 
    public static void compileSource(String sourcePath) throws MalformedURLException { 
     // set this so that won't get compile error, 
     System.setProperty("java.class.path", System.getProperty("java.class.path") + File.pathSeparator + tmpSourceFolderBaseLocation); 
     compiler.run(null, null, null, sourcePath); 
    } 

    /** 
    * generate a classloader, 
    * 
    * @param path 
    * @return 
    * @throws MalformedURLException 
    */ 
    public static ClassLoader genCl(String path) { 
     ClassLoader cl = null; 
     try { 
      cl = new URLClassLoader(new URL[] { new File(path).toURI().toURL() }); 
     } catch (MalformedURLException e) { 
      e.printStackTrace(); 
     } 
     return cl; 
    } 

    /** 
    * <p> 
    * Generate source for model. 
    * </p> 
    * 
    * @param modelName 
    * @throws IOException 
    * @throws JClassAlreadyExistsException 
    */ 
    public static void genSourceModel(String modelName) throws IOException, JClassAlreadyExistsException { 
     String modelFullName = genFullName(modelPackage, modelName); 

     JCodeModel cm = new JCodeModel(); 
     // define type, 
     JDefinedClass dc = cm._class(modelFullName, ClassType.CLASS); 
     // extends 
     dc._extends(BaseIdModel.class); 

     // id 
     JFieldVar idField = dc.field(JMod.PRIVATE, Integer.class, "id"); // field 

     // id - getter method 
     JMethod getIdMethod = dc.method(JMod.PUBLIC, Integer.class, "getId"); 
     getIdMethod.body()._return(idField); 
     getIdMethod.annotate(cm.ref(Override.class)); // annotation - override 

     // generate source code, 
     cm.build(new File(tmpSourceFolderBaseLocation)); 

     // compile 
     compileSource(genFullPath(modelPkgPath, modelName)); 
    } 

    public static void genSourceDao(String modelName) throws JClassAlreadyExistsException, ClassNotFoundException, IOException { 
     String daoFullName = genFullName(daoPackage, modelName, daoSuffix); 
     String modelFullName = genFullName(modelPackage, modelName); 

     JCodeModel cm = new JCodeModel(); 
     // define type, 
     JDefinedClass dc = cm._class(daoFullName, ClassType.INTERFACE); 
     // extends 
     JClass superClazz = cm.ref(BaseDao.class).narrow(cl.loadClass(modelFullName)); 
     dc._extends(superClazz); 

     // generate source code, 
     cm.build(new File(tmpSourceFolderBaseLocation)); 

     // compile 
     compileSource(genFullPath(daoPkgPath, modelName, daoSuffix)); 
    } 

    public static void genSourceDaoImpl(String modelName) throws JClassAlreadyExistsException, ClassNotFoundException, IOException { 
     String daoImplFullName = genFullName(daoImplPackage, modelName, daoImplSuffix); 
     String daoFullName = genFullName(daoPackage, modelName, daoSuffix); 
     String modelFullName = genFullName(modelPackage, modelName); 

     JCodeModel cm = new JCodeModel(); 
     // define type, 
     JDefinedClass dc = cm._class(daoImplFullName, ClassType.CLASS); 
     dc.annotate(Repository.class); 

     // extends 
     JClass superClazz = cm.ref(BaseDaoImpl.class).narrow(cl.loadClass(modelFullName)); 
     dc._extends(superClazz); 
     // implements 
     dc._implements(cl.loadClass(daoFullName)); 

     // generate source code, 
     cm.build(new File(tmpSourceFolderBaseLocation)); 

     // compile 
     compileSource(genFullPath(daoImplPkgPath, modelName, daoImplSuffix)); 
    } 

    public static void genSourceService(String modelName) throws JClassAlreadyExistsException, ClassNotFoundException, IOException { 
     String serviceFullName = genFullName(servicePackage, modelName, serviceSuffix); 
     JCodeModel cm = new JCodeModel(); 
     // define type, 
     JDefinedClass dc = cm._class(serviceFullName, ClassType.INTERFACE); 

     // extends 
     dc._extends(BaseService.class); 

     // generate source code, 
     cm.build(new File(tmpSourceFolderBaseLocation)); 

     // compile 
     compileSource(genFullPath(servicePkgPath, modelName, serviceSuffix)); 
    } 

    public static void genSourceServiceImpl(String modelName, boolean serviceTransaction) throws JClassAlreadyExistsException, ClassNotFoundException, 
      IOException { 
     String serviceImplFullName = genFullName(serviceImplPackage, modelName, serviceImplSuffix); 
     String serviceFullName = genFullName(servicePackage, modelName, serviceSuffix); 

     JCodeModel cm = new JCodeModel(); 
     // define type, 
     JDefinedClass dc = cm._class(serviceImplFullName, ClassType.CLASS); 

     // annotation 
     dc.annotate(Service.class); 
     if (serviceTransaction) { 
      dc.annotate(Transactional.class); 
     } 

     // extends 
     dc._extends(BaseServiceImpl.class); 
     // implements 
     dc._implements(cl.loadClass(serviceFullName)); 

     // generate source code, 
     cm.build(new File(tmpSourceFolderBaseLocation)); 

     // compile 
     compileSource(genFullPath(serviceImplPkgPath, modelName, serviceImplSuffix)); 
    } 

    public static void genSourceAction(String modelName) throws JClassAlreadyExistsException, ClassNotFoundException, IOException { 
     genSourceAction(modelName, null); 
    } 

    /** 
    * generate action, 
    * 
    * @param modelName 
    * @param rootMappingPath 
    *   root mapping path, if null or empty then don't have this annotation, 
    * @throws JClassAlreadyExistsException 
    * @throws ClassNotFoundException 
    * @throws IOException 
    */ 
    public static void genSourceAction(String modelName, String rootMappingPath) throws JClassAlreadyExistsException, ClassNotFoundException, IOException { 
     String actionFullName = genFullName(actionPackage, modelName, actionSuffix); 

     JCodeModel cm = new JCodeModel(); 
     // define type, 
     JDefinedClass dc = cm._class(actionFullName, ClassType.CLASS); 

     // annotation 
     dc.annotate(Controller.class); 
     if (StringUtils.isNotBlank(rootMappingPath)) { 
      dc.annotate(cm.ref(RequestMapping.class)).param("value", rootMappingPath); 
     } 

     // extends 
     dc._extends(BaseAction.class); 

     // generate source code, 
     cm.build(new File(tmpSourceFolderBaseLocation)); 

     // compile 
     compileSource(genFullPath(actionPkgPath, modelName, actionSuffix)); 
    } 

    /** 
    * <p> 
    * generate a serial java source code base on a single model, don't include service level, 
    * </p> 
    * <p> 
    * Warning: this will override existing code, so, be careful! 
    * </p> 
    * 
    * @param modelName 
    */ 
    public static void genStack(String modelName) { 
     genStack(modelName, false, false, null); 
    } 

    /** 
    * <p> 
    * generate a serial java source code base on a single model. 
    * </p> 
    * <p> 
    * Warning: this will override existing code, so, be careful! 
    * </p> 
    * 
    * @param modelName 
    * @param includeService 
    *   specify whether include service level, 
    * @param serviceTransaction 
    *   whether add transaction annotation to service impl class, 
    * @param actionRootMappingPath 
    *   root mapping path, if null or empty then don't have this annotation, 
    */ 
    public static void genStack(String modelName, boolean includeService, boolean serviceTransaction, String actionRootMappingPath) { 
     try { 
      initTmp(); // clean or create folder, 

      // generate code - start 
      genSourceModel(modelName); 
      genSourceDao(modelName); 
      genSourceDaoImpl(modelName); 
      if (includeService) { 
       genSourceService(modelName); 
       genSourceServiceImpl(modelName, serviceTransaction); 
      } 
      genSourceAction(modelName, actionRootMappingPath); 
      // generate code - end 

      merge(); // copy, 
      initTmp(); // clean, so that won't have duplicated class, 
     } catch (IOException | JClassAlreadyExistsException e) { 
      e.printStackTrace(); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
    } 

    /** 
    * <p> 
    * batch generate. 
    * </p> 
    * <p> 
    * Warning: this will override existing code, so, be careful! 
    * </p> 
    * 
    * @param models 
    *   map of "modelName : actionRootMappingPath" 
    * @param includeService 
    *   specify whether include service level, 
    * @param serviceTransaction 
    *   whether add transaction annotation to service impl class, 
    */ 
    public static void genStackBatch(Map<String, String> models, boolean includeService, boolean serviceTransaction) { 
     for (String modelName : models.keySet()) { 
      genStack(modelName, includeService, serviceTransaction, models.get(modelName)); 
     } 
    } 

    /** 
    * generate class fullname, 
    * 
    * @param subPackage 
    * @param modelName 
    * @return 
    */ 
    public static String genFullName(String subPackage, String modelName) { 
     return genFullName(subPackage, modelName, ""); 
    } 

    /** 
    * generate class fullname, 
    * 
    * @param subPackage 
    * @param modelName 
    * @param suffix 
    * @return 
    */ 
    public static String genFullName(String subPackage, String modelName, String suffix) { 
     return new StringBuilder().append(basePackage).append(packageSeparator).append(subPackage).append(packageSeparator).append(modelName).append(suffix) 
       .toString(); 
    } 

    /** 
    * generate source file path, 
    * 
    * @param subPkgPath 
    * @param modelName 
    * @return 
    */ 
    public static String genFullPath(String subPkgPath, String modelName) { 
     return genFullPath(subPkgPath, modelName, ""); 
    } 

    /** 
    * generate source file path, 
    * 
    * @param subPkgPath 
    * @param modelName 
    * @param suffix 
    * @return 
    */ 
    public static String genFullPath(String subPkgPath, String modelName, String suffix) { 
     return new StringBuilder().append(tmpSourceFolderBaseLocation).append(basePkgPath).append(pkgPathSeparator).append(subPkgPath).append(pkgPathSeparator) 
       .append(modelName).append(suffix).append(sourceSuffix).toString(); 
    } 

    /** 
    * clean tmp location, 
    * 
    * @throws IOException 
    */ 
    public static void initTmp() throws IOException { 
     File tmp = new File(tmpSourceFolderBaseLocation); 

     if (!tmp.exists()) { // create if not exists, 
      tmp.mkdirs(); 
     } else { // clean if exists, 
      FileUtils.cleanDirectory(tmp); 
     } 
    } 

    /** 
    * <p> 
    * move generated code into source folder, 
    * </p> 
    * <p> 
    * Warning: this will override existing code, so, be careful! 
    * </p> 
    */ 
    public static void merge() { 
     File originalFile = new File(tmpSourceFolderBaseLocation + basePkgPath); 
     File targetFile = new File(actualSourceFolderBaseLocation + basePkgPath); 
     try { 
      // filter - java file, 
      IOFileFilter javaSuffixFilter = FileFilterUtils.suffixFileFilter(".java"); 
      IOFileFilter javaFiles = FileFilterUtils.and(FileFileFilter.FILE, javaSuffixFilter); 

      // filter - dir or java file, 
      FileFilter filter = FileFilterUtils.or(DirectoryFileFilter.DIRECTORY, javaFiles); 

      FileUtils.copyDirectory(originalFile, targetFile, filter); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 
     // String modelName = "LoginHistory"; 
     // String actionRootMappingPath = "/loginHistory"; 
     // genStack(modelName, true, false, actionRootMappingPath); 

     Map<String, String> models = new HashMap<String, String>(); 
     models.put("AdminAccount", "/adminAccount"); 
     models.put("CustomerAccount", "/customerAccount"); 
     models.put("Role", "/role"); 

     genStackBatch(models, true, true); 
    } 
} 
Verwandte Themen