前言
在没有学习spring的时候,通常我们采用servlet来截取请求,然后处理请求获得响应,通常是一个请求地址一个servlet,特别的麻烦,而在spring中利用Controller和Mappering注解来标识,容器会自动解析。
通过本片文章,可以理解spring底层的一些原理和思想。
本文通过代码来解释,如何利用反射来获取注解信息,然后处理请求,减少代码的开发。
自定义注解来标识Controller层和Servlet请求
首先做一些解释
注解@Controller
用来标识controller层
注解@RequestMapping
来标识servlet请求
Annotation 其实就是代码里的特殊标记,这些标记可以编译,类加载,运行时被读取,并执行相应的处理。
Annotation 可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明,这些信息被保存在Annotation的 ‘’ name = value” 对中。
使用Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素。
自定义注解
@Controller
代码:
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;
}
利用反射获取注解信息,分发请求
流程:
- 获取所有的请求地址
- 首先得到servlrt的包名地址
- 得到包下的所有文件
- 给文件加上包名,利用反射得到类
- 判断是否有controller注解,说明是controller层,类中是请求处理的代码。
- 如果有注解,得到类的实例。则得到类的所有方法
- 判断方法时候又requsetmapper注解 有则代表为servlet
- 将地址,和 实例和方法存入map , 地址由注解得到。
- 收到客户端的请求后
- 得到项目名
- 根据请求地址将项目名替换掉 只留下servlet请求地址
- 根据地址 判断map中是否由对应的servlet
- 如果由 执行方法 返回gson
首先这和类是一个Servlet类,用来拦截所有的请求,然后分发给特定的具有RequestMapping 注解。
定义一个内部类,来存储 请求地址,方法(就是处理请求的方法),对象(就是具有@contrller标识的类,用来执行方法)
java">@Data
@AllArgsConstructor
@NoArgsConstructor
static class MapperHander{
String uri;
Object target;
Method method;
}
获取所有的请求地址
利用sevlet的初始化init方法,来解析所有的请求地址,和方法名,利用Map来存储对应的请求地址和对象,以便于拦截请求之后进行分发处理。
- 首先得到controller层的地址
java">String path = this.getClass().getResource("../").toURI().getPath() + "controller";
- 根据地址得到controller层下的所有类的类名
java">File file = new File(path);
String[] fileNames = file.list();
- 得到完整的类名,包名+类名
java">String className = "com.dyit.bims.controller."+fileName.replace(".class", "");
- 利用反射得到类对象
java">Class<?> clazz = Class.forName(className);
- 根据类对象得到标识由@Controller的类。
java">Controller controller = clazz.getDeclaredAnnotation(Controller.class);
- 如果为null,则判断下一个类,如果不为null,则根据类对象得到相应的实例(用来执行方法)和所有的方法(用来判断是否为处理请求的方法)。
java">Object target = clazz.newInstance();
Method[] methods = clazz.getDeclaredMethods();
- 判断方法是否是处理请求的方法即是否有@RequestMapping 注解
java">RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class);
- 如果有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();
};
}
处理请求
- 根据Request请求来得到请求地址
java">String projectName = request.getContextPath();
String key = request.getRequestURI().replace(projectName, "");
- 判断是否有对应的请求地址
java">MapperHander mh = map.get(key);
- 如果有则取出 相应的实例,来执行方法,根据返回的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;
}
}