1 概述
1、简易的实现SpringMVC中注解类的加载,和IOC、DI的实现
2、实现DispatchServlet根据RequestMapping等注解标注路径的的请求分发
3、利用策略模式和反射对请求方法的参数的处理
2 代码实现
2.1 注解类准备
- EnjoyController
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({java.lang.annotation.ElementType.TYPE}) //作用范围:用在接口或类上
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented//明该注解将被包含在javadoc中 @Inherited:说明子类可以继承父类中的该注解
public @interface EnjoyController {
String value() default "";
}
- EnjoyQualifier
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyQualifier {
String value() default "";
}
- EnjoyRequestMapping
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyRequestMapping {
//路径EnjoyRequestMapping(value)
String value() default "";
}
- EnjoyRequestParam
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyRequestParam {
String value() default "";
}
- EnjoyService
@Target({java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyService {
String value() default "";
}
2.2 Controller、Service类准备
- Controller
@EnjoyController
@EnjoyRequestMapping("/james")
public class JamesController {
@EnjoyQualifier("JamesServiceImpl")//此处为模拟依赖注入
private JamesService jamesService;
@EnjoyRequestMapping("/query")
public void query(HttpServletRequest request, HttpServletResponse response,
@EnjoyRequestParam("name") String name,
@EnjoyRequestParam("age") String age) {
try {
PrintWriter pw = response.getWriter();
String result = jamesService.query(name,age);
pw.write(result);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- Service
@EnjoyService("JamesServiceImpl")
public class JamesServiceImpl implements JamesService {
public String query(String name, String age) {
return "{name="+name+",age="+age+"}";
}
public String insert(String param) {
// TODO Auto-generated method stub
return "insert successful.....";
}
public String update(String param) {
// TODO Auto-generated method stub
return "update successful.....";
}
}
2.3 DisPatchServlet类准备(重点)
/**
* Servlet implementation class DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
//承接需要IOC装载类的全类名
List classNames = new ArrayList();
//承接IOC实例化的bean对象
Map beans = new HashMap();
//承接Controller中方法和对应方法访问路径
Map handlerMap = new HashMap();
Properties prop = null;
private static String HANDLERADAPTER = "jamesHandlerAdapter";
/**
* Default constructor.
*/
public DispatcherServlet() {
// TODO Auto-generated constructor stub
}
/**
* @see Servlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
// 1、我们要根据一个基本包进行扫描,扫描里面的子包以及子包下的类
scanPackage("com.enjoy");
for (String classname : classNames) {
System.out.println(classname);
}
// 2、我们肯定是要把扫描出来的类进行实例化
instance();
for (Map.Entry entry : beans.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 3、依赖注入,把service层的实例注入到controller
ioc();
// 4、建立一个path与method的映射关系
HandlerMapping();
for (Map.Entry entry : handlerMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
/*
* InputStream is = this.getClass()
* .getResourceAsStream("/config/properties/spring.properties"); prop =
* new Properties(); try { prop.load(is); } catch (IOException e) {
* e.printStackTrace(); }
*/
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取到请求路径 /james-springmvc/james/query
String uri = request.getRequestURI();
//获取容器路径 /james-springmvc
String context = request.getContextPath();
//将 "/james-springmvc/james/query" 去掉"/james-springmvc"
String path = uri.replace(context, "");
//根据请求路径来获取要执行的方法
Method method = (Method) handlerMap.get(path);
//拿到控制类
JamesController instance = (JamesController) beans.get("/" + path.split("/")[1]);
//处理器(处理器也做为Spring内置对象被装进beans中,因为处理器类上也标注了@enjoyService注解)
HandlerAdapterService ha = (HandlerAdapterService) beans.get(HANDLERADAPTER);
/*
* @RequestMapping("/order")
* order(@RequestBody String params, @RequestHeader @RequestParam String param1){//参数有多种类型接收方式
* }
*/
//通过参数解析器将参数解析并获取对应参数的值,将参数放在args数组中返回,并传给反射方法来调用目标方法
Object[] args = ha.hand(request, response, method, beans);
try {
method.invoke(instance, args);
// method.invoke(instance, new
// Object[]{request,response,null});//拿参数
/*如果有多个参数类型,就得这样写了(可用策略模式,省去以下代码)
* if(ParamType == HttpServletRequest){
}else if(ParamType == @RquestHeader){
}else
*用策略模式实现(把粒度控制得更细),新建 JamesHandlerAdapter
*/
} 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();
}
}
/*
* 将目标方法和方法访问路径关系传入handlerMap中保存
*
*/
private void HandlerMapping() {
if (beans.entrySet().size() <= 0) {
System.out.println("没有类的实例化!");
return;
}
for (Map.Entry entry : beans.entrySet()) {
Object instance = entry.getValue();
Class> clazz = instance.getClass();
// 拿所有Controller的类
if (clazz.isAnnotationPresent(EnjoyController.class)) {
//@com.enjoy.james.annotation.EnjoyRequestMapping(value=/james)
EnjoyRequestMapping requestMapping = (EnjoyRequestMapping) clazz
.getAnnotation(EnjoyRequestMapping.class);
// 获取Controller类上面的EnjoyRequestMapping注解里的请求路径
String classPath = requestMapping.value();
// 获取控制类里的所有方法
Method[] methods = clazz.getMethods();
// 获取方法上的EnjoyRequestMapping设置的路径,与方法名建立映射关系
for (Method method : methods) {
//判断哪些方法上使用EnjoyRequestMapping路径注解
if (method.isAnnotationPresent(EnjoyRequestMapping.class)) {
//@com.enjoy.james.annotation.EnjoyRequestMapping(value=/query)
EnjoyRequestMapping methodrm = (EnjoyRequestMapping) method
.getAnnotation(EnjoyRequestMapping.class);
String methodPath = methodrm.value();
// 把方法上与路径建立映射关系( /james/query--->public void com.enjoy.james.controller.JamesController.query )
handlerMap.put(classPath + methodPath, method);
} else {
continue;
}
}
}
}
}
// 初始化IOC容器
private void ioc() {
if (beans.entrySet().size() <= 0) {
System.out.println("没有类的实例化!");
return;
}
//将实例化好的bean遍历,
for (Map.Entry entry : beans.entrySet()) {
Object instance = entry.getValue();//获取bean实例
Class> clazz = instance.getClass();//获取类,用来判断类里声明了哪些注解(主要是针对控制类里的判断,比如使用了@Autowired @Qualifier,对这些注解进行解析)
//判断该类是否使用了EnjoyController注解
if (clazz.isAnnotationPresent(EnjoyController.class)) {
Field[] fields = clazz.getDeclaredFields();// 拿到类里面的属性
// 判断是否声明了自动装配(依赖注入)注解,比如@Autrowired @Qualifier
for (Field field : fields) {
if (field.isAnnotationPresent(EnjoyQualifier.class)) {
EnjoyQualifier qualifier = (EnjoyQualifier) field.getAnnotation(EnjoyQualifier.class);
//拿到@EnjoyQualifier("JamesServiceImpl")里的指定要注入的bean名字"JamesServiceImpl"
String value = qualifier.value();
field.setAccessible(true);//将属性设置为允许修改,否则不能注入
try {
// 从MAP容器中获取"JamesServiceImpl"对应的bean,并注入实例控制层bean,解决依赖注入
field.set(instance, beans.get(value));
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
continue;
}
}
} else {
continue;
}
}
}
private void instance() {
if (classNames.size() <= 0) {
System.out.println("包扫描失败!");
return;
}
//遍历扫描到的class文件,将需要实例化的类(加了注解的类)进行反射创建对象(像注解就不需要实例化)
for (String className : classNames) {
// com.enjoy.james.service.impl.JamesServiceImpl.class
String cn = className.replace(".class", "");
try {
Class> clazz = Class.forName(cn);//拿到class类,用来实例化
// 将扫描到的类,获取类名,并判断是否标记了EnjoyController注解
if (clazz.isAnnotationPresent(EnjoyController.class)) {
EnjoyController controller = (EnjoyController) clazz.getAnnotation(EnjoyController.class);
Object instance = clazz.newInstance();
//获取对应的请求路径"/james"
EnjoyRequestMapping requestMapping = (EnjoyRequestMapping) clazz
.getAnnotation(EnjoyRequestMapping.class);
String rmvalue = requestMapping.value();//得到"/james"请求路径
//用路径做为key,对应value为实例化对象
beans.put(rmvalue, instance);
} else if (clazz.isAnnotationPresent(EnjoyService.class)) {
//获取当前clazz类的注解(通过这个注解可得到当前service的id) @com.enjoy.james.annotation.EnjoyService(value=JamesServiceImpl)
EnjoyService service = (EnjoyService) clazz.getAnnotation(EnjoyService.class);
Object instance = clazz.newInstance();
//put(JamesServiceImpl,instance)
beans.put(service.value(), instance);
} else {
continue;
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void scanPackage(String basePackage) {
//扫描编译好的类路径下所有的类
URL url = this.getClass().getClassLoader().getResource("/" + replaceTo(basePackage));
String fileStr = url.getFile();
File file = new File(fileStr);
//拿到所有类com.enjoy下的james文件夹
String[] filesStr = file.list();
for (String path : filesStr) {
File filePath = new File(fileStr + path);//扫描com.enjoy.james下的所有class类
//递归调用扫描,如果是路径,继续扫描
if (filePath.isDirectory()) {
// com.enjoy.james
scanPackage(basePackage + "." + path);
} else {
classNames.add(basePackage + "." + filePath.getName());//如果是class文件则加入List集合(待生成bean)
}
}
}
private String replaceTo(String basePackage) {
return basePackage.replaceAll("\\.", "/");
}
}
- JamesHandlerAdapter
可以将目标方法中的参数进行处理,在请求时将request传入的请求参数根据目标方法method定义的参数类型进行处理,传出一个Object[]对象数组,该数组在框架利用反射调用目标方法时作为参数传入,例如method.invoke(instance, args); 而对于不用的参数类型,我们可以用策略模式来处理
@EnjoyService("jamesHandlerAdapter")
public class JamesHandlerAdapter implements HandlerAdapterService {
//对method方法里的参数进行处理
public Object[] hand(HttpServletRequest request,//需要传入request,拿请求的参数
HttpServletResponse response, Method method,//执行的方法,可以拿到当前待执行的方法有哪些参数
Map beans) {
//拿到当前待执行的方法有哪些参数
Class>[] paramClazzs = method.getParameterTypes();
//根据参数的个数,new 一个参数的数组,将方法里的所有参数赋值到args来
Object[] args = new Object[paramClazzs.length];
//1、要拿到所有实现了ArgumentResolver这个接口的实现类
Map argumentResolvers = getBeansOfType(beans,
ArgumentResolver.class);
int paramIndex = 0;
int i = 0;
//对每一个参数进行循环,每个参数都有特殊处理(比如RequestParam的处理类为 RequestParamArgumentResolver )
for (Class> paramClazz : paramClazzs) {
//哪个参数对应了哪个参数解析类,用策略模式来找
for (Map.Entry entry : argumentResolvers.entrySet()) {
ArgumentResolver ar = (ArgumentResolver)entry.getValue();
if (ar.support(paramClazz, paramIndex, method)) {
args[i++] = ar.argumentResolver(request,
response,
paramClazz,
paramIndex,
method);
}
}
paramIndex++;
}
return args;
}
//获取实现了ArgumentResolver接口的所有实例(其实就是每个参数的注解实例)
private Map getBeansOfType(Map beans,//所有bean
Class> intfType) //类型的实例
{
Map resultBeans = new HashMap();
for (Map.Entry entry : beans.entrySet()) {
//拿到实例-->反射对象-->它的接口(接口有多实现,所以为数组)
Class>[] intfs = entry.getValue().getClass().getInterfaces();
if (intfs != null && intfs.length > 0) {
for (Class> intf : intfs) {
//接口的类型与传入进来的类型一样,把实例加到resultBeans里来
if (intf.isAssignableFrom(intfType)) {
resultBeans.put(entry.getKey(), entry.getValue());
}
}
}
}
return resultBeans;
}
}
- ArgumentResolver
参数解析接口类
public interface ArgumentResolver {
public boolean support(Class> type, int paramIndex, Method method);
//参数解析方法,每一个参数都会调用一次这个方法
public Object argumentResolver(HttpServletRequest request,
HttpServletResponse response, Class> type,
int paramIndex,//参数索引下坐标,有很多注解,你得知道是哪个参数的注解,每个参数的索引顺序不一样
Method method);
}
- RequestParamArgumentResolver
参数解析实现类,解析对应的@EnjoyRequestParam注解
@EnjoyService("requestParamArgumentResolver")
//解析声明注解为RequestParam, 获取注解的值
public class RequestParamArgumentResolver implements ArgumentResolver {
//判断传进来的参数是否为EnjoyRequestParam
public boolean support(Class> type, int paramIndex, Method method) {
Annotation[][] an = method.getParameterAnnotations();
Annotation[] paramAns = an[paramIndex];
for (Annotation paramAn : paramAns) {
//判断传进的paramAn.getClass()是不是 EnjoyRequestParam 类型
if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
return true;
}
}
return false;
}
//参数解析,并获取注解的值
public Object argumentResolver(HttpServletRequest request,
HttpServletResponse response, Class> type, int paramIndex,
Method method) {
//获取方法中参数的所有注解并放入二维数组中
Annotation[][] an = method.getParameterAnnotations();
//根据paramIndex获取对应的参数注解信息
Annotation[] paramAns = an[paramIndex];
for (Annotation paramAn : paramAns) {
//判断该注解是否为EnjoyRequestParam类注解
if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
EnjoyRequestParam rp = (EnjoyRequestParam)paramAn;
String value = rp.value();
//获取参数注解中标注的参数别名,根据别名获取请求中的参数值并返回
return request.getParameter(value);
}
}
return null;
}
}