spring如何利用反射获取注解信息来解析请求地址

news/2024/5/19 6:03:41 标签: spring, java, 后端, 反射, 注解

文章目录

  • 前言
  • 自定义注解来标识Controller层和Servlet请求
  • 利用反射获取注解信息,分发请求
    • 获取所有的请求地址
    • 处理请求

前言

在没有学习spring的时候,通常我们采用servlet来截取请求,然后处理请求获得响应,通常是一个请求地址一个servlet,特别的麻烦,而在spring中利用ControllerMappering注解来标识,容器会自动解析。

框架 = 注解 + 反射 + 设计模式

通过本片文章,可以理解spring底层的一些原理和思想。

本文通过代码来解释,如何利用反射来获取注解信息,然后处理请求,减少代码的开发。

自定义注解来标识Controller层和Servlet请求

首先做一些解释
注解 @Controller 用来标识controller层
注解 @RequestMapping 来标识servlet请求

Annotation 其实就是代码里的特殊标记,这些标记可以编译,类加载,运行时被读取,并执行相应的处理。

Annotation 可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明,这些信息被保存在Annotation的 ‘’ name = value” 对中。

使用Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素。

自定义注解

@Controller

  • 使用 @interface 关键字 来定义新的注解
  • 自定义注解自动继承了 java.lang.annotation.Annotation接口

代码:

java">package com.dyit.bims.annotation;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 此注解表示一个controller类
 * 表示是servlet
 * @author strve
 *
 */
@Retention(RUNTIME)
@Target(TYPE)
public @interface Controller {

}

@Retention(RUNTIME) 在运行时有效
@Target(TYPE) 用于描述类、接口、或enum声明

@RequestMapping

代码:

java">import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 前端发起的请求地址注解
 * @author strve
 *
 */
@Retention(RUNTIME)
@Target(METHOD)
public @interface RequestMapping {
	String value();
}

@Target(METHOD) 用于描述方法
String value() 相当于参数 ,请求地址

标识类

用来查询所有的Publisher
使用@Controller 和 @RequestMapping("/findallpublisher")来标识 ()中写的是请求地址。

如果还有其他的业务请求,则只需要给类中添加其他的方法即可,不需要在写其他的servlet,只需要给方法上加上@RequestMapping直接
代码:

java">package com.dyit.bims.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.dyit.bims.annotation.Controller;
import com.dyit.bims.annotation.RequestMapping;
import com.dyit.bims.dto.HttpResp;
import com.dyit.bims.entity.Publisher;
import com.dyit.bims.service.IPublisherService;

import com.dyit.bims.service.impl.PublisherServiceImpl;

@Controller
public class PublisherController {
	private ThreadLocal<IPublisherService> local = new ThreadLocal<IPublisherService>() {
		protected IPublisherService initialValue() {
			return new PublisherServiceImpl();
		};
	};

	@RequestMapping("/findallpublisher")
	public HttpResp findAllPublisher(HttpServletRequest request, HttpServletResponse response) {
		
		List<Publisher> list  = local.get().findAll();
		System.out.println(list);
		list.forEach(e->System.out.println(e));
		HttpResp httpResp = new HttpResp(111, "加载publisher_name成功", list);
		return httpResp;
	}
}

HttpResp类 返回的json对象内容

java">package com.dyit.bims.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpResp {
	private int code;
	private String msg;
	private Object results;
}

利用反射获取注解信息,分发请求

流程:

  • 获取所有的请求地址
  1. 首先得到servlrt的包名地址
  2. 得到包下的所有文件
  3. 给文件加上包名,利用反射得到类
  4. 判断是否有controller注解,说明是controller层,类中是请求处理的代码。
  5. 如果有注解,得到类的实例。则得到类的所有方法
  6. 判断方法时候又requsetmapper注解 有则代表为servlet
  7. 将地址,和 实例和方法存入map , 地址由注解得到。
  • 收到客户端的请求后
  1. 得到项目名
  2. 根据请求地址将项目名替换掉 只留下servlet请求地址
  3. 根据地址 判断map中是否由对应的servlet
  4. 如果由 执行方法 返回gson

首先这和类是一个Servlet类,用来拦截所有的请求,然后分发给特定的具有RequestMapping 注解

在这里插入图片描述
定义一个内部类,来存储 请求地址方法(就是处理请求的方法),对象(就是具有@contrller标识的类,用来执行方法)

java">@Data
	@AllArgsConstructor
	@NoArgsConstructor
	static class MapperHander{
		String uri;
		Object target;
		Method method;
	}

获取所有的请求地址

利用sevlet的初始化init方法,来解析所有的请求地址,和方法名,利用Map来存储对应的请求地址和对象,以便于拦截请求之后进行分发处理。

  1. 首先得到controller层的地址

在这里插入图片描述

java">String path = this.getClass().getResource("../").toURI().getPath() +  "controller";
  1. 根据地址得到controller层下的所有类的类名
java">File file = new File(path);
String[] fileNames = file.list();
  1. 得到完整的类名,包名+类名
java">String className = "com.dyit.bims.controller."+fileName.replace(".class", "");
  1. 利用反射得到类对象
java">Class<?> clazz = Class.forName(className);
  1. 根据类对象得到标识由@Controller的类。
java">Controller controller = clazz.getDeclaredAnnotation(Controller.class);
  1. 如果为null,则判断下一个类,如果不为null,则根据类对象得到相应的实例(用来执行方法)和所有的方法(用来判断是否为处理请求的方法)。
java">Object target = clazz.newInstance();
Method[] methods = clazz.getDeclaredMethods();
  1. 判断方法是否是处理请求的方法即是否有@RequestMapping 注解
java">RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class);
  1. 如果有RequestMapping 注解 说明这个方法是用来处理客户端请求的,根据 注解.value()方法拿到注解中的请求地址,将请求地址,实例对象,方法封装进MapperHander对象,最后存入Map中。
java">MapperHander mh = new MapperHander(rm.value(), target, method);
map.put(mh.getUri(), mh);

如上:就可以将所有的请求全部存进Map中,如果拦截到请求,根据map是否包含指定的地址来解析,根据实例来执行相应的方法即可。

完整代码:

java">public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		try {
			String path = this.getClass().getResource("../").toURI().getPath() +  "controller";
			System.out.println("path"+path);
			
			File file = new File(path);
			String[] fileNames = file.list();
			for (String fileName : fileNames) {
				String className = "com.dyit.bims.controller."+fileName.replace(".class", "");
				
				System.out.println("className"+className);
				Class<?> clazz = Class.forName(className);
				Controller controller = clazz.getDeclaredAnnotation(Controller.class);
				System.out.println(controller);
				if (controller!=null) {
					Object target = clazz.newInstance();
					Method[] methods = clazz.getDeclaredMethods();
					for (Method method : methods) {
						RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class);
						System.out.println(rm);
						if (rm!=null) {
							
							MapperHander mh = new MapperHander(rm.value(), target, method);
							
							map.put(mh.getUri(), mh);
						}
					}
				}
			}
		} catch (Exception e) {
			Logger.getLogger(this.getClass()).debug(e.getMessage());
			e.printStackTrace();
		};
	}

处理请求

  1. 根据Request请求来得到请求地址
java">String projectName = request.getContextPath();
		String key = request.getRequestURI().replace(projectName, "");
  1. 判断是否有对应的请求地址
java">MapperHander mh = map.get(key);
  1. 如果有则取出 相应的实例,来执行方法,根据返回的HttpResp来保证成json对象放回给客户端
java">if (mh!=null) {
			Method method = mh.getMethod();
			Object target = mh.getTarget();
			
			try {
				Object dto = method.invoke(target, request,response);
				response.getWriter().println(new Gson().toJson(dto));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

完整代码:

java">protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String projectName = request.getContextPath();
		String key = request.getRequestURI().replace(projectName, "");
		
		MapperHander mh = map.get(key);
		System.out.println("mh:"+mh);
		if (mh!=null) {
			Method method = mh.getMethod();
			Object target = mh.getTarget();
			
			try {
				Object dto = method.invoke(target, request,response);
				response.getWriter().println(new Gson().toJson(dto));
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

DispatcherServlet完整代码:

java">package com.dyit.bims.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.dyit.bims.annotation.Controller;
import com.dyit.bims.annotation.RequestMapping;
import com.google.gson.Gson;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * Servlet implementation class DispatcherServlet
 */
@WebServlet("/*")
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private ConcurrentHashMap<String, MapperHander> map = new ConcurrentHashMap<String, DispatcherServlet.MapperHander>();
	
			
	
	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		try {
			String path = this.getClass().getResource("../").toURI().getPath() +  "controller";
			System.out.println("path"+path);
			
			File file = new File(path);
			String[] fileNames = file.list();
			for (String fileName : fileNames) {
				String className = "com.dyit.bims.controller."+fileName.replace(".class", "");
				
				System.out.println("className"+className);
				Class<?> clazz = Class.forName(className);
				Controller controller = clazz.getDeclaredAnnotation(Controller.class);
				System.out.println(controller);
				if (controller!=null) {
					Object target = clazz.newInstance();
					Method[] methods = clazz.getDeclaredMethods();
					for (Method method : methods) {
						RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class);
						System.out.println(rm);
						if (rm!=null) {
							
							MapperHander mh = new MapperHander(rm.value(), target, method);
							
							map.put(mh.getUri(), mh);
						}
					}
				}
			}
		} catch (Exception e) {
			Logger.getLogger(this.getClass()).debug(e.getMessage());
			e.printStackTrace();
		};
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String projectName = request.getContextPath();
		String key = request.getRequestURI().replace(projectName, "");
		
		MapperHander mh = map.get(key);
		System.out.println("mh:"+mh);
		if (mh!=null) {
			Method method = mh.getMethod();
			Object target = mh.getTarget();
			
			try {
				Object dto = method.invoke(target, request,response);
				response.getWriter().println(new Gson().toJson(dto));
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
	
	@Data
	@AllArgsConstructor
	@NoArgsConstructor
	static class MapperHander{
		String uri;
		Object target;
		Method method;
	}

}


http://www.niftyadmin.cn/n/1440820.html

相关文章

解决玩游戏时显卡卡屏现象(转)

笔者朋友有一台计算机&#xff0c;具体配置为Pentium4 2.66GHz CPU、256M内存、80G硬盘&#xff0c;该计算机使用的显示卡为祺祥极风6600的AGP类型的显卡。最近使用这台计算机玩一些大型游戏程序时&#xff0c;常常会出现显示画面容易卡住现象&#xff0c;有的时候显示屏幕很长…

CMD命令全集(转)

2K和XP的CMD命令教程(命令篇)accwiz.exe > 辅助工具向导acsetups.exe > ACS setup DCOM server executableactmovie.exe > 直接显示安装工具append.exe > 允许程序打开制定目录中的数据arp.exe > 显示和更改计算机的IP与硬件物理地址的对应列表at.exe > 计划…

Vue3:在 VSCode 中如何成功安装 Mockjs 及成功引入 Mock 的详细过程

Ⅰ、Mock 简介&#xff1a; 1、什么是 Mock&#xff1f; 其一、Mock 的解释一&#xff1a; Mock 服务是指在测试过程中对于某些复杂&#xff08;或者不太好构造&#xff09;的对象&#xff0c;用一个虚拟的对象替代它&#xff1b;对于前端来说&#xff0c;就是后台数据还没有…

Thinkphp的SQL查询方式

一、普通查询方式 a、字符串$arr$m->where("sex0 and usernamegege")->find();b、数组$data[sex]0;$data[username]gege;$arr$m->where($data)->find();注意&#xff1a;这种方式默认是and的关系&#xff0c;如果使用or关系&#xff0c;需要添加如下数组…

通过老师的讲解,我明白了什么是IoC

文章目录前言Spring的容器&#xff08;bean&#xff09;Spring的IoC容器IoC的概念DI 依赖注入前言 今天讲到了spring框架&#xff0c;老师通过极少的语言让我明白了IoC产生的原因&#xff0c;为什么需要控制反转&#xff0c;依赖注入的原理。 本文就主要讲解Ioc和DI。 Sprin…

博客文章分类列表

最新推荐 APM系统SkyWalking介绍DevOps系列 开源推荐 | CoDo开源一站式DevOps平台我们自研的那些Devops工具Docker环境的持续部署优化实践探秘varian&#xff1a;优雅的发布部署程序中小团队基于Docker的devops实践中小团队落地配置中心详解中小团队快速构建SQL自动审核系统代码…

3G的市场状况和技术状况(转)

&#xff08;一&#xff09;3G的技术状况 首先&#xff0c;我们概括一下目前的移动通信标准和它们向3G演进的道路&#xff0c;未来3G的主要技术将是WCDMA和cdma2001x。其中&#xff0c;以TDMA技术为2G核心技术的运营商多数会在3G 核心频段上选择WCDMA技术&#xff0c;但如果不能…

暑假生活第五周总结

这一周和同学出去游玩了几天&#xff0c;所以进度慢下来了&#xff0c;不过这周开始尝试做课堂测试样卷了&#xff0c;遇到的问题不少&#xff1a; 1.get/set函数的定义不是很明白&#xff0c;在学长学姐的帮助下&#xff0c;完成了Student类的set/get定义 2.对Java语句的输入有…