手写SpringMVC IOC,让自己爽一爽(二)
下面开始进入第二节,手写DispatcherServlet。
(当然只是实现部分IOC部分,像其余的组件暂未实现)
首先创建一个DispatcherServlet 在这里为了和Spring Mvc中的做区分,我这里命名为ZyxDispatcherServlet。
既然是一个Servlet就要继承HttpServlet,并且要重写四个方法:
- init();
- init(ServletConfig config);
- doGet(HttpServletRequest req, HttpServletResponse resp);
- doPost(HttpServletRequest req, HttpServletResponse resp);
3,4不用多说,既然要响应网页前端请求必须要有get和post方法的一个处理,GET请求直接转给POST统一做处理,而init方法具体暂时也不需要实现什么东西,2 才是最关键的部分,其中功能如下:
@Override
public void init(ServletConfig config) throws ServletException {
// 先整理思路
// 1 先加载配置文件 读取web文件中的param-name
doLoadConfig(config.getInitParameter("contextConfigLocation"));
// 2 扫描所有的包
doScanner(properties.getProperty("scanPackage"));
// 3 初始化所有的类,并保存在IOC容器中
doInstace();
// 4 实现依赖注入
doAutowired();
// 5 构造一个HanderMapping 将请求的Url和Method建立关系
initHandlerMapping();
// 6 等待请求,根据匹配的URL 去动态调用doPost 或者doGet
System.out.println("ZyxDispatcherServlet init 加载完成!");
}
下面贴上我的具体实现代码:
/**
* 获取配置文件
* @param path
*/
private void doLoadConfig(String path) {
//获取输入流
System.out.println("path :"+path);
InputStream input = this.getClass().getClassLoader().getResourceAsStream(path);
try {
properties.load(input);
} catch (IOException e) {
System.out.println("获取输入流发送错误"+e.getMessage());
e.printStackTrace();
}finally{
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 扫描所有的包
* @author ZHANG_YX
* @date 2017年12月28日 上午9:21:52
*/
private void doScanner(String packageName) {
System.out.println("packageName :"+packageName);
URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
File file = new File(url.getFile());
for (File fi : file.listFiles()) {
if(fi.isDirectory()){
System.out.println("file name:"+fi.getName());
doScanner(packageName+"."+fi.getName());
}else{
classNames.add(packageName+"."+fi.getName().replace(".class", ""));
}
}
}
/**
* 初始化所有的实例
* @author ZHANG_YX
* @date 2017年12月28日 上午9:18:11
*/
private void doInstace() {
if(classNames.size()<1)return ;
try {
for(String className : classNames) {
Class<?> clazz =Class.forName(className);
if(clazz.isAnnotationPresent(ZyxController.class)){
ioc.put(lowerFirst(clazz.getSimpleName()), clazz.newInstance());
}else if(clazz.isAnnotationPresent(ZyxService.class)){
//1 如果自己有名字 则用自己的名字
//2 如果没有 则用类名首字母小写
//3 如果是接口,则用实现接口的小写实例名
ZyxService zyxservice = clazz.getAnnotation(ZyxService.class) ;
String beanName = zyxservice.value();
if(!beanName.equals("")){
ioc.put(beanName, clazz.newInstance());
}else{
ioc.put(lowerFirst(clazz.getSimpleName()), clazz.newInstance());
}
Class[] interfaces = clazz.getInterfaces();
for (Class class1 : interfaces) {
System.out.println("class1.getName():"+class1.getName());
ioc.put(class1.getSimpleName(), clazz.newInstance());
}
}else{
continue;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void doAutowired() {
if(ioc.isEmpty())return;
for (Entry entry : ioc.entrySet()) {
Field [] fields = entry.getClass().getDeclaredFields();
for (Field field : fields) {
if(!field.isAnnotationPresent(ZyxAutowired.class)){continue;}
ZyxAutowired zyxAutowired = field.getAnnotation(ZyxAutowired.class);
//和初始化class一样 注入也是需要分三种情况(有名字,无名字)
String beanName = zyxAutowired.value();
if(!"".equals(beanName))
beanName = field.getType().getName();
field.setAccessible(true);
try {
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
private void initHandlerMapping(){
if(ioc.isEmpty())return;
for (Entry<String,Object> entry : ioc.entrySet()) {
Class controllerClass =entry.getValue().getClass();
if(!controllerClass.isAnnotationPresent(ZyxController.class))continue;
String baseUrl = "";//获取requestMapping url
if(controllerClass.isAnnotationPresent(ZyxRequestMapping.class)){
ZyxRequestMapping mapping = (ZyxRequestMapping) controllerClass.getAnnotation(ZyxRequestMapping.class);
baseUrl = mapping.value();
}
Method[] methods = controllerClass.getMethods();
for (Method method : methods) {
if(!method.isAnnotationPresent(ZyxRequestMapping.class))continue;
ZyxRequestMapping methodMapping = method.getAnnotation(ZyxRequestMapping.class);
baseUrl = "/"+baseUrl+methodMapping.value();
// handlerMapping.put(baseUrl, method);
Pattern pa = Pattern.compile(baseUrl);
handlerMapping.add(new Handler(controllerClass,method,pa));
}
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException{
Handler handler = getHandler(req);
if(handler==null){
resp.getWriter().write("404 not Found Handler");
return ;
}
handler.method.invoke(handler.controller, handler.paramMappingIndex);
}
private Handler getHandler(HttpServletRequest request){
if(handlerMapping.isEmpty())return null;
String contextPath = request.getContextPath();
String url = request.getRequestURI();
System.out.println("contextPath:"+contextPath);
// url.replace(contextPath, "").replaceAll("/+", "/");
System.out.println("url:"+url);
for (Handler handler : handlerMapping) {
Matcher matcher = handler.pattern.matcher(url);
if(!matcher.matches())continue;
return handler;
}
return null;
}
private String lowerFirst(String name){
char[] chars = name.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
@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 {
doDispatch( req, resp);
} catch (Exception e) {
resp.getWriter().write("500 doDispatch错误:"+Arrays.toString(e.getStackTrace()));
}
}