1. 程式人生 > >從零寫一個Java WEB框架(四)框架的演進

從零寫一個Java WEB框架(四)框架的演進

  • 該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。
  • 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。
  • 每一篇,都是針對專案的不足點進行優化的。
  • 專案已放上github

本篇

  • 專案現在也跑起來了,而且Server和Dao層都已經封裝好了,都可以專心處理各自的業務邏輯,耦合度也低了很多。
  • 但是專案還是有很多需要改進的地方。
    例如:

    1. Controller 層 的耦合度還是非常高。首先請求由doGet()和doPost()獲取,然後從訊息頭裡面獲取url或者引數,然後進行邏輯判斷是需要進行哪些業務。當業務多的時候,Controller層就會變得很臃腫,而且耦合度很高。
      image.png

    2. 層與層之間的呼叫還需要手動New物件,這裡也可以實現”控制反轉”的思想。

框架實現

專案結構
我還是決定在原來的專案上進行開發。所以前期框架的封裝程式碼會與業務程式碼放在同一個包下,等開發完成,再抽取出來,我覺得這樣會更加容易理解。
專案結構:
image.png

  • annotation : 元註解。自定義的一些註解。
  • config:配置,主要存放定義了框架配置的Key.(根據key來獲取properties檔案的values)
  • Helper: 對工具類,配置類 的封裝。
  • Util : 工具類。

ClassUtil類
一個類的載入器,該類主要根據類名,或者包名來載入類。

public class ClassUtil {

    private static final Logger log = LoggerFactory.getLogger(ClassUtil.class);


    /*
    *  獲取類載入器
    * */
    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    /*
    *  載入類
    * */

    public static
Class<?> loadClass(String className, boolean isInitialized) { Class<?> cls=null; try { cls = Class.forName(className, isInitialized, getClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } return cls; } /* * 獲取指定包名下的所有類 * */ public static Set<Class<?>> getClassSet(String packageName) { Set<Class<?>> classSet = new HashSet<>(); try { // 獲取到包名下所有類的URL Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/")); // 開始遍歷 while (urls.hasMoreElements()) { URL url=urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals("file")) { String packagePath = url.getPath().replaceAll("%20", " "); addClass(classSet, packagePath, packageName); } else if (protocol.equals("jar")) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); if (jarURLConnection != null) { JarFile jarFile = jarURLConnection.getJarFile(); if (jarFile != null) { Enumeration<JarEntry> jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String name = jarEntry.getName(); if (name.endsWith(".class")) { String className = name.substring(0, name.lastIndexOf(".")).replaceAll("/", "."); doAddClass(classSet,className); } } } } } } } } catch (IOException e) { log.error("獲取類失敗",e); e.printStackTrace(); } return classSet; } private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) { //在該路徑下獲取所有檔案 //FileFilter過濾器,只要class檔案和文件。 File[] files = new File(packagePath).listFiles(new FileFilter() { @Override public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); } }); //遍歷每個檔案 for (File file : files) { String fileName = file.getName(); if (file.isFile()) { //去掉.class 字尾 String className = fileName.substring(0, fileName.lastIndexOf(".")); //如果包名不是空的 則加上包名 if (StringUtils.isNotEmpty(packageName)) { className = packageName + "." + className; } //載入類 doAddClass(classSet, className); } else { //這裡是對file 是資料夾 進行的操作 String subPackagePath = fileName; if (StringUtils.isNotEmpty(packagePath)) { subPackagePath = packagePath + "/" + subPackagePath; } String subPackageName = fileName; if (StringUtils.isNotEmpty(packageName)) { subPackageName = packageName + "." + subPackageName; } addClass(classSet,subPackagePath,subPackageName); } } } //真正的載入類 private static void doAddClass(Set<Class<?>> classSet, String className) { Class<?> cls = loadClass(className, false); classSet.add(cls); } }

建立元註解
image.png

Action 註解


/*
*  Action 方法註解
*
* */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {

    /*
     *  請求型別與路徑
     * */
    String value();

}

定義配置檔案的key ConfigConstant
定義這些key,根據這些key去獲取properties檔案的values;

/*
*  定義配置檔案的key
* */
public enum ConfigConstant {
    CONFIG_FILE("config.properties"),


    JDBC_DRIVER("jdbc.driver"),
    JDBC_URL("jdbc.url"),
    JDBC_USERNAME("jdbc.username"),
    JDBC_PASSWORD("jdbc.password"),

    APP_BASE_PACKAGE("base_package"),
    APP_JSP_PATH("jsp_path"),
    ASSET_PATH("asset_path"),
    ;


    private final String value;

    ConfigConstant(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

}

ClassHelper 類操作的組手類
主要是將類根據註解來進行分類


/*
 *  類操作 助手類
 * */
public class ClassHelper {

    /*
     *  定義類集合(用於存放所載入的類)
     * */
    private static final Set<Class<?>> CLASS_SET;

    static {
        String basePackage = ConfigHelper.getAppBasePackage();
        CLASS_SET = ClassUtil.getClassSet(basePackage);
    }


    /*
     *  獲取應用包下的所有類
     * */
    public static Set<Class<?>> getClassSet() {
        return CLASS_SET;
    }

    /*
     *
     *  獲取應用包名下所有Service類
     * */
    public static Set<Class<?>> getServiceClassSet() {
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for (Class<?> cls : CLASS_SET) {
            if (cls.isAnnotationPresent(Service.class)) {
                classSet.add(cls);
            }
        }
        return classSet;
    }


    /*
     *  獲取應用包下所有Controller類
     * */
    public static Set<Class<?>> getControllerClassSet() {
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for (Class<?> cls : CLASS_SET) {
            if (cls.isAnnotationPresent(Controller.class)) {
                classSet.add(cls);
            }
        }
        return classSet;
    }

    /*
     *  獲取應用包下所有Bean類 (包括: Service,Controller等)
     *
     * */
    public static Set<Class<?>> getBeanClassSet() {
        Set<Class<?>> beanClassSet = new HashSet<>();
        beanClassSet.addAll(getServiceClassSet());
        beanClassSet.addAll(getControllerClassSet());
        return beanClassSet;
    }

}

ConfigHelper 類
獲取配置檔案的values

/*
*  配置載入類
*  載入屬性檔案
* */
public class ConfigHelper {

    private static final Properties CONFIG_PROPS =
            PropsUtil.loadProps(ConfigConstant.CONFIG_FILE.getValue());

    /*
    *  獲取JDBC 驅動
    * */

    public static String getJdbcDriver() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_DRIVER.getValue());
    }

    /*
    *  獲取JDBC URL
    * */

    public static String getJdbcUrl() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_URL.getValue());
    }

    /*
    *  獲取 JDBC 使用者名稱
    * */
    public static String getJdbcUsername() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_USERNAME.getValue());
    }

    /*
    *
    *  獲取 JDBC 密碼
    * */

    public static String getJdbcPassword() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_PASSWORD.getValue());
    }


    /*
    *
    *  獲取基礎包
    * */
    public static String getAppBasePackage() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_BASE_PACKAGE.getValue());
    }


    /*
    *  獲取應用JSP路徑
    * */

    public static String getAppJspPath() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_JSP_PATH.getValue(), "/WEB-INF/view/");
    }

    /*
     *  獲取靜態資源路徑
     * */
    public static String getAppAssetPath() {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.ASSET_PATH.getValue(), "/asset/");
    }
}

總結

框架的基礎就準備好了。
核心是ClassUtil類,類載入工具類。是框架的核心,實現控制反轉的第一步。