Java 扫描实现 Ioc 动态注入,过滤器根据访问url调用自定义注解标记的类及其方法
扫描实现 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
Java 扫描实现 Ioc 动态注入,过滤器根据访问url调用自定义注解标记的类及其方法的更多相关文章
- java 编程基础:【注解】 提取注解信息,利用自定义注解编写测试类,注解绑定事件
提取注解信息 使用注解修饰了类.方法.成员变量等成员之后,这些注解不会自己生效,必须由开发者提供相应工具来提取并处理注解信息. Java使用java.lang.annotation.Annotat ...
- Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类
1. Android中的IOC(DI)框架 1.1 ViewUtils简介(xUtils中的四大部分之一) IOC: Inverse of Controller 控制反转. DI: Dependenc ...
- Spring IOC 依赖注入的两种方式XML和注解
依赖注入的原理 依赖注入的方式---XML配置 依赖注入的方式---注解的方式 Spring 它的核心就是IOC和AOP.而IOC中实现Bean注入的实现方式之一就是DI(依赖注入). 一 DI的原理 ...
- Java中两个线程是否可以同时访问同一个对象的两个不同的synchronized方法?
不可以!!! 多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本 ...
- spring 3.1.13中新增的util @value注解,给类或方法注入值
在spring 3.0以上版本中,可以通过使用@value,对一些如xxx.properties文件 ,进行键值对的注入,例子如下: 一.类变量注入 1 首先在applicationContext.x ...
- 【java基础】IOC介绍及其简单实现
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心. 控制反转一般分为两种类型,依赖注入 ...
- Java 自定义注解与注解解析实例
在学习Java之后会遇到很多的注解,有加载JavaBean的注解:@Component,@Service,@Controller:有获取配置文件中数值的注解@Value:有获取Http请求的数据的注解 ...
- 动态生成简约MVC请求接口|抛弃一切注解减少重复劳动吧
背景 目前创建一个后端请求接口给别人提供服务,无论是使用SpringMVC方式注解,还是使用SpringCloud的Feign注解,都是需要填写好@RequestMap.@Controller.@Pa ...
- 注解【介绍、基本Annotation、元Anntation、自定义注解、注入基本信息、对象】
什么是注解? 注解:Annotation-. 注解其实就是代码中的特殊标记,这些标记可以在编译.类加载.运行时被读取,并执行相对应的处理. 为什么我们需要用到注解? 传统的方式,我们是通过配置文件(x ...
随机推荐
- [图解Java]读写锁ReentrantReadWriteLock
图解ReentrantReadWriteLock 如果之前使用过读写锁, 那么可以直接看本篇文章. 如果之前未使用过, 那么请配合我的另一篇文章一起看:[源码分析]读写锁ReentrantReadWr ...
- Angular记录(10)
文档资料 速查表:https://www.angular.cn/guide/cheatsheet 风格指南:https://www.angular.cn/guide/styleguide Angula ...
- Angular记录(7)
文档资料 箭头函数--MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_fun ...
- js手写'Promise'
/* * pending:初始化成功 * fulfilled:成功 * rejected:失败 * */ function Promise(executor) {// 执行器 this.status ...
- JAVA的抽象类和接口
抽象类 在面向对象的概念中,所有的对象都是通过类来描述的,但是反过来,并不是所有的类都是用来描述对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类. 抽象类除了不能实例化对 ...
- P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm
对于一个牛,它存在两种状态:1.处于联通分量 2.不处于联通分量.对于处于联通分量的牛,求出联通分量的大小:对于不处于联通分量的牛,求出其距离联通分量的路程+联通分量大小. 不同的联通分量,染上不同的 ...
- PostgreSQL对汉字按拼音排序
转自:https://www.cnblogs.com/gaojian/p/3188609.html postgres=# \l List of databases Name | Owner | Enc ...
- 一个 戴尔 dell 笔记本 bios Preparing to begin setup 问题
昨天帮亲戚安装系统,是一个dell 笔记本,原本想的很简单,但是在修改了bios里的 SATA 模式后,不但系统启动不了,连bios都进不去了,就像一直在检测一个错误的硬件.google了很多,也没有 ...
- Eclipse install new software无反应
一个问题可以有不同的解决方案 其他人提供了不少方案 我遇到了这个问题 但是这些解决方案都无济于事 于是 我就采取了一个新方案: 然后重新解压,找到里面的eclipse.exe重新打开就可以了 现在有反 ...
- IntelliJ IDEA设置svn.exe的路径
安装TortoiseSVN客户端时,必须选中command line client tools这个选项