当前位置: 首页 > news >正文

手写Spring IOC-简易版

目录

    • 项目结构
      • entity
      • dao
        • IUserDao
        • UserDaoImpl
      • service
        • IUserService
        • UserServiceImpl
      • ApplicationContext
    • 配置文件初始化 IOC 容器
      • RunApplication
    • 注解初始化 IOC 容器
      • @Bean
      • @Autowired
    • Reference

项目结构

在这里插入图片描述

entity

  1. User
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class User implements Serializable {private String username;private String password;private int age;
}

dao

IUserDao
public interface IUserDao {/*** Add user*/int addUser(User user);/*** Get user by id.*/User getUserById(int id);/*** Get all users.*/List<User> getAllUsers();/*** Get users by name.*/List<User> getUsersByName(String name);
}
UserDaoImpl
public class UserDaoImpl implements IUserDao {@Overridepublic int addUser(User user) {System.out.println("Dao: addUser()");return 0;}@Overridepublic User getUserById(int id) {System.out.println("Dao: getUserById()");return null;}@Overridepublic List<User> getAllUsers() {System.out.println("Dao: getAllUsers()");return Collections.emptyList();}@Overridepublic List<User> getUsersByName(String name) {System.out.println("Dao: getUsersByName()");return Collections.emptyList();}
}

service

IUserService
public interface IUserService {void login();void register();
}
UserServiceImpl
public class UserServiceImpl implements IUserService {/*** Each time this interface is called, aenw UserDaoImpl object is created,* resulting in resource waste or causing OutOfMemoryError in serious cases.*/IUserDao userDao = new UserDaoImpl();@Overridepublic void login() {userDao.getAllUsers();System.out.println("This is implementation o login method.");}@Overridepublic void register() {userDao.getAllUsers();System.out.println("This is implementation o register method.");}
}

假设 Web 应用程序现在运行正常。
每次用户调用 IUserService 接口时,都会创建一个 IUserDaoImpl 对象,这会导致资源浪费,在严重情况下可能会引发 OutOfMemoryError

IUserDao userDao = new UserDaoImpl();

如何解决这个问题****❓

我们可以将 IUserDaoImplClass 作为 key,IUserDaoImpl 实例作为 value 存储在一个HashMap<Class, Object>中。

  1. 在 Web 应用程序运行之前,将 ApplicationConext 类中的对象加载并初始化到 IOC 容器中。
  2. 当调用 IUserService 接口时,Java 会获取 IUserDaoImpl 对象,而不再需要创建新的 IUserDaoImpl对象。

ApplicationContext

public class ApplicationContext {// Store bean objects in a Hashmap, equivalent to ioc container.private HashMap<Class<?>, Object> beanFactory = new HashMap<>();/*** init ApplicationContext, and put bean objects into IOC container.*/public void initContext() throws IOException, ClassNotFoundException {// Load specified objects to IOC container.beanFactory.put(IUserService.class,new UserServiceImpl());beanFactory.put(IUserDao.class,new UserDaoImpl());}/*** get bean object by class from IOC container.*/public Object getBean(Class<?> clazz) {return beanFactory.get(clazz);}/*** return all bean objects in IOC container.*/public HashMap<Class<?>, Object> getAllBeans() {return beanFactory;}
}

配置文件初始化 IOC 容器

❓****问题:如上述代码所示,我们可以将指定的对象加载到IOC容器中,但如果我们需要添加一些新对象时,就必须修改源代码,这非常麻烦。

public void initContext() throws IOException, ClassNotFoundException {// Load specified objects to IOC container.beanFactory.put(IUserService.class,new UserServiceImpl());beanFactory.put(IUserDao.class,new UserDaoImpl());// Add new objects into beanFactory.// beanFactory.put(xxxx.class,new xxxxx());// ...........
}

为了解决这个问题,我们可以使用 Java 中的反射。

  1. 创建一个application.properties文件,在其中可以添加我们需要的对象信息。
IOC.service.IUserService = IOC.service.Impl.UserServiceImpl
IOC.dao.IUserDao = IOC.dao.Impl.UserDaoImpl
  1. 利用反射机制加载对象,并存储到 IOC 容器。
public void initContext() throws IOException, ClassNotFoundException {// Load application.properties file and Get information of bean object.Properties properties = new Properties();properties.load(new FileInputStream("src/main/resources/application.properties"));Set<Object> keys = properties.keySet();for (Object key : keys) {Class<?> keyClass = Class.forName(key.toString());String value = properties.getProperty(key.toString());Class<?> valueClass = Class.forName(value);Object instance = valueClass.newInstance();// put bean object into IOC container.beanFactory.put(keyClass, instance);}
}

RunApplication

public class RunApplication {public static void main(String[] args) throws IOException, ClassNotFoundException {ApplicationContext applicationContext = new ApplicationContext();applicationContext.initContext();// Get all beans.HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();Set<Class<?>> classes = allBeans.keySet();for (Class<?> clazz : classes) {System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));}}
}


注解初始化 IOC 容器

@Bean

  1. 新增 annotation 包,创建 Bean 注解类。
@Target(ElementType.TYPE)    // 只能修饰类型元素
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {}
  1. UserDaoImplUserServiceImpl 类上加上 **@Bean **注解。
@Bean
public class UserServiceImpl implements IUserService {}@Bean
public class UserDaoImpl implements IUserDao {}
  1. ApplicationContext 类中添加使用注解初始化 bean 对象。
// root path of project.
private String filePath;/**
* Load all bean objects with annotation @Bean from the project path.
*/
public void initContextByAnnotation() throws ClassNotFoundException, InstantiationException, IllegalAccessException {// Get the project path.filePath = Objects.requireNonNull(ApplicationContext.class.getClassLoader().getResource("")).getFile();// Load all bean objects into IOC container.loadOne(new File(filePath));
}/**
* Load all class file with @Bean.
*/
public void loadOne(File fileParent) throws ClassNotFoundException, InstantiationException, IllegalAccessException {if(!fileParent.isDirectory()){return;}File[] childrenFiles = fileParent.listFiles();if (childrenFiles == null) {return;}for (File child : childrenFiles) {if (child.isDirectory()) {loadOne(child);} else {String pathWithClass = child.getAbsolutePath().substring(filePath.length() - 1);// Get file name like UserServiceImpl.classif (pathWithClass.contains(".class")) {String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");Class<?> clazz = Class.forName(fullName);if (!clazz.isInterface()) {Bean annotation = clazz.getAnnotation(Bean.class);if (annotation != null) {Object instance = clazz.newInstance();Class<?>[] interfacesList = clazz.getInterfaces();// interface as key, if object has no interface.if (interfacesList.length > 0) {Class<?> interfaceClass = interfacesList[0];beanFactory.put(interfaceClass, instance);}// clazz as key, if object has interface.else {beanFactory.put(clazz, instance);}}}}}}
}
  1. 修改 RunApplication 启动类中使用注解初始化 IOC 容器。
public class RunApplication {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {ApplicationContext applicationContext = new ApplicationContext();//applicationContext.initContext();// Load beans by @Bean.applicationContext.initContextByAnnotation();// Get all beans.HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();Set<Class<?>> classes = allBeans.keySet();for (Class<?> clazz : classes) {System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));}}
}

输出:

@Autowired

  1. 在 annotation 包中添加 Aurowired 注解类。
@Target(ElementType.FIELD)    // 只能修饰成员变量
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
  1. UserServiceImpl 成员变量添加 @Autowired 注解。
@Bean
public class UserServiceImpl implements IUserService {@Autowiredprivate IUserDao userDao;
}
  1. ApplicationContext 类中添加使用注解初始化 bean 对象后,初始化各个 bean 对象的成员变量。
    1. 这里默认只实现了根据类型初始化成员变量(在 springboot 中支持类型名称)。
/**
* Load all bean objects with annotation @Bean from the project path.
*/
public void initContextByAnnotation() throws ClassNotFoundException, InstantiationException, IllegalAccessException {// Get the project path.filePath = Objects.requireNonNull(ApplicationContext.class.getClassLoader().getResource("")).getFile();// Load all bean objects into IOC container.loadOne(new File(filePath));// initiate fields of object.assembleObject();
}/**
* @Autowired annotation to initiate fields of bean objects.
*/
public void assembleObject() throws IllegalAccessException {Set<Class<?>> keys = beanFactory.keySet();for (Class<?> key : keys) {Object value = beanFactory.get(key);Class<?> clazz = value.getClass();// Set value of field by @Autowired.Field[] declaredFields = clazz.getDeclaredFields();for (Field filed : declaredFields) {Autowired annotation = filed.getAnnotation(Autowired.class);if (annotation != null) {filed.setAccessible(true);// Get bean object by type from IOC container.Object object = beanFactory.get(filed.getType());filed.set(value, object);}}}
}
  1. 答复
public class RunApplication {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {ApplicationContext applicationContext = new ApplicationContext();//applicationContext.initContext();applicationContext.initContextByAnnotation();// Get all beans.HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();Set<Class<?>> classes = allBeans.keySet();for (Class<?> clazz : classes) {System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));}// Get bean object by class type.IUserService bean = (IUserService) applicationContext.getBean(IUserService.class);// call login method.bean.login();}
}

输出:

Dao 层调用了 getAllUsers() 方法,说明依赖注入成功。

源码:https://github.com/RainbowJier/HandWrittenSpring

Reference

  1. 一堂课深挖java反射机制,手撸一个ioc,实现自动装配,顺便spring也了解了_哔哩哔哩_bilibili
http://www.lryc.cn/news/462085.html

相关文章:

  • 【算法题】62. 不同路径(LeetCode)
  • 【VUE】Vue中的data属性为什么是一个函数而不是一个对象
  • ddos攻击介绍和排查方法
  • git clone --single-branch 提升效率
  • 代码随想录算法训练营第十天|1. 两数之和,第454题.四数相加II
  • 龙迅LT8911EX LVDS转EDP 点屏,大批量出货产品
  • 浅谈Oracle之游标
  • 基于在线教育系统源码的企业培训平台开发解决方案详解
  • Whisper 音视频转写
  • 【详尽-实战篇】使用Springboot生成自带logo或者图片的二维码-扫描二维码可以跳转到指定的页面-Zing-core
  • vue跨标签页通信(或跨窗口)详细教程
  • 【VUE】Vue3通过数组下标更改数组视图为什么会更新?
  • 前端转换double数据,保留两位小数
  • 【实战案例】JSR303统一校验与SpringBoot项目的整合
  • 忘记了系统root密码,如何重置root密码?
  • 7-基于国产化FT-M6678+JFM7K325T的6U CPCI信号处理卡
  • 计算机毕业设计 | SSM超市进销存管理系统(附源码)
  • 手撕数据结构 —— 堆(C语言讲解)
  • TS和JS中,string与String的区别
  • jna调用c++动态库linux测试
  • 智诊小助手TF卡记录文件导出
  • Jetpack-ViewModel+LiveData+DataBinding
  • Servlet[springmvc]的Servlet.init()引发异常
  • 总结:SQL查询变慢,常见原因分析!
  • 基于webrtc实现音视频通信
  • 【多版本并发控制(MVCC)】
  • 常见漏洞及webshell工具的流量特征
  • python学习-怎么在Pycharm写代码
  • 牛客周赛63(C++实现)
  • 高级英语1第四版教材全解pdf课后答案+课文翻译张汉熙