Подтвердить что ты не робот

Java. Избегайте создания пустого вспомогательного класса и интерфейса или создания шаблона исходного кода Java.

Я разрабатываю веб-проект java с использованием Spring и Mybatis.

На уровне dao я определил суперкласс и супер интерфейс, который реализовал все распространенные методы.
Таким образом, при создании подкласса или интерфейса для конкретной модели на уровне dao мне нужно только реализовать класс и интерфейс super dao и оставить тело класса и интерфейс пустым. Более половины класса и интерфейса уровня sub dao пуст все время.


(Пример пустого класса dao и интерфейса:)

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

Мой вопрос:

Есть ли хороший способ избежать написания этих пустых классов и интерфейса, хотя они могут их использовать?

Я собираюсь использовать Code generator для создания этого файла класса или использовать Java reflection для создания такого класса и интерфейса во время выполнения по мере необходимости, но пока не вдаваться в подробности.


@ Обновление

Кажется, нелегко достичь цели без создания исходного кода, поэтому я решил написать простой генератор исходного кода java для веб-проекта java.

И инструмент под названием codemodel очень подходит для этого, он разработан Sun и теперь принадлежит Oracle, я думаю.

И я дал ответ сам с кодом, который я написал, чтобы генерировать исходный код Java.

4b9b3361

Ответ 1

Примерно месяц назад я спрашивал себя то же самое:) Итак, кажется, что у нас есть своего рода решение, поскольку вы используете библиотеку Spring. Когда я прочитал docs:

Вместо использования объектов доступа к данным (DAO) вручную SqlSessionDaoSupport или SqlSessionTemplate, Mybatis- Spring предоставляет proxy factory: MapperFactoryBean. Этот класс позволяет вам вводить данные интерфейсы mapper непосредственно в вашу службу beans. При использовании карт вы просто называете их так, как вы всегда называли ваши DAO, но вы не потребуется кодировать какую-либо реализацию DAO, потому что MyBatis- Spring будет создайте для вас прокси.

Вот пример GitHub, а также на этом MyBatis ' страница.

Надеюсь, что это даст вам некоторые идеи, потому что, возможно, нецелесообразно реорганизовать всю вашу систему, чтобы получить выгоду от такой приятной функции.

Ответ 2

Классы репозитория для классов в наших проектах, которые используют QueryDSL и JPA, имеют интерфейс, но не реализацию. Однако он не отвечает на вопрос, можно ли напрямую создавать эти репозитории на основе классов сущностей, хотя это было бы похоже на то, что Apt Maven Plugin делает для создания классов QEntity для использования с QueryDSL.

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

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

Ответ 3

Я написал простой генератор кода для моего проекта.

Это всего лишь один класс и может генерировать шаблон кода модели/дао/службы/действия для 1 или более моделей за одно исполнение.

Зависимость:

Он использует codemodel и apache commons-io lib, а проект spring + springMVC.

Как его использовать:

Он импортирует некоторый базовый класс/интерфейс в мой проект, из которого сгенерированный класс расширяет/реализует, поэтому вы можете не запускать его напрямую. Но вы можете создать их как пустой класс/интерфейс или удалить их из функции 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);
    }
}