手写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;
}
}
注释很全,自己看看吧,累死了。