扫描实现 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; } }
扫描类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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:woorkspaceservletdemojavaweb-servlet-demouildclasses // 使用 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); } } }
过滤器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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