1. 程式人生 > >自定義Java web框架(二)

自定義Java web框架(二)

接續上一章自定義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。