2015-01-29 10 views
12

Sto sviluppando un progetto Web Java utilizzando Spring e Mybatis.
Nel livello dao, ho definito una super classe e una super interfaccia che ha implementato tutti i metodi comuni.
Quindi quando si crea sottoclasse o interfaccia per un modello specifico in livello dao, ho solo bisogno di implementare l'interfaccia & classe super-dao e lasciare il corpo della classe e il corpo dell'interfaccia vuoti. Oltre la metà della classe di livello sub-dao & l'interfaccia è vuota in ogni momento.Java: evitare di creare sottoclasse e interfaccia vuoti o generare un modello di codice sorgente Java


(Esempio della classe dao vuoto & interfaccia :)

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

La mia domanda è:

C'è un buon modo per evitare di scrivere questi vuota interfaccia di classe &, mentre ancora li potrebbe usare?

Sto pensando di utilizzare Code generator per generare questi file di classe, o utilizzare Java reflection per creare tale classe & interfaccia in fase di esecuzione come bisogno, ma non ha ottenuto nei dettagli ancora.


@Aggiornamento

Sembra non flessibile per raggiungere l'obiettivo senza creare il codice sorgente, così ho deciso di scrivere qualche semplice generatore di codice sorgente di Java per il progetto Java Web.

E uno strumento chiamato codemodel è molto adatto per farlo, è stato sviluppato da Sun, e ora di proprietà di Oracle, credo.

E, ho dato una risposta da solo con il codice che ho scritto per generare il codice sorgente java.

+4

A mio parere si tratta di una soluzione migliore e più gestibile a "attuare" le classi reali, invece di usare un po 'di riflessione trucco. Domanda interessante però. – Magnilex

+8

Non sono vuoti. Sono * limitati * e questo è importante, quindi * c'è * un certo valore aggiunto. Non è impossibile generare queste classi/istanze anche in fase di esecuzione (con qualche libreria di manipolazione bytecode), ma non lo farei. Meno magia, meno sorprese. – lexicore

+1

In questo caso non userei la codegenerazione, perché in futuro la tua applicazione verrebbe mantenuta da altri sviluppatori. I quadri saranno aggiornati in futuro, probabilmente la tua codegenerazione non si adatta a una nuova versione. La maggior parte delle volte gli sviluppatori di manutenzione non sono così abili, avrebbero problemi a capire il codice, lo chiamerebbero complicato e aumenterebbero i costi di manutenzione. –

risposta

3

Circa un mese fa mi stavo chiedendo la stessa cosa :) Quindi, sembra che abbiamo una sorta di soluzione, dal momento che si utilizza la libreria di primavera. Come ho letto su docs:

Invece di oggetti di accesso ai dati di codice (Tao) utilizzando manualmente SqlSessionDaoSupport o SqlSessionTemplate, Mybatis-Spring offre una fabbrica delega: MapperFactoryBean. Questa classe consente di iniettare le interfacce di mapping dei dati direttamente nei bean di servizio. Quando si utilizzano i mapper , è sufficiente chiamarli come hai sempre chiamato DAO, ma non è necessario codificare un'implementazione DAO in quanto perché MyBatis-Spring creerà un proxy per te .

C'è un esempio su GitHub e anche su questo MyBatis' page.

Spero che vi dia qualche spunto, perché forse non è fattibile rifattorizzare l'intero sistema per beneficiare di una tale caratteristica piacevole.

3

Le classi di repository per le classi nei nostri progetti che utilizzano QueryDSL e JPA hanno solo un'interfaccia, ma non un'implementazione. Tuttavia, non risponde alla domanda se sia possibile generare direttamente questi repository in base alle classi di entità, anche se sarebbe simile a ciò che lo Apt Maven Plugin fa per creare le classi QEntity da utilizzare con QueryDSL.

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

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

Ho appena scritto un semplice generatore di codice per il mio progetto.

È una singola classe e può generare modello di codice modello/dao/servizio/livello di azione per 1 o più modelli in un'unica esecuzione.

Dipendenza:

Si usa codemodel e apache commons-io lib, ed è un progetto di spring + springMVC.

Come si usa:

E 'importare qualche classe di base/interfaccia nel mio progetto, da cui la classe generata estende/implementa da, così si potrebbe non è possibile eseguire direttamente. Ma puoi crearli come classe/interfaccia vuota, o rimuoverli dalla funzione genSourceXxx().

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); 
    } 
} 
Problemi correlati