一个类手写Spring核心原理

水深无声 2023-01-13 04:20 265阅读 0赞

Spring实现的基本思路

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2lmZXJsb25neHU_size_16_color_FFFFFF_t_70

通过DispatcherServlet一个类演示Spring核心原理

  1. public class DispatcherServlet extends HttpServlet {
  2. // 保存用户配置的配置文件
  3. private Properties contextConfiguration = new Properties();
  4. // 保存扫描包下的所有类的全名
  5. private List<String> classNames = new ArrayList<String>();
  6. // ioc容器保存所有扫描到的类的实例对象
  7. private Map<String, Object> ioc = new HashMap<String, Object>();
  8. // 保存URL和method的对应关系
  9. private Map<String, Method> handlerMapping = new HashMap<String, Method>();
  10. @Override
  11. public void init(ServletConfig config) throws ServletException {
  12. // 1、加载配置文件
  13. doLoadConfig(config.getInitParameter("contextConfigLocation"));
  14. // 2、扫描相关的类
  15. doScanner(contextConfiguration.getProperty("scanPackage"));
  16. // 3、初始化IoC容器,将扫描到的类实例化,缓存到IoC容器
  17. doInstance();
  18. // 4、完成依赖注入
  19. doAutowired();
  20. // 5、初始化HandlerMapping
  21. initHandlerMapping();
  22. System.out.println("Spring framework is init.");
  23. }
  24. @Override
  25. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  26. this.doPost(req, resp);
  27. }
  28. @Override
  29. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  30. // 6、根据URL委派给具体的调用方法
  31. try {
  32. doDispatch(req, resp);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
  38. String url = req.getRequestURI();
  39. String contextPath = req.getContextPath();
  40. url = url.replaceAll(contextPath, ""); //获取相对路径
  41. if (!this.handlerMapping.containsKey(url)) {
  42. resp.getWriter().write("404 Not Found");
  43. return;
  44. }
  45. Method method = this.handlerMapping.get(url);
  46. //1、先把参数的位置参数名字建立映射关系,并且缓存下来
  47. Map<String, Integer> paramIndexMapping = new HashMap<String, Integer>();
  48. Annotation[][] parameterAnnotations = method.getParameterAnnotations();
  49. for (int i = 0; i < parameterAnnotations.length; i ++) {
  50. for (Annotation anno : parameterAnnotations[i]) {
  51. if (anno instanceof MyRequestParam) {
  52. String paraName = ((MyRequestParam) anno).value();
  53. if (!"".equals(paraName.trim())) {
  54. paramIndexMapping.put(paraName, i);
  55. }
  56. }
  57. }
  58. }
  59. Class<?>[] parameterTypes = method.getParameterTypes();
  60. for (int i = 0; i < parameterTypes.length; i ++) {
  61. Class<?> parameterType = parameterTypes[i];
  62. if (parameterType == HttpServletRequest.class || parameterType == HttpServletResponse.class) {
  63. paramIndexMapping.put(parameterType.getName(), i);
  64. }
  65. }
  66. //2、根据参数位置匹配参数名字,从url中获取到参数名字对应的值
  67. Object[] paramValues = new Object[parameterTypes.length];
  68. Map<String, String[]> parameterMap = req.getParameterMap();
  69. for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
  70. String key = entry.getKey();
  71. String[] values = entry.getValue();
  72. String value = Arrays.toString(values).replaceAll("\\[|\\]", "").replaceAll("\\s", "");
  73. if (!parameterMap.containsKey(key)) { continue; }
  74. int index = paramIndexMapping.get(key);
  75. // TODO 涉及到类型转换
  76. paramValues[index] = value;
  77. }
  78. if (paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
  79. paramValues[paramIndexMapping.get(HttpServletRequest.class.getName())] = req;
  80. }
  81. if (paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
  82. paramValues[paramIndexMapping.get(HttpServletResponse.class.getName())] = resp;
  83. }
  84. //3、组成动态参数列表,传给方法反射调用
  85. String beanName = toLowerCaseFirstChar(method.getDeclaringClass().getSimpleName());
  86. method.invoke(ioc.get(beanName), paramValues);
  87. }
  88. private void initHandlerMapping() {
  89. if (ioc.isEmpty()) { return; }
  90. for (Map.Entry<String, Object> entry : ioc.entrySet()) {
  91. Class<?> clazz = entry.getValue().getClass();
  92. if (!clazz.isAnnotationPresent(MyController.class)) { continue; }
  93. String controllerUrl = "";
  94. if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
  95. controllerUrl = clazz.getAnnotation(MyRequestMapping.class).value();
  96. }
  97. // 只迭代controller中的public方法
  98. for (Method method : clazz.getMethods()) {
  99. if (!method.isAnnotationPresent(MyRequestMapping.class)) { continue; }
  100. MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
  101. String url = "/" + controllerUrl + "/" + myRequestMapping.value();
  102. // 不确定用户声明的url路径前是否添加了/,所以暂时当做没有加/,最后通过正则将多个连续/替换成一个/
  103. url = url.replaceAll("/+", "/");
  104. handlerMapping.put(url, method);
  105. System.out.println("Mapped" + url + " --> " + method);
  106. }
  107. }
  108. }
  109. // 自动注入
  110. private void doAutowired() {
  111. if (ioc.isEmpty()) { return; }
  112. for (Map.Entry<String, Object> entry : ioc.entrySet()) {
  113. Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
  114. for (Field field : declaredFields) {
  115. if (!field.isAnnotationPresent(MyAutowired.class)) { continue; }
  116. MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
  117. String beanName = myAutowired.value().trim();//获取注解的别名
  118. if ("".equals(beanName)) {//如果注解上没有起别名,那么就获取对象名
  119. beanName = field.getType().getName();
  120. }
  121. // 强制访问赋值
  122. field.setAccessible(true);
  123. try {
  124. // 相当于userController.userService=ioc.get("com.lucifer.project.service.UserService");
  125. field.set(entry.getValue(), ioc.get(beanName));
  126. } catch (IllegalAccessException e) {
  127. e.printStackTrace();
  128. }
  129. }
  130. }
  131. }
  132. // 实例化对象,并缓存到IoC容器中
  133. private void doInstance() {
  134. if (classNames.isEmpty()){ return; }
  135. try {
  136. for (String className : classNames) {
  137. Class<?> clazz = Class.forName(className);
  138. Object instance = clazz.newInstance();
  139. if (clazz.isAnnotationPresent(MyController.class)) {
  140. String beanName = toLowerCaseFirstChar(clazz.getSimpleName());
  141. ioc.put(beanName, instance);
  142. } else if (clazz.isAnnotationPresent(MyService.class)) {
  143. // 1、默认类名首字母小写,取默认名
  144. String beanName = toLowerCaseFirstChar(clazz.getSimpleName());
  145. // 2、如果多个包下出现相同的类名,优先使用别名
  146. MyService serviceAnno = clazz.getAnnotation(MyService.class);
  147. if (!"".equals(serviceAnno.value())) {
  148. beanName = serviceAnno.value();
  149. }
  150. // 3、如果是接口,只能吃实话它的实现类
  151. for (Class<?> inte : clazz.getInterfaces()) {
  152. if (ioc.containsKey(inte.getName())) {
  153. throw new Exception("The" + inte.getName() + " is exists, please use alias.");
  154. }
  155. beanName = inte.getName();
  156. }
  157. ioc.put(beanName, instance);
  158. } else {
  159. continue;
  160. }
  161. }
  162. } catch (Exception e) {
  163. e.printStackTrace();
  164. }
  165. }
  166. // 将类名首字母转小写
  167. private String toLowerCaseFirstChar(String simpleName) {
  168. char[] chars = simpleName.toCharArray();
  169. chars[0] += 32; //利用ASCII码,大写字母和小写字母相差32
  170. return String.valueOf(chars);
  171. }
  172. // 扫描指定包下所有的Class文件
  173. private void doScanner(String scanPackage) {
  174. URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
  175. File classpath = new File(url.getFile());
  176. for (File file : classpath.listFiles()) {
  177. if (file.isDirectory()) {
  178. doScanner(scanPackage + "." + file.getName() + ".");
  179. } else {
  180. if (!file.getName().endsWith(".class")) { continue; }
  181. // 包名.类名 比如com.lucifer.project.UserController
  182. String className = scanPackage + file.getName().replace(".class", "");
  183. // 实例化时需要通过Class.forName(className);
  184. classNames.add(className);
  185. }
  186. }
  187. }
  188. // 根据contextConfigLocation路径去Classpath下找到对应配置文件
  189. private void doLoadConfig(String contextConfigLocation) {
  190. InputStream in = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
  191. try {
  192. contextConfiguration.load(in);
  193. } catch (IOException e) {
  194. e.printStackTrace();
  195. } finally {
  196. if (null != in) {
  197. try {
  198. in.close();
  199. } catch (IOException e) {
  200. e.printStackTrace();
  201. }
  202. }
  203. }
  204. }
  205. }

自定义Spring注解类

  1. @Target({ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface MyAutowired {
  5. String value() default "";
  6. }
  7. @Target({ElementType.TYPE})
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. public @interface MyController {
  11. String value() default "";
  12. }
  13. @Target({ElementType.TYPE})
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Documented
  16. public @interface MyService {
  17. String value() default "";
  18. }
  19. @Target(ElementType.TYPE)
  20. @Retention(RetentionPolicy.RUNTIME)
  21. @Documented
  22. public @interface MyRespository {
  23. String value() default "";
  24. }
  25. @Target({ElementType.TYPE, ElementType.METHOD})
  26. @Retention(RetentionPolicy.RUNTIME)
  27. @Documented
  28. public @interface MyRequestMapping {
  29. String value() default "";
  30. }
  31. @Target({ElementType.PARAMETER})
  32. @Retention(RetentionPolicy.RUNTIME)
  33. @Documented
  34. public @interface MyRequestParam {
  35. String value() default "";
  36. }

测试用户请求访问类

  1. @MyController
  2. @MyRequestMapping("/user")
  3. public class UserController {
  4. @MyAutowired
  5. UserService userService;
  6. @MyRequestMapping("/query")
  7. public void query(HttpServletRequest req, HttpServletResponse resp, @MyRequestParam("name") String name) {
  8. String result = "Hello, I am " + name;
  9. try {
  10. resp.getWriter().write(result);
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }

20210418231124544.png

发表评论

表情:
评论列表 (有 0 条评论,265人围观)

还没有评论,来说两句吧...

相关阅读

    相关 一个Tomcat

    作为一个java学习的起步者,对tomcat的认识还是有很多的欠缺,在无意中发现了这篇文章,便在自己的环境下尝试搭建,收获良多: 分以下几个步骤: (1)提供Socke

    相关 Spring核心原理

    Spring核心原理 > 在Spring中拥有许多的组件,但核心部分主要为:Beans、Core、Context、Expression,其中最为主要的为Core、与Bea