模拟实现Spring IoC

Posted sakura1027

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟实现Spring IoC相关的知识,希望对你有一定的参考价值。

项目结构

直接上代码

 1 package com.sakura.annotion;
 2 
 3 import java.lang.annotation.*;
 4 
 5 @Target(ElementType.TYPE)
 6 @Retention(RetentionPolicy.RUNTIME)
 7 @Documented
 8 public @interface Controller {
 9     String value() default "";
10 }
 1 package com.sakura.annotion;
 2 
 3 import java.lang.annotation.*;
 4 
 5 @Target(ElementType.FIELD)
 6 @Retention(RetentionPolicy.RUNTIME)
 7 @Documented
 8 public @interface Qualifier {
 9     String value() default "";
10 }
 1 package com.sakura.annotion;
 2 
 3 import java.lang.annotation.*;
 4 
 5 @Target(ElementType.METHOD)
 6 @Retention(RetentionPolicy.RUNTIME)
 7 @Documented
 8 public @interface RequestMapping {
 9     String value() default "";
10 }
 1 package com.sakura.annotion;
 2 
 3 import java.lang.annotation.*;
 4 
 5 @Target(ElementType.TYPE)
 6 @Retention(RetentionPolicy.RUNTIME)
 7 @Documented
 8 public @interface Service {
 9     String value() default "";
10 }
 1 package com.sakura.controller;
 2 
 3 import com.sakura.annotion.Controller;
 4 import com.sakura.annotion.Qualifier;
 5 import com.sakura.annotion.RequestMapping;
 6 import com.sakura.service.FishService;
 7 
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 @Controller("fish")
12 public class FishController {
13     @Qualifier("fishServiceImpl")
14     FishService fishService;
15 
16     @RequestMapping("get")
17     public void get(HttpServletRequest request, HttpServletResponse response) {
18         System.out.println(fishService.get());
19     }
20 
21 }
 1 package com.sakura.service.impl;
 2 
 3 import com.sakura.annotion.Service;
 4 import com.sakura.service.FishService;
 5 
 6 @Service("fishServiceImpl")
 7 public class FishServiceImpl implements FishService {
 8     @Override
 9     public String get() {
10         return "shark";
11     }
12 }
1 package com.sakura.service;
2 
3 public interface FishService {
4     String get();
5 }
  1 package com.sakura.servlet;
  2 
  3 import com.sakura.annotion.Controller;
  4 import com.sakura.annotion.Qualifier;
  5 import com.sakura.annotion.RequestMapping;
  6 import com.sakura.annotion.Service;
  7 import com.sakura.controller.FishController;
  8 
  9 import javax.servlet.ServletException;
 10 import javax.servlet.annotation.WebServlet;
 11 import javax.servlet.http.HttpServlet;
 12 import javax.servlet.http.HttpServletRequest;
 13 import javax.servlet.http.HttpServletResponse;
 14 import java.io.File;
 15 import java.io.IOException;
 16 import java.lang.reflect.Field;
 17 import java.lang.reflect.Method;
 18 import java.net.URL;
 19 import java.util.ArrayList;
 20 import java.util.HashMap;
 21 import java.util.List;
 22 import java.util.Map;
 23 
 24 @WebServlet("/")
 25 public class DispatcherServlet extends HttpServlet {
 26     private List<String> classNames = new ArrayList<>();
 27     private Map<String, Object> instanceMap = new HashMap<>();
 28     private Map<String, Method> methodMap = new HashMap<>();
 29 
 30     @Override
 31     public void init() throws ServletException {
 32         // 找到bean
 33         scanBase("com.sakura");
 34         try {
 35             // 生成并注册bean
 36             registerBean();
 37             // 注入bean
 38             springDi();
 39             mvc();
 40         } catch (Exception e) {
 41             e.printStackTrace();
 42         }
 43     }
 44 
 45     private void mvc() {
 46         if (instanceMap.size() == 0) return;
 47         for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
 48             if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
 49                 String ctlUrl = entry.getValue().getClass().getAnnotation(Controller.class).value();
 50                 Method[] methods = entry.getValue().getClass().getMethods();
 51                 for (Method method : methods) {
 52                     if (method.isAnnotationPresent(RequestMapping.class)) {
 53                         String reqUrl = method.getAnnotation(RequestMapping.class).value();
 54                         String dispatchUrl = "/spring/" + ctlUrl + "/" + reqUrl;
 55                         methodMap.put(dispatchUrl, method);
 56                     }
 57                 }
 58             }
 59         }
 60     }
 61 
 62     private void springDi() throws Exception {
 63         if (instanceMap.size() == 0) return;
 64         for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
 65             Field[] fields = entry.getValue().getClass().getDeclaredFields();
 66             for (Field field : fields) {
 67                 if (field.isAnnotationPresent(Qualifier.class)) {
 68                     String key = field.getAnnotation(Qualifier.class).value();
 69                     field.setAccessible(true);
 70                     field.set(entry.getValue(), instanceMap.get(key));
 71                 }
 72             }
 73         }
 74     }
 75 
 76     private void registerBean() throws Exception {
 77         if (classNames.size() == 0) return;
 78         for (String className : classNames) {
 79             Class clazz = Class.forName(className.replace(".class", ""));
 80             if (clazz.isAnnotationPresent(Controller.class)) {
 81                 Object instance = clazz.newInstance();
 82                 String key = ((Controller) clazz.getAnnotation(Controller.class)).value();
 83                 // bean交付给ioc容器
 84                 instanceMap.put(key, instance);
 85             } else if (clazz.isAnnotationPresent(Service.class)) {
 86                 Object instance = clazz.newInstance();
 87                 String key = ((Service) clazz.getAnnotation(Service.class)).value();
 88                 // bean交付给ioc容器
 89                 instanceMap.put(key, instance);
 90             }
 91         }
 92     }
 93 
 94     private void scanBase(String basePackage) {
 95         URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\\\.", "/"));
 96         String path = url.getFile();
 97         File file = new File(path);
 98         String[] strFiles = file.list();
 99         for (String strFile : strFiles) {
100             File eachFile = new File(path + strFile);
101             if (eachFile.isDirectory()) {
102                 scanBase(basePackage + "." + eachFile.getName());
103             } else {
104                 System.out.println("class name:" + eachFile.getName());
105                 classNames.add(basePackage + "." + eachFile.getName());
106             }
107         }
108     }
109 
110     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
111         // localhost:8080/spring/fish/get
112         String uri = request.getRequestURI();
113         Method method = methodMap.get(uri);
114         String className = uri.split("/")[2];
115         FishController fishController = (FishController) instanceMap.get(className);
116         try {
117             method.invoke(fishController, new Object[]{request, response});
118         } catch (Exception e) {
119             e.printStackTrace();
120         }
121 
122     }
123 }

运行结果

实现思路

1. 通过scanBase方法获取com.sakura包下的classNames

2. 通过registerBean方法注册bean,需要注意还未完成依赖注入

3. 通过springDi方法完成依赖注入

4. 通过mvc方法绑定url到methodMap

5. 反射调用

 

以上是关于模拟实现Spring IoC的主要内容,如果未能解决你的问题,请参考以下文章

JAVA模拟Spring实现IoC过程(附源码)

spring之IOC模拟实现

javaSpring 自己模拟 Spring 实现 IOC依赖注入 并且 解决 循环依赖

自己动手模拟spring的IOC

分享一道面试题:模拟Spring IOC 控制反转实现原理,建议收藏!

一道面试题,简单模拟spring ioc