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

作者:Code0cean 时间:2023-06-03 17:37:42 

实现的功能:

  • 默认情况下将扫描整个项目的文件

  • 可以使用@ComponentScan注解配置扫描路径

  • 只将被@Component注解修饰的类装载到容器中

  • 可以使用@AutoWired注解实现自动装配

  • 读取配置文件中的声明的类并注册到容器中

项目结构

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

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

自定义注解

下面是自定义的三个注解: @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方法
验证用户【张三】登录

来源:https://blog.csdn.net/huangjhai/article/details/106177527

标签:Java,模拟,Spring,IoC容器
0
投稿

猜你喜欢

  • Android ProgressBar进度条使用详解

    2023-01-22 00:53:38
  • Jenkins自动化打包为war包

    2021-08-08 20:25:49
  • springboot实现通过路径从磁盘直接读取图片

    2023-09-01 03:01:01
  • Android使用Retrofit2.0技术仿微信发说说

    2021-05-24 03:09:18
  • java实现分布式项目搭建的方法

    2022-10-13 10:42:36
  • Android编程创建桌面快捷方式的常用方法小结【2种方法】

    2023-12-07 00:50:35
  • Android自定义星星可滑动评分控件

    2022-03-18 10:23:03
  • C#将PPT文件转换成PDF文件

    2022-09-08 20:33:21
  • 详解DES&3DES算法的原理以及C#和JS的实现

    2021-06-29 21:58:07
  • 详解SpringBoot如何实现统一后端返回格式

    2022-11-27 05:26:24
  • Java二维数组查找功能代码实现

    2023-01-04 19:47:17
  • Java利用读写的方式实现音频播放代码实例

    2022-08-21 15:38:52
  • Android实现文字上下滚动效果

    2023-02-02 07:40:00
  • Android自定义View仿IOS圆盘时间选择器

    2023-10-12 20:40:32
  • Kotlin挂起函数原理示例剖析

    2023-11-10 21:52:13
  • Java常用的八种排序算法及代码实现+图解

    2022-04-09 13:30:06
  • Spring事务传播中嵌套调用实现方法详细介绍

    2021-08-31 22:34:24
  • Java中PriorityQueue实现最小堆和最大堆的用法

    2022-03-25 14:32:18
  • C#使用TextBox作数据输入方法

    2023-11-16 22:26:14
  • C#把数字转换成大写金额的代码实例

    2022-03-21 08:08:48
  • asp之家 软件编程 m.aspxhome.com