手写MVC之IOC,DI

手写IOC和DI,全部代码只使用了javax.servlet.*包,无其他任何工具包

目录结构如图所示,最终实现效果如下:

依次写好一下几个注解:

这几个代码就不发了,代码放在这里。

最重要的步骤如下:

1.新建一个web项目,配置web.xml文件

 <servlet>
        <servlet-name>dispatchServlet</servlet-name>
        <servlet-class>com.panfan.config.MyDispatcherServlet</servlet-class>
        <init-param>
            <param-name>scanPackage</param-name>
            <param-value>com.panfan</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatchServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

2.新建sevlet,如下:

package com.panfan.config;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.panfan.annotation.Autowired;
import com.panfan.annotation.Controller;
import com.panfan.annotation.RequestMapping;
import com.panfan.annotation.RequestParam;
import com.panfan.annotation.Service;


@SuppressWarnings("serial")
public class MyDispatcherServlet extends HttpServlet {

	// 保存需要被托管的类名(全类名)
	private List<String> classNames = new ArrayList<>();
	// 保存beanname到bean实例的映射map
	private Map<String, Object> beanMap = new HashMap<>();
	// Url 到方法的映射,MappingModel里面有Method,参数名,controller的全类名
	private Map<String, MappingModel> handlerMap = new HashMap<>();
	
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		System.out.println("MyDispatcherServlet -- 各种初始化操作");
		// 得到包,扫描web.xml配置中的init-param指定的路径下文件,并打印结果
		scanPackage(config.getInitParameter("scanPackage"));
    	System.out.println(" 包扫描完成,所有被扫描到类有: ");
		classNames.forEach(name -> System.out.println(name));
		// 得到注解的实例,为之后注入做准备
		doInject();
		// 注入AutoWired标注的属性
		doAutowired();
		// 路径url和方法之间映射管理建立
		doHandlerMapping();
	}
	
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			// 映射url到方法
            boolean isMatcher = mapping(req, resp);
            if (!isMatcher) {
                out(resp,"404 not found");
            }
        } catch (Exception e) {
        	e.printStackTrace();
            ByteArrayOutputStream buf = new java.io.ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(buf, true));
            String expMessage = buf.toString();
            buf.close();
            out(resp, "500 Exception" + "\n" + expMessage);
        }
		System.out.println("doPost完毕-------------------------------");
    }
 
    
    // 扫描包,并保存扫描到的所有符合的类的全类名
    private void scanPackage(String packageName) {
    	 //获取指定的包的实际路径url,将com.panfan变成目录结构com/panfan
    	URL url = this.getClass().getClassLoader().getResource("/" + packageName.replace(".", "/"));
    	//转化成file对象
    	File dir = new File(url.getFile());
    	for(File file : dir.listFiles()) {
    		if (file.isDirectory()) {
    			scanPackage(packageName + "." + file.getName());
    		}else {
    			if (!file.getName().endsWith(".class")) {
    				continue;
    			}
    			String className = packageName + "." + file.getName().replace(".class", "");
    			
    			//判断是否被Controller或者Service注解了
                try {
                    Class<?> clazz = Class.forName(className);
                    if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)) {
                        // 保存需要托管的类的全类名
                    	classNames.add(className);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
    		}
    	}
    }
    
    // 保存所有添加了注解的bean,放入map,注意key为类的全类名,
    private void doInject() {
    	if (classNames.size() <= 0) {
    		return ;
    	}
    	classNames.forEach(classname -> {
    		try {
    			Class<?> clazz = Class.forName(classname);
    			// 如果是Controller,则放入一个新的实例到beanMap,controller
    			if (clazz.isAnnotationPresent(Controller.class)) {
					beanMap.put(classname, clazz.newInstance());
    			}else if (clazz.isAnnotationPresent(Service.class)) {
    				// 如果是Service,因为@Service注解是在ServiceImpl实现类上,所以我们需要先扫描这个实现类的所有接口
					Service service = clazz.getAnnotation(Service.class);
					// 如果@Service 注解的value属性不为空,bean的名字就是value的值
					String value = service.value();
					if (!"".equals(value)) {
						beanMap.put(value, clazz.newInstance());
					}else {
						Class[] interfaces = clazz.getInterfaces();
						for (Class c : interfaces) {
							/** 有可能有多个接口,例如UserSerivceImpl实现UserService接口和AccountServcie接口,
							     这里判断UserSerivceImpl名字中包含UserService,所以默认bean名字就是com.panfan.service.UserService
							   你可以自定义此处的规则 */
							if (clazz.getSimpleName().contains(c.getSimpleName())) {
								beanMap.put(c.getName(), clazz.newInstance());
								break;
							}
						}
					}
				}
    		}catch (ClassNotFoundException e) {
				e.printStackTrace();
			}catch (IllegalAccessException e) {
				e.printStackTrace();
			}catch (InstantiationException e) {
				e.printStackTrace();
			}
    	});
    	// 打印一下
    	System.out.println("所有完成映射的实例");
		beanMap.forEach((k, v) -> System.out.println(k + ":" + v));
    }
    
    
    // 为autowired注入属性
    private void doAutowired() {
    	if (beanMap.isEmpty()) {
    		return ;
    	}
    	// 遍历所有被托管的对象
    	beanMap.forEach((k, v) -> {
    		// 取出所有字段
    		Field[] fields = v.getClass().getDeclaredFields();
    		for (Field field : fields) {
    			if (!field.isAnnotationPresent(Autowired.class)) {
    				continue;
    			}
    			// 设置对象的访问权限,保证对private的属性的访问
                field.setAccessible(true);
    			// 获取autowired标注属性的类型,autowired 先按类型注入
                String className = field.getType().getName();
                if (null != beanMap.get(className)) {
                	try {
						field.set(v, beanMap.get(className));
						System.out.println("根据类型为 " + v.getClass().getSimpleName() + " 注入 " + field.getName() + " 成功");
					} catch (Exception e) {
						System.out.println("======================AutoWired注入失败=====================");
					}
                }// autowired 按类型寻找不到bean,则按属性名字注入
                else if (null != beanMap.get(field.getName())) {
                	try {
						field.set(v, beanMap.get(field.getName()));
						System.out.println("根据属性名为 " + v.getClass().getSimpleName() + " 注入 " + field.getName() + " 成功");
					} catch (Exception e) {
						System.out.println("======================AutoWired注入失败=====================");
					}
                }
    		}
    		
    	});
    }
    
    
    // 把url对方法的映射保存到map
    public void doHandlerMapping() {
    	if (beanMap.isEmpty()) {
    		return ;
    	}
    	beanMap.forEach((k, v) -> {
    		Class<?> clazz = v.getClass();
    		// 只处理Controller的,只有Controller才有RequestMapping
    		if (clazz.isAnnotationPresent(Controller.class)) {
    			// 方法对应uri地址
    			String url = null ;
    			// 获取类上的路径,自动补前面的斜杠,后面不能有斜杠
    			if (clazz.isAnnotationPresent(RequestMapping.class)) {
    				RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
    				String value = requestMapping.value();
    				if (value.startsWith("/")) {
    					url = value;
    				}else {
    					url = "/" + value;
    				}
    			}
    			// 获取方法上路径
    			Method[] methods = clazz.getDeclaredMethods();
    			// 只处理有RequestMapping的方法
    			for (Method method : methods) {
    				if (!method.isAnnotationPresent(RequestMapping.class)) {
                        continue;
                    }
					RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
					String value = requestMapping.value();
					String realUrl ;
    				if (value.startsWith("/")) {
    					realUrl = url + value;
    				}else {
    					realUrl = url + "/" + value;
    				}
    				
    				LinkedList<String> list = new LinkedList<String>() ;
    				// 第一个维度对应参数列表里参数的数目,第二个维度对应参数列表里对应的注解
    				Annotation[][] annotations = method.getParameterAnnotations();
    				// 扫描@RequestParam注解
    				int t =0;
    				for (int i=0; i<annotations.length; i++,t++) {
    					// 如果前面有注解的话
						if (annotations[i].length > 0 ) {
							// 循环遍历是否有RequestParam注解
							for (int j=0; j<annotations[i].length; j++) {
								Class<?> type = annotations[i][j].annotationType();
    							if (type == RequestParam.class) {
    								RequestParam requestParam = (RequestParam) annotations[i][j];
    								list.add(requestParam.value());
    								break;
    							}
    						}
    					}
    				}
    				// 得到一个url映射,MappingModel包括controller,参数列表,和method
    				MappingModel mappingModel = new MappingModel(method, list.toArray(), clazz.getName());
    				handlerMap.put(realUrl, mappingModel);
    			}
    		}
    	});
    	
    	// 打印一下
    	System.out.println("所有url对应方法的映射关系");
		handlerMap.forEach((k, v) -> {
			System.out.println(k + ":" + v.method.getName() + "()  方法所有参数如下:");
			System.out.println(v.list);
		});
    	
    }
    
    
    // 通过请求地址和参数进行映射,成功后执行对应方法代码
    protected boolean mapping(HttpServletRequest request, HttpServletResponse response) {
    	if (handlerMap.isEmpty()) {
    		return false;
    	}
    	// 用户请求地址
        String requestUri = request.getRequestURI();
        String contextPath = request.getContextPath();
        // 找出除项目外的uri,用户写了多个"///",只保留一个
        requestUri = requestUri.replace(contextPath, "").replaceAll("/+", "/");
        MappingModel mappingModel = handlerMap.get(requestUri);
        // 得到目标方法
        Method method = mappingModel.method;
    	// 得到参数列表
        Object[] list = mappingModel.list;
        LinkedList<Object> values = new LinkedList<>() ;
        Class<?>[] classes = method.getParameterTypes();
    	for (int i=0; i<classes.length; i++) {
    		if (classes[i]== HttpServletRequest.class) {
    			values.add(request);
    			continue;
    		}else if (classes[i]== HttpServletResponse.class) {
    			values.add(response);
    			continue;
			}
    		values.add(convert(request.getParameter(list[i].toString()), classes[i]));
    	}
        try {
			method.invoke(beanMap.get(mappingModel.controller), values.toArray());
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} 
    	
		
	}
    
    
    
    // 头个字母大写变小写,这里用到ascii码
    private String lowerFirstChar(String className) {
        char[] chars = className.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
    
    // 参数转换
    private Object convert(String parameter, Class<?> targetType) {
        if (targetType == String.class) {
            return parameter;
        } else if (targetType == Integer.class || targetType == int.class) {
            return Integer.valueOf(parameter);
        } else if (targetType == Long.class || targetType == long.class) {
            return Long.valueOf(parameter);
        } else if (targetType == Boolean.class || targetType == boolean.class) {
            if (parameter.toLowerCase().equals("true") || parameter.equals("1")) {
                return true;
            } else if (parameter.toLowerCase().equals("false") || parameter.equals("0")) {
                return false;
            }
            throw new RuntimeException("不支持的参数");
        }
        else {
            //TODO 还有很多其他的类型,char、double之类的依次类推,也可以做List<>, Array, Map之类的转化
            return null;
        }
    }
    
    // 疯狂输出
    private void out(HttpServletResponse response, String str) {
        try {
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().print(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

还有一个java类

import java.lang.reflect.Method;
import java.util.LinkedList;

public class MappingModel {
	
	// url对应的Method
	Method method;
	// 对应的Controller全类名,可以得到MyDispatcherServlet中的beanMap中的Controller实例
	Object controller;
	// 记录此方法中的所有@RequestParam注解的参数,有序放置
	Object[] list;
	
	public MappingModel(Method method, Object[] list, Object controller) {
		this.method = method;
		this.list = list;
		this.controller = controller;
	}
}

注释很全,自己看看吧,累死了。

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值