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 ...
随机推荐
- centos7安装与配置nginx1.11,开机启动
1.官网下载安装包 http://nginx.org/en/download.html,选择适合Linux的版本,这里选择最新的版本,下载到本地后上传到服务器或者centos下直接wget命令下载. ...
- Sql查询某个字段是否包含小写字母
SELECT * from student where username COLLATE Chinese_PRC_CS_AS LIKE '%[abcdefghijklmnopqrstuvwxyz]%'
- 第31月第19天 NV12
1. //设置CIContext,并从CIImage -> CGImage -> UIImage CIContext *context = [CIContext contextWithOp ...
- 关于Java的volatile
volatile的作用 1.防止指令重排序 首先要理解什么是指令重排序?指令重排序的利弊?后续举例说明 2.多线程访问共享资源时,缓解synchronized重量级锁带来的性能问题 但是volatil ...
- Java多线程访问共享资源类及类之间关系设计
1.涉及的类 多线程类.共享资源存储类 2.类之间的关系 (1)共享资源存储类作为线程类的全局成员变量,在线程初始化时,通过setter或者构造注入(当然此处是同一个共享资源类对象),实现多个线程共享 ...
- Luogu P1082 同余方程(NOIP 2012) 题解报告
题目传送门 [题目大意] 求关于x的同余方程 ax≡1(mod b)的最小整数解. [思路分析] 由同余方程的有关知识可得,ax≡1(mod b)可以化为ax+by=1,此方程有解当且仅当gcd(a, ...
- day 19 - 1 模块
collections 模块 在内置数据类型(dict.list.set.tuple)的基础上,collections 模块还提供了几个额外的数据类型:Counter.deque.defaultdic ...
- kalilinux渗透测试笔记
声明:本文理论大部分是苑房弘kalilinux渗透测试的内容 第五章:基本工具 克隆网页,把gitbook的书记下载到本地 httrack "http://www.mybatis.org/m ...
- SVM小白教程(2):拉格朗日对偶
在上一篇文章中,我们推导出了 SVM 的目标函数: \[ \underset{(\mathbf{w},b)}{\operatorname{min}} ||\mathbf{w}|| \\ \operat ...
- docker保存日志文件到本地
其实很简单 docker logs +你需要添加的额外参数 + 容器id >文件名称 然后查看这个文件就可以了,也可以通过ftp协议下载到本地