- 该系列,其实是对《架构探险》这本书的实践。本人想记录自己的学习心得所写下的。
- 从一个简单的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类,类加载工具类。是框架的核心,实现控制反转的第一步。