從零寫一個Java WEB框架(四)框架的演進
阿新 • • 發佈:2018-11-21
- 該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。
- 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。
- 每一篇,都是針對專案的不足點進行優化的。
- 專案已放上github
本篇
- 專案現在也跑起來了,而且Server和Dao層都已經封裝好了,都可以專心處理各自的業務邏輯,耦合度也低了很多。
但是專案還是有很多需要改進的地方。
例如:Controller 層 的耦合度還是非常高。首先請求由doGet()和doPost()獲取,然後從訊息頭裡面獲取url或者引數,然後進行邏輯判斷是需要進行哪些業務。當業務多的時候,Controller層就會變得很臃腫,而且耦合度很高。
層與層之間的呼叫還需要手動New物件,這裡也可以實現”控制反轉”的思想。
框架實現
專案結構
我還是決定在原來的專案上進行開發。所以前期框架的封裝程式碼會與業務程式碼放在同一個包下,等開發完成,再抽取出來,我覺得這樣會更加容易理解。
專案結構:
- 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);
}
}
建立元註解
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類,類載入工具類。是框架的核心,實現控制反轉的第一步。