下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

实现Bean管理,从头开始实现一个小型spring框架

作者:课课家教育     来源: http://www.kokojia.com点击数:893发布时间: 2019-06-27 09:51:10

标签: spring框架spring框架视频spring视频课程

大神带你学编程,欢迎选课

  写在前面

  最近学习了一下spring的相关内容,所以也就想要照猫画虎地记录和实现一下spring的框架,通过阅读这些也希望能够消除对Spring框架的恐惧,其实细心阅读框架也很容易理解。

  mini-spring尽量实现spring的核心功能。文章写得很仓促,可能不是很全面,在全部完成之后我们再来完善表达吧,见谅~

  手写Spring之 实现Bean管理(IOC与DI)

  写在前面

  一、bean管理

  1.1 什么是bean?

  1.2 bean优势

  二、依赖注入和控制反转

  2.1 控制反转

  2.2 依赖注入

  三、实现

  3.1 包结构的改变

  3.2 具体实现

  framework模块

  test模块

  3.3 测试

  四、小结

  五、框架总结

  项目的源码我放在了github上:源码地址

  我会在这里整理文章的系列目录:

  从头开始实现一个小型spring框架——手写Spring之实现SpringBoot启动

  从头开始实现一个小型spring框架——手写Spring之集成tomcat服务器

  从头开始实现一个小型spring框架——控制器controller的实现

  从头开始实现一个小型spring框架——实现Bean管理(IOC与DI)

  一、bean管理

  1.1 什么是bean?

  bean是spring抽象出的概念

  区别于普通的对象,bean的声明周期一般较长。

  在整个虚拟机中都是可见的

  普通的对象仅在当前类可见,在其他类中是引用不到的。因此对bean的维护成本要高于对普通对象的维护。

  bean一般单例存在

  正因为对bean的维护成本很高,因此bean单例存在。

  1.2 bean优势

  运行期效率高

  bean在项目启动时初始化,使用时直接获取,提高了获取对象的性能;也减少了代码在维护上成本;减少了对象使用过后被抛弃的情况,降低虚拟机在gc时的压力。

  统一维护,便于管理和拓展

  不必处理链式的依赖

  单例存在

  二、依赖注入和控制反转

  很多人认为控制反转和依赖注入是相同的,其实这是一个误区。

  2.1 控制反转

  IOC(Inversion of Control):思想

  控制反转是一种设计的原则和思想,用来降低代码之间的耦合度。

  2.2 依赖注入

  DI(Dependency Injection):方式

  依赖注入是实现IOC的一种方式。

  采用依赖注入的方式,类A中只需要定义一个B的属性,不需要通过new来获取对象,二是通过bean容器将对象new出来,并注入到A对象的引用里。

  控制正转方式:

  控制反转创建的方式:

  三、实现

  因为我们已经在上一篇文章中实现了包扫描,所以可以非常方便获取到类的信息。

  获取到类的信息后,我们就要根据是否存在需要注入的依赖来实例化 Bean对象了,我们来看具体的实现:

  3.1 包结构的改变

  直接看下图

  更改后的内容添加了对依赖注入的实现,具体有

  framework模块:

  beans包下增加

  Autowired自定义注解:用于标记需要进行注入的域

  Bean自定义注解:标记类为需要实例化的Bean

  test模块

  service包增加

  UserService :实现到controller的注入

  3.2 具体实现

  framework模块

  Autowired标记注解

  package com.qcby.beans;

  import java.lang.annotation.*;

  @Documented

  @Retention(RetentionPolicy.RUNTIME)

  @Target(ElementType.FIELD)

  public @interface Autowired {

  }

  Bean注解

  package com.qcby.beans;

  import java.lang.annotation.*;

  @Documented

  @Retention(RetentionPolicy.RUNTIME)

  @Target(ElementType.TYPE)

  public @interface Bean {

  }

  BeanFactory工厂

  这是本次实现的核心代码,将Bean通过map的形式进行保存,提供getBean,initBean方法对Bean容器内的实例进行获取和初始化Bean容器

  package com.qcby.beans;

  import com.qcby.web.mvc.Controller;

  import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

  import java.lang.reflect.Field;

  import java.util.ArrayList;

  import java.util.List;

  import java.util.Map;

  import java.util.concurrent.ConcurrentHashMap;

  /**

  * @author kevinlyz

  * @ClassName BeanFactory

  * @Description bean工厂

  * @Date 2019-06-09 11:09

  **/

  public class BeanFactory {

  //ConcurrentHashMap保存bean

  public static Map,Object> classToBean = new ConcurrentHashMap<>();

  //获取bean

  public static Object getBean(Class cls){

  return classToBean.get(cls);

  }

  //初始化

  public static void initBean(List> classList) throws Exception {

  List> toCreate = new ArrayList<>(classList);

  //循环实例化bean,判断是否存在循环依赖

  while (toCreate.size() != 0){

  int remainSize = toCreate.size();

  for (int i=0;i

  if (finishCreate(toCreate.get(i))){

  toCreate.remove(i);

  }

  }

  if (toCreate.size() == remainSize){ //陷入死循环,抛出异常

  throw new Exception("循环依赖异常");

  }

  }

  }

  //创建bean

  private static boolean finishCreate(Class cls) throws IllegalAccessException, InstantiationException {

  //不是bean直接返回true并删除

  if (!cls.isAnnotationPresent(Bean.class) && !cls.isAnnotationPresent(Controller.class)){

  return true;

  }

  //创建对象

  Object bean = cls.newInstance();

  //解决存在的依赖;获取域并判断是否被Autowired注解

  for (Field field : cls.getDeclaredFields()){

  if (field.isAnnotationPresent(Autowired.class)){ //存在Autowired注解

  Class fieldType = field.getType();

  Object reliantBean = BeanFactory.getBean(fieldType); //从Bean工厂内获取被依赖的bean

  if (reliantBean == null){

  return false;

  }

  field.setAccessible(true);

  field.set(bean,reliantBean); //为bean设置依赖域

  }

  }

  classToBean.put(cls,bean);

  return true;

  修改MiniApplication,对Bean工厂进行初始化

  BeanFactory.initBean(classList);

  完整代码为

  package com.qcby.starter;

  import com.qcby.beans.BeanFactory;

  import com.qcby.core.ClassScanner;

  import com.qcby.web.handler.HandlerManagger;

  import com.qcby.web.server.TomcatServer;

  import java.util.List;

  /**

  * @author kevinlyz

  * @ClassName MiniApplication

  * @Description 框架的入口类

  * @Date 2019-06-04 19:21

  **/

  public class MiniApplication {

  public static void run(Class cls,String[] args){

  System.out.println("Hello mini-spring application!");

  TomcatServer tomcatServer = new TomcatServer(args);

  try {

  tomcatServer.startServer();

  List> classList = ClassScanner.scannClasses(cls.getPackage().getName());

  BeanFactory.initBean(classList);

  classList.forEach(it->System.out.println(it.getName()));

  HandlerManagger.resolveMappingHandler(classList);

  } catch (Exception e) {

  e.printStackTrace();

  }

  }

  }

  修改MappingHandler获取Bean的方式为从工厂中获取

  Object ctl = BeanFactory.getBean(controller);

  完整代码为

  package com.qcby.web.handler;

  import com.qcby.beans.Bean;

  import com.qcby.beans.BeanFactory;

  import javax.servlet.ServletRequest;

  import javax.servlet.ServletResponse;

  import javax.servlet.http.HttpServletRequest;

  import java.io.IOException;

  import java.lang.reflect.InvocationTargetException;

  import java.lang.reflect.Method;

  /**

  * @author kevinlyz

  * @ClassName MappingHandler

  * @Description 包含uri,类的方法信息,类信息和参数

  * @Date 2019-06-08 18:32

  **/

  public class MappingHandler {

  private String uri;

  private Method method;

  private Class controller;

  private String[] args;

  public MappingHandler(String uri, Method method, Class controller, String[] args) {

  this.uri = uri;

  this.method = method;

  this.controller = controller;

  this.args = args;

  }

  public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {

  String reqUri = ((HttpServletRequest)req).getRequestURI();

  if (!this.uri.equals(reqUri))

  return false;

  //相等则调用Handler的resolveMappingHandler方法,实例化并返回

  Object[] parameters = new Object[args.length];

  for(int i=0;i

  parameters[i] = req.getParameter(args[i]);

  }

  Object ctl = BeanFactory.getBean(controller);

  Object response = method.invoke(ctl,parameters);

  res.getWriter().println(response.toString());

  return true;

  }

  }

  test模块

  添加UserService

  package com.qcby.service;

  import com.qcby.beans.Bean;

  /**

  * @author kevinlyz

  * @ClassName UserService

  * @Description TODO

  * @Date 2019-06-09 13:37

  **/

  @Bean

  public class UserService {

  public String getInfo(String name){

  return name+" is handsome!!";

  }

  }

  修改UserController

  package com.qcby.controller;

  import com.qcby.beans.Autowired;

  import com.qcby.service.UserService;

  import com.qcby.web.mvc.Controller;

  import com.qcby.web.mvc.RequestMapping;

  import com.qcby.web.mvc.RequestParam;

  /**

  * @author kevinlyz

  * @ClassName UserController

  * @Description TODO

  * @Date 2019-06-08 17:14

  **/

  @Controller

  public class UserController {

  @Autowired

  private UserService userService;

  @RequestMapping("/test")

  public String test(@RequestParam("name") String name,@RequestParam("desc") String desc){

  System.out.println("test访问了!");

  System.out.println("name>>>>>>>"+name);

  return userService.getInfo(name);

  }

  }

  3.3 测试

  再熟悉不过的流程了

  构建:gradle build

  运行:java -jar test/build/libs/test-1.0-SNAPSHOT.jar

  然后在浏览器输入需要传递的参数,这里仅传输了一个那么用于测试,看到如下结果:

  完事!!!

  到此,我们的依赖注入也成功集成到了框架之中!

  测试中增加了UserService并用@Bean标记,并在UserController中注入,成功完成测试.

  第二篇对Web请求的流程进行了介绍和分析,分析了请求到Web服务器的传输过程。在此基础上,我们对项目中的包结构进行了扫描,并实现拦截了TestServlet中的请求,最终将Tomcat容器集成在了mini-spring框架之中。

  第三篇是对控制器的核心实现。我们对容器内部的处理流程做了进一步的分析,分析其中存在的问题和Spring对请求方式处理上的改进。根据Spring的实现方式,我们对mini-spring框架进行了实现,ClassScanner获取项目的包结构,使用DispatcherServlet对/下的请求进行了拦截,基于Controller、RequestMapping、RequestParam三个注解实现信息的扫描和配置。

  第四篇在第三篇包扫描的基础上进一步实现依赖注入,首先对控制反转和依赖注入的区别进行了分析,进而在BeanFactory中解决了容器中存在的循环依赖的问题,最终完成我们的mini-spring框架!

  项目中还可以进一步完善,如Controller的参数注解的改进,依赖注入的性能,还可以结合数据持久层的框架,作进一步完善!

  项目中首先对架构进行了介绍,将项目分为了framewok模块和test模块两个,分别负责框架的实现和测试,在简单的介绍了gradle的相关知识后,实现了SpringBoot的启动方式。

  五、框架总结

  到这里,我们的小型Spring框架的开发就彻底的结束了。其实项目中还存在着许多的缺陷和不足,在细节方面没有很细的雕琢,如果有大佬进行了改进和实现,欢迎在github里面提交代码呀。

  小结

  在这一篇我们使用反射的方式对依赖注入进行了实现,增加了Bean注解用于标记一个类是否是需要管理的Bean,Autowired注解用于注解在何处注入实例化后的Bean。以及核心的实现类BeanFactory工厂,对外提供了获取bean的getBean方法和初始化工厂的initBean的方法。同时修改MappingHandler获取Bean的方式为从工厂中获取。

赞(18)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程