自定義Java web框架(二)
阿新 • • 發佈:2018-12-17
接續上一章自定義Java web框架(一) 的內容,這章主要是講解如何實現類載入器,實現個Bean容器,用於生產Bean例項。 先看檔案目錄如下: 做開發思路是很重要的。首先,我們的目的是要獲取Bean的例項,這樣就需要一個Bean的容器,通過這個容器動態的獲取Bean例項,如何實現這個Bean的容器呢?Java中使用反射的方式得到Bean例項放置到Map容器中。如何得到Bean例項呢?參考Spring框架中的Controller層、Service層等都是Bean例項,因此這裡定義了幾個Controller、Service的註解,作為Bean例項。同時為了動態獲取Bean例項,自己實現一個類載入器。這樣,思路已經很清晰了吧。 為了易於閱讀,下面畫個主要的類關係,其中每個類的主要方法寫出來。
public class ClassUtil { private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class); private static final char DOT_CHAR = '.'; private static final char SLASH_CHAR = '/'; /** * 獲取類載入器 * @return */ public static ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } /** * 載入類 * @param className * @param isInitialized * @return */ public static Class<?> loadClass(String className, boolean isInitialized) { Class<?> cls; try { cls = Class.forName(className, isInitialized, getClassLoader()); } catch (ClassNotFoundException e) { LOGGER.error("load class failure", e); throw new RuntimeException(e); } return cls; } /** * 獲取指定包下的所有類 * @param packageName * @return */ public static Set<Class<?>> getClassSet(String packageName) { Set<Class<?>> classSet = new HashSet<>(); try { Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(DOT_CHAR, SLASH_CHAR)); 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); } } } } catch (Exception e) { LOGGER.error("get class set failure", e); throw new RuntimeException(e); } return classSet; } /** * 新增類 * @param classSet * @param packagePath * @param packageName */ private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) { 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()) { String className = fileName.substring(0, fileName.lastIndexOf(".")); if (StringUtils.isNotEmpty(packageName)) { className = packageName + "." + className; } doAddClass(classSet, className); } else { String subPackagePath = fileName; if (StringUtils.isNotEmpty(packagePath)) { subPackagePath = packagePath + "/" + subPackagePath; } String subPackageName = fileName; if (StringUtils.isNotEmpty(packageName)) { subPackageName = packageName + "/" + subPackageName; } addClass(classSet, subPackagePath, subPackageName); } } } /** * 類放置到set * @param classSet * @param className */ private static void doAddClass(Set<Class<?>> classSet, String className) { Class<?> cls = loadClass(className, false); classSet.add(cls); } }
然後定義幾個常用註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
String value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Inject { }
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
定義一個幫助類
public final class ClassHelper {
/**
* 定義類集合(用於存放所載入的類)
*/
private static final Set<Class<?>> CLASS_SET;
static {
String basePackage = ConfigHelper.getAppBasePackage() ;
CLASS_SET = ClassUtil.getClassSet(basePackage);
}
/**
* 獲取應用包名下的所有類
* @return
*/
public static Set<Class<?>> getClassSet() {
return CLASS_SET;
}
/**
* 獲取應用包下的所有service類
* @return
*/
public static Set<Class<?>> getServiceClassSet() {
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> cls: CLASS_SET) {
if (cls.isAnnotationPresent((Service.class))) {
classSet.add(cls);
}
}
return classSet;
}
/**
* 獲取應用包名下的所有controller類
* @return
*/
public static Set<Class<?>> getControllerClassSet() {
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(Controller.class)) {
classSet.add(cls);
}
}
return classSet;
}
/**
* 獲取應用包下所有的bean類(包括:Service。Controller 等)
* @return
*/
public static Set<Class<?>> getBeanClassSet() {
Set<Class<?>> beanClassSet = new HashSet<>();
beanClassSet.addAll(getControllerClassSet());
beanClassSet.addAll(getServiceClassSet());
return beanClassSet;
}
使用反射獲取Bean例項
public final class ReflectionUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);
/**
* 建立例項
*/
public static Object newInstance(Class<?> cls) {
Object instance;
try {
instance = cls.newInstance();
} catch (Exception e) {
LOGGER.error("new instance failure", e);
throw new RuntimeException(e);
}
return instance;
}
/**
* 呼叫方法
*/
public static Object invokeMethod(Object obj, Method method, Object...args) {
Object result;
try {
method.setAccessible(true);
result = method.invoke(obj, args);
} catch (Exception e) {
LOGGER.error("invoke method failure", e);
throw new RuntimeException(e);
}
return result;
}
/**
* 設定成員變數的值
*/
public static void setField(Object obj, Field field, Object value) {
try {
field.setAccessible(true);
field.set(obj, value);
} catch(Exception e) {
LOGGER.error("set field failure", e);
throw new RuntimeException(e);
}
}
}```
定義Bean的幫助類
public class BeanHelper {
/**
* 定義Bean對映(用於存放Bean類與Bean例項的對映關係)
*/
private static final Map<Class<?>, Object> BEAN_MAP = new HashMap<Class<?>, Object>();
static {
Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
for (Class<?> beanClass : beanClassSet) {
Object obj = ReflectionUtil.newInstance(beanClass);
BEAN_MAP.put(beanClass, obj);
}
}
/**
* 獲取Bean對映
* @return
*/
public static Map<Class<?>, Object> getBeanMap() {
return BEAN_MAP;
}
/**
* 獲取Bean例項
* @param cls
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> cls) {
if (!BEAN_MAP.containsKey(cls)) {
throw new RuntimeException("can not get bean by class: " + cls);
}
return (T)BEAN_MAP.get(cls);
}
}
[程式碼地址](https://github.com/lyin226/custom-framework)如果本文對您有幫助, 動動小手給個star。