使用Java反射模拟实现Spring的IoC容器的操作

目录
  • 实现的功能:
  • 项目结构
    • 下面是程序的项目结构图:
  • 自定义注解
  • 容器实现
  • 测试
    • 实体类User的定义:

实现的功能:

  • 默认情况下将扫描整个项目的文件
  • 可以使用@ComponentScan注解配置扫描路径
  • 只将被@Component注解修饰的类装载到容器中
  • 可以使用@AutoWired注解实现自动装配
  • 读取配置文件中的声明的类并注册到容器中

项目结构

下面是程序的项目结构图:

自定义注解

下面是自定义的三个注解: @AutoWired,@Component,@ComponentScan。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoWired {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
  String[] value();
}

容器实现

其中AnnotationConfigApplicationContext和ClassPathXMLApplicationContext为核心的类,其中

AnnotationConfigApplicationContext类实现扫描文件和解析注解等功能。

package learn.reflection.reflect;
import learn.reflection.Bootstrap;
import learn.reflection.annotation.AutoWired;
import learn.reflection.annotation.Component;
import learn.reflection.annotation.ComponentScan;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class AnnotationConfigApplicationContext<T>{
  //使用HaspMap存储Bean
  private HashMap<Class,Object> beanFactory=new HashMap<>();
  //获取Bean的方法
  public T getBean(Class clazz){
    return (T) beanFactory.get(clazz);
  }
  String path;//编译后的字节码存储路径
  /**
   * 初始化ApplicationContext,加载注解修饰的Bean到beanFactory
   */
  public void initContextByAnnotation(){
    //编译后的项目根目录:D:/idea_workplace/javaAppliTechnology/target/classes/
    path = AnnotationConfigApplicationContext.class.getClassLoader().getResource("").getFile();
    //查看启动类Bootstrap是否有定义扫描包
    ComponentScan annotation = Bootstrap.class.getAnnotation(ComponentScan.class);
    if (annotation!=null){
      //有定义就只扫描自定义的
      String[] definedPaths = annotation.value();
      if (definedPaths!=null&&definedPaths.length>0){
        loadClassInDefinedDir(path,definedPaths);
      }
    }else{
      //默认扫描整个项目的目录
      System.out.println(path);
      findClassFile(new File(path));
    }
    assembleObject();
  }
  /**
   * 给@AutoWired修饰的属性赋值
   */
  private void assembleObject(){
    Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
    //扫描所有容器中的Bean
    for (Map.Entry<Class, Object> entry : entries) {
      Object value = entry.getValue();
      //获取所有属性
      Field[] fields = value.getClass().getDeclaredFields();
      for (Field field : fields) {
        //如果被@AutoWired注解修饰则进行赋值
        AutoWired annotation = field.getAnnotation(AutoWired.class);
        if (annotation!=null){
          try {
            field.setAccessible(true);
            field.set(value,beanFactory.get(field.getType()));
          } catch (IllegalAccessException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }
  /**
   * 扫描用户自定义的包
   * @param path
   * @param definedPaths
   */
  private void loadClassInDefinedDir(String path, String[] definedPaths){
    for (String definedPath : definedPaths) {
      //转换成绝对路径
      String s = definedPath.replaceAll("\\.", "/");
      String fullName=path+s;
      System.out.println(s);
      findClassFile(new File(fullName));
    }
  }
  /**
   * 扫描项目中的每一个文件夹找到所有的class文件
   */
  private void findClassFile(File pathParent) {
    //路径是否是目录,子目录是否为空
    if (pathParent.isDirectory()) {
      File[] childrenFiles = pathParent.listFiles();
      if (childrenFiles == null || childrenFiles.length == 0) {
        return;
      }
      for (File childrenFile : childrenFiles) {
        if (childrenFile.isDirectory()) {
          //递归调用直到找到所有的文件
          findClassFile(childrenFile);
        } else {
          //找到文件
          loadClassWithAnnotation(childrenFile);
        }
      }
    }
  }
  /**
   *   装配找到的所有带有@Component注解的类到容器
   */
  private void loadClassWithAnnotation(File file) {
    //1.去掉前面的项目绝对路径
    String pathWithClass=file.getAbsolutePath().substring(path.length()-1);
    //2.将路径的“/”转化为“.”和去掉后面的.class
    if (pathWithClass.contains(".class")){
      String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
      /**
       *  根据获取到的类的全限定名使用反射将实例添加到beanFactory中
       */
      try {
        Class<?> clazz = Class.forName(fullName);
        //3.判断是不是接口,不是接口才创建实例
        if (!clazz.isInterface()){
          //4.是否具有@Bean注解
          Component annotation = clazz.getAnnotation(Component.class);
          if (annotation!=null){
            //5.创建实例对象
            Object instance = clazz.newInstance();
            //6.判断是否有实现的接口
            Class<?>[] interfaces = clazz.getInterfaces();
            if (interfaces!=null&&interfaces.length>0){
              //如果是有接口就将其接口的class作为key,实例对象作为value
              System.out.println("正在加载【"+interfaces[0].getName()+"】 实例对象:"+instance.getClass().getName());
              beanFactory.put(interfaces[0],instance);
            }else{
              System.out.println("正在加载【"+clazz.getName()+"】 实例对象:"+instance.getClass().getName());
              beanFactory.put(clazz,instance);
            }
            //如果没有接口就将自己的class作为key,实例对象作为value
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}

ClassPathXMLApplicationContext类实现解析xml配置文件,并装载组件到容器中。

package learn.reflection.reflect;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.jdom2.Document;
import org.jdom2.JDOMException;
import org.jdom2.Element;
import org.jdom2.xpath.XPath;
import org.jdom2.input.SAXBuilder;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URISyntaxException;
import java.util.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/**
 * @author Hai
 * @date 2020/5/17 - 18:47
 */
public class ClassPathXMLApplicationContext{
  private File file;
  private Map<String,Object> map = new HashMap();
  public ClassPathXMLApplicationContext(String config_file) {
    URL url = this.getClass().getClassLoader().getResource(config_file);
    try {
      file = new File(url.toURI());
      XMLParsing();
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  private void XMLParsing() throws Exception {
    SAXBuilder builder = new SAXBuilder();
    Document document = builder.build(file);
    Element root = document.getRootElement();
    List elementList = root.getChildren("bean");
    Iterator i = elementList.iterator();
    //读取bean节点的所有信息
    while (i.hasNext()) {
      Element bean = (Element) i.next();
      String id = bean.getAttributeValue("id");
      //根据class创建实例
      String cls = bean.getAttributeValue("class");
      Object obj = Class.forName(cls).newInstance();
      Method[] method = obj.getClass().getDeclaredMethods();
      List<Element> list = bean.getChildren("property");
      for (Element el : list) {
        for (int n = 0; n < method.length; n++) {
          String name = method[n].getName();
          String temp = null;
          //找到属性对应的setter方法进行赋值
          if (name.startsWith("set")) {
            temp = name.substring(3, name.length()).toLowerCase();
            if (el.getAttribute("name") != null) {
              if (temp.equals(el.getAttribute("name").getValue())) {
                method[n].invoke(obj, el.getAttribute("value").getValue());
              }
            }
          }
        }
      }
      map.put(id, obj);
    }
  }
  public Object getBean(String name) {
    return map.get(name);
  }
}

测试

实体类User的定义:

@Component
public class User {
  private String username;
  private String password;

  public User(String username, String password) {
    this.username = username;
    this.password = password;
  }
  public User() {
  }
  //省略getter,setter方法
  }

在UserServiceImpl类中添加@Component注解,并使用@AutoWired注解注入容器中的IUerDao接口的实现类UserDaoImpl。

@Component
public class UserServiceImpl implements IUserService {
  @AutoWired
  private IUserDao userDao;
  @Override
  public void login(User user) {
    System.out.println("调用UserDaoImpl的login方法");
    userDao.loginByUsername(user);
  }
}

UserDaoImpl类同样添加@Component注解

@Component
public class UserDaoImpl implements IUserDao {
  @Override
  public void loginByUsername(User user) {
    System.out.println("验证用户【"+user.getUsername()+"】登录");
  }
}

在beans.xml中配置注册User类,文件beans.xml的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="user" class="learn.reflection.entity.User">
        <property name="username" value="张三" />
        <property name="password" value="123" />
    </bean>
</beans>

下面同时使用 AnnotationConfigApplicationContext类和 ClassPathXMLApplicationContext类。

Bootstrap类作为启动类添加注解@ComponentScan,指定扫描learn.reflection.dao和learn.reflection.service这两个包。

@ComponentScan(value = {"learn.reflection.dao","learn.reflection.service"})
public class Bootstrap {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    applicationContext.initContextByAnnotation();
    UserServiceImpl userService = (UserServiceImpl) applicationContext.getBean(IUserService.class);
    ClassPathXMLApplicationContext xmlApplicationContext = new ClassPathXMLApplicationContext("beans.xml");
    User user = (User) xmlApplicationContext.getBean("user");
    System.out.println(user);
    userService.login(user);
  }
}

运行Bootstrap类,程序运行结果如下:

learn/reflection/dao
正在加载【learn.reflection.dao.IUserDao】 实例对象:learn.reflection.dao.impl.UserDaoImpl
learn/reflection/service
正在加载【learn.reflection.service.IUserService】 实例对象:learn.reflection.service.impl.UserServiceImpl
User{username='张三', password='123'}
调用UserDaoImpl的login方法
验证用户【张三】登录

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 用java的spring实现一个简单的IOC容器示例代码

    要想深入的理解IOC的技术原理,没有什么能比的上我们自己实现它.这次我们一起实现一个简单IOC容器.让大家更容易理解Spring IOC的基本原理. 这里会涉及到一些java反射的知识,如果有不了解的,可以自己去找些资料看看. 注意 在上一篇文章,我说,启动IOC容器时,Spring会将xml文件里面配置的bean扫描并实例化,其实这种说法不太准确,所以我在这里更正一下,xml文件里面配置的非单利模式的bean,会在第一次调用的时候被初始化,而不是启动容器的时候初始化.但是我们这次要做的例子是容

  • Spring实现一个简单的SpringIOC容器

    接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心于是买了一本计文柯的<Spring技术内幕>,第二章没看完,就被我扔一边了,看的那是相当痛苦,深深觉得自己资质尚浅,能力还不够,昨天在网上碰巧看到一个实现简单的SpringIOC容器的视频教程,于是跟着做了一遍,竟然相当顺利,至少每一行代码都能理解,于是细心整理了一番,放在这里. 主要思想: 提到

  • 使用Java注解模拟spring ioc容器过程解析

    使用注解,简单模拟spring ioc容器.通过注解给对象属性注入值. 项目结构 annotation 包,用于存放自定义注解 Component 注解表示该类为组件类,并需要声明名字 package priv.haidnor.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;

  • Spring的Ioc模拟实现详细介绍

    简单来说就是当自己需要一个对象的时候不需要自己手动去new一个,而是由其他容器来帮你提供:Spring里面就是IOC容器. 例如: 在Spring里面经常需要在Service这个装配一个Dao,一般是使用@Autowired注解:类似如下 public Class ServiceImpl{ @Autowired Dao dao; public void getData(){ dao.getData(); } 在这里未初始化Dao直接使用是会报出空指针异常的,那么在Spring里面的做法就是通过反

  • 浅析Java的Spring框架中IOC容器容器的应用

    Spring容器是Spring框架的核心.容器将创建对象,它们连接在一起,配置它们,并从创建到销毁管理他们的整个生命周期.在Spring容器使用依赖注入(DI)来管理组成应用程序的组件.这些对象被称为Spring Beans. 容器获得其上的哪些对象进行实例化,配置和组装通过阅读提供的配置元数据的说明.配置元数据可以通过XML,Java注释或Java代码来表示.下面的图是Spring如何工作的高层次图. Spring IoC容器是利用Java的POJO类和配置元数据的产生完全配置和可执行的系统或

  • 使用Java反射模拟实现Spring的IoC容器的操作

    目录 实现的功能: 项目结构 下面是程序的项目结构图: 自定义注解 容器实现 测试 实体类User的定义: 实现的功能: 默认情况下将扫描整个项目的文件 可以使用@ComponentScan注解配置扫描路径 只将被@Component注解修饰的类装载到容器中 可以使用@AutoWired注解实现自动装配 读取配置文件中的声明的类并注册到容器中 项目结构 下面是程序的项目结构图: 自定义注解 下面是自定义的三个注解: @AutoWired,@Component,@ComponentScan. @T

  • Java反射机制在Spring IOC中的应用详解

    目录 Java反射机制在Spring IOC的应用 下面是Spring通过配置进行实例化对象 Spring的配置如下所示 实现一下Spring底层处理原理 反射机制.反射机制的作用.反射机制的功能 1.反射机制的作用 2.Java反射机制的功能 3.反射机制相关的重要的类有哪些? Java反射机制在Spring IOC的应用 IOC:即"控制反转",不是什么技术,而是一种思想.使用IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制. 本篇文章主要讲解一下IOC

  • spring在IoC容器中装配Bean详解

    1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean实例放入bean缓存池: 应用程序使用bean. 1.2.基于xml的配置 (1)xml文件概述 xmlns------默认命名空间 xmlns:xsi-------标准命名空间,用于指定自定义命名空间的schema文件 xmlns:xxx="aaaaa"-------自定义命名空间,xx

  • Spring框架IOC容器底层原理详解

    目录 1.什么是IOC 2.IOC容器的底层原理 3.那么上边提到的三种技术如何实现IOC的呢 4.IOC(接口) 1.什么是IOC IOC – Inverse of Control,控制反转,将对象的创建权力反转给Spring框架! 在java当中一个类想要使用另一个类的方法,就必须在这个类当中创建这个类的对象,那么可能会出现如下情况, 比如A类当中创建着B对象,B类当中有C对象,C类当中有A对象,这个如果一个类出了问题,那么可能会导致这个框架出现问题. Spring 将创建对象的权利给了IO

  • 利用Java反射机制实现对象相同字段的复制操作

    一.如何实现不同类型对象之间的复制问题? 1.为什么会有这个问题? 近来在进行一个项目开发的时候,为了隐藏后端数据库表结构.同时也为了配合给前端一个更友好的API接口文档(swagger API文档),我采用POJO来对应数据表结构,使用VO来给传递前端要展示的数据,同时使用DTO来进行请求参数的封装.以上是一个具体的场景,可以发现这样子一个现象:POJO.VO.DTO对象是同一个数据的不同视图,所以会有很多相同的字段,由于不同的地方使用不同的对象,无可避免的会存在对象之间的值迁移问题,迁移的一

  • Spring为IOC容器注入Bean的五种方式详解

    这篇文章主要介绍了Spring为IOC容器注入Bean的五种方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一 @Import导入组件,id默认是组件的全类名 //类中组件统一设置.满足当前条件,这个类中配置的所有bean注册才能生效: @Conditional({WindowsCondition.class}) @Configuration @Import({Color.class,Red.class,MyImportSelector

  • Spring创建IOC容器的方式解析

    1.直接得到 IOC 容器对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); 封装起来: public class ApplicationContextUtil { private static ApplicationContext applicationContext = null; public ApplicationCont

  • Java Spring 控制反转(IOC)容器详解

    目录 什么是容器? 无侵入容器 IOC控制反转 IOC理论推导 传统应用程序开发的弊端 "注入"机制 小结 IOC本质 DI(依赖注入) 总结 IoC 容器是 Spring 的核心,也可以称为 Spring 容器.Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期. Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象.由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是

  • spring中ioc是什么

    IoC--Inversion of Control,控制反转 在Java开发中,IoC意味着将你设计好的类交给系统去控制,而不是在你的类内部控制.IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则. DI--Dependency Injection(依赖注入) 即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中. 依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活.

  • 简单实现Spring的IOC原理详解

    控制反转(InversionofControl,缩写为IoC) 简单来说就是当自己需要一个对象的时候不需要自己手动去new一个,而是由其他容器来帮你提供:Spring里面就是IOC容器. 例如: 在Spring里面经常需要在Service这个装配一个Dao,一般是使用@Autowired注解:类似如下 public Class ServiceImpl{ @Autowired Dao dao; public void getData(){ dao.getData(); } 在这里未初始化Dao直接

随机推荐