掃描實現 Ioc 動態注入
參考:
http://www.private-blog.com/2017/11/16/java-%e6%89%ab%e6%8f%8f%e5%ae%9e%e7%8e%b0-ioc-%e5%8a%a8%e6%80%81%e6%b3%a8%e5%85%a5/
實現思路:
1.首先要通過 IO 去找到指定的路徑(當前類的全路徑)下的所有的 class文件;
2.通過反射機制 使用文件的全路徑來 初始化對象;
3.接下來判斷這個對象是否有使用了自定義的注解,如果有就保存到 classMap (類容器)中緩存起來;
4.類保存好以后在把方法的名稱也 緩存到 methodMap(方法容器)中,方便反射調用。
實體bean的注解類
/** * 自定義注解 */ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) /** 這種類型的Annotations將被JVM保留,所以他們能在運行時被JVM或其他使用反射機制的代碼所讀取和使用. */ @Target(ElementType.TYPE) /** 此注解應用於 類上. */ @Documented /** 注解表明這個注解應該被 javadoc工具記錄. */ public @interface CustomAnnotationBean { /** * 描述 */ String description() default ""; }
實體bean的方法的注解類
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義注解 */ @Retention(RetentionPolicy.RUNTIME) /** 這種類型的Annotations將被JVM保留,所以他們能在運行時被JVM或其他使用反射機制的代碼所讀取和使用. */ @Target(ElementType.METHOD) /** 此注解應用於 方法上. */ @Documented /** 注解表明這個注解應該被 javadoc工具記錄. */ public @interface CustomAnnotationMethod { /** * 描述 */ String description() default ""; /** * 訪問路徑 * * @return */ String uri(); }
實體beanT1
@CustomAnnotationBean public class T1 { private String id; private String name; @CustomAnnotationMethod(uri = "t1/getId") public String getId() { System.out.println("進入==========t1/getId=========方法"); return id; } public void setId(String id) { this.id = id; } @CustomAnnotationMethod(uri = "t1/getName") public String getName() { System.out.println("進入==========t1/getName=========方法"); return name; } public void setName(String name) { this.name = name; } }
實體beanT2
@CustomAnnotationBean public class T2 { private String id; private String name; @CustomAnnotationMethod(uri = "t2/getId") public String getId() { System.out.println("進入==========t2/getId=========方法"); return id; } public void setId(String id) { this.id = id; } @CustomAnnotationMethod(uri = "t2/getName") public String getName() { System.out.println("進入==========t2/getName=========方法"); return name; } public void setName(String name) { this.name = name; } }
掃描類

import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 掃描類 * * @author Administrator * */ public class JavaScan { /** * 存放 文件根路徑 */ static String classPath; /** * 存放結果 */ static List<String> classPathList = new ArrayList<>(); /** * 使用類全名定義的bean容器 */ static Map<String, Class<?>> beans = new HashMap<>(); /** * 使用方法的注解的url屬性定義的classes容器 */ static Map<String, Class<?>> classes = new HashMap<>(); /** * method 容器 */ static Map<String, Method> methods = new HashMap<>(); /** * 初始化 * * @return * @throws Exception */ public static void init(String path) throws Exception { if (null == path) throw new NullPointerException("JavaScan.init(String path) 參數不能為null"); // 初始化 獲取項目的 classPath 路徑,File.separator:\ classPath = new File(getRootPath()).getPath() + File.separator; // 總結:String classPathNoseparator = new File(rootPath).getPath() // 結果:G:\woorkspace\servletdemo\javaweb-servlet-demo\build\classes // 使用 IO掃描 指定路徑下的所有文件 getFileName(classPath + path); // 使用 所有類命名字符串來 初始化容器 initContainer(); } /** * 獲取rootPath的相關的根路徑<br> * getResources("") 如果放為空串兒,那么就是獲取rootPath的相關的根路徑 * * @return rootPath的相關的根路徑 * @throws Exception */ private static String getRootPath() throws Exception { // 注: main方法啟動 獲取路徑方式 // Enumeration<URL> resources = JavaScan.class.getClassLoader().getResources(""); // 注: servlet啟動 獲取路徑方式 Enumeration<URL> resources = JavaScan.class.getClassLoader().getResources("/"); URL url = resources.nextElement(); return url.getPath(); // 總結:String rootPath = this.getClass().getClassLoader().getResources("").nextElement().getPath() // 結果:/G:/woorkspace/servletdemo/javaweb-servlet-demo/build/classes/ } /** * 使用 IO掃描 Class文件 */ private static void getFileName(String rootPath) { File file = new File(rootPath); // 獲取所有文件和文件夾 File[] fileList = file.listFiles(); for (int i = 0; null != fileList && i < fileList.length; i++) { String path = fileList[i].getPath(); // 如果是目錄 if (fileList[i].isDirectory()) { // 繼續遞歸 getFileName(path); } if (fileList[i].isFile()) { // 輸出 所有文件夾下的全路徑 // System.out.println(path); // 拼接類路徑保存到集合中,(類路徑所指的是 去掉根路徑以外的項目中 class的全路徑) // path.replace(fileRootPath, "") 去除根路徑 // .replaceAll(".class", "") 去掉后綴名 // .replaceAll(File.separator + File.separator, ".") 將斜杠'\或/'轉成 // 點'.' String classpath = path.replace(classPath, "").replaceAll(".class", "") .replaceAll(File.separator + File.separator, "."); classPathList.add(classpath); } } } /** * 使用 所有類全命名來 初始化容器 * * @throws Exception */ private static void initContainer() throws Exception { if (null == classPathList || classPathList.size() <= 0) throw new Exception("文件路徑不存在!" + JavaScan.class.getName()); // 獲取 所有類的類全名 for (int i = 0; i < classPathList.size(); i++) { String className = classPathList.get(i); Class<?> forName = Class.forName(className); // 初始化限制,初始化的文件類型必須是 class文件 if (!forName.isAnnotation() && !forName.isEnum() && !forName.isInterface()) { // 只初始化 實現了CustomAnnotationBean注解的類 if (forName.isAnnotationPresent(CustomAnnotationBean.class)) { // 初始化類對象 添加到容器中 if (!beans.containsKey(className)) beans.put(className, forName); } // 只初始化 實現了CustomAnnotationBean注解的類中的方法 Method[] methodArray = forName.getDeclaredMethods(); for (Method method : methodArray) { // 初始化 實現了CustomAnnotationMethod注解的方法 if (method.isAnnotationPresent(CustomAnnotationMethod.class)) { // 獲取注解 CustomAnnotationMethod annotation = method.getAnnotation(CustomAnnotationMethod.class); // 獲取注解的屬性 String attr = annotation.uri(); if (!methods.containsKey(attr)) { // 初始化方法 添加到容器中 methods.put(attr, method); // 將此方法對應的類 添加到容器中 classes.put(attr, forName); } } } } } } /** * 執行 method * * @param url * @return * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws IllegalArgumentException */ public Object executeMethod(String url, Object... args) throws InvocationTargetException, IllegalAccessException, IllegalArgumentException, InstantiationException { if (null == url || "".equals(url)) throw new NullPointerException("ApiPool.executeMethod(String url):參數不能為null"); return methods.get(url).invoke(classes.get(url).newInstance(), args); } /** * 獲取 使用類全名定義的bean容器 * * @return */ public Map<String, Class<?>> getBeans() { return beans; } /** * 獲取 使用類全名定義的bean * * @return */ public Class<?> getBean(String key) { return beans.get(key); } /** * 獲取 使用方法的注解的url屬性定義的classes容器 * * @return */ public Map<String, Class<?>> getClazzs() { return classes; } /** * 獲取 使用方法的注解的url屬性定義的classes * * @return */ public Class<?> getClazz(String key) { return classes.get(key); } /** * 獲取Method容器 * * @return */ public Map<String, Method> getMethods() { return methods; } /** * 獲取Method * * @return */ public Method getMethod(String key) { return methods.get(key); } /** * 測試 * * @param args * @throws Exception */ public static void main(String[] args) throws Exception { JavaScan javaScan = new JavaScan(); // 在main 方法中調用傳空串就可以 javaScan.init(""); for (String key : javaScan.getBeans().keySet()) { Object object = javaScan.getBean(key); System.out.println(object); } } }
過濾器

import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.JSONObject; public class AnnotationHandleFilter implements Filter { private ServletContext servletContext = null; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("---進入注解處理過濾器---"); // 將ServletRequest強制轉換成HttpServletRequest HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; // 初始化方法init()中注入類的容器 Map<String, Class<?>> classMap = (Map<String, Class<?>>) servletContext.getAttribute("servletClassMap"); // 獲取contextPath:/javaweb-servlet-demo String contextPath = req.getContextPath(); // 獲取用戶請求的URI資源:/javaweb-servlet-demo/t1/getId String uri = req.getRequestURI(); // 截取項目名后的uri如有的話:t1/getId String reqUri = uri.substring(contextPath.length() + 1); // 如果請求了某種帶自定義注解的類的方法 if (!"".equals(reqUri)) { // 獲取要使用的類 Class<?> clazz = classMap.get(reqUri); // 創建類的實例 Method[] methods = clazz.getMethods(); // 循環執行方法 for (Method method : methods) { if (method.getName().contains("get")) { try { // 創建類的實例 Object o = clazz.newInstance(); // 執行方法 method.invoke(o); } catch (InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException | InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } // 返回頁面 res.setCharacterEncoding("UTF-8"); res.setContentType("application/json; charset=utf-8"); PrintWriter out = null; // 返回json對象 JSONObject json = new JSONObject(); try { // 返回json json.put("code", "200"); json.put("msg", "執行成功"); out = res.getWriter(); out.append(json.toString()); } catch (Exception e) { e.printStackTrace(); res.sendError(500); } } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("---AnnotationHandleFilter過濾器初始化開始---"); servletContext = filterConfig.getServletContext(); Map<String, Class<?>> classMap = new HashMap<String, Class<?>>(); addServletClassToServletContext(classMap); System.out.println("----AnnotationHandleFilter過濾器初始化結束---"); } private void addServletClassToServletContext(Map<String, Class<?>> classMap) { try { JavaScan.init(""); Map<String, Class<?>> classes = JavaScan.classes; for (Map.Entry<String, Class<?>> entry : classes.entrySet()) { String key = entry.getKey(); Class<?> value = entry.getValue(); System.out.println("Key = " + key + ", Value = " + value); classMap.put(key, value); servletContext.setAttribute("servletClassMap", classMap); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>javaweb-servlet-demo</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- <servlet> <servlet-name>ServletDemo</servlet-name> <servlet-class>com.demo.ServletDemo</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/servlet/ServletDemo</url-pattern> </servlet-mapping> --> <filter> <description>注解處理過濾器</description> <filter-name>AnnotationHandleFilter</filter-name> <filter-class>com.demo.AnnotationHandleFilter</filter-class> <init-param> <description>配置要掃描包及其子包, 如果有多個包,以逗號分隔</description> <param-name>basePackage</param-name> <param-value>com.demo</param-value> </init-param> </filter> <filter-mapping> <filter-name>AnnotationHandleFilter</filter-name> <!-- 攔截后綴是.do的請求 --> <!-- <url-pattern>*.do</url-pattern> --> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
git地址:
https://github.com/yuki9467/javaweb-scanner-demo