手写IOC

IOC原理(手写IOC)

Spring框架的IOC是基于反射机制实现的。

反射回顾

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制,简单来说,反射机制就是程序在运行时能够获取自身的信息。

示例

实体类Car

package com.louis.reflect;/*** @author XRY* @date 2023年06月26日8:59*/
public class Car {private String bind;private int lifeTime;private String color;public Car() {}public Car(String bind, int lifeTime, String color) {this.bind = bind;this.lifeTime = lifeTime;this.color = color;}//普通方法private void use(){System.out.println("私有方法..........");}public String getBind() {return bind;}public void setBind(String bind) {this.bind = bind;}public int getLifeTime() {return lifeTime;}public void setLifeTime(int lifeTime) {this.lifeTime = lifeTime;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Car{" +"bind='" + bind + '\'' +", lifeTime=" + lifeTime +", color='" + color + '\'' +'}';}
}

1、获取class对象

@Test
public void testGetClass() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1、类名.classClass<Car> clazz01 = Car.class;//2、对象.getClass()Class<? extends Car> clazz02 = new Car().getClass();//3、Class.forName("全路径")Class<?> clazz03 = Class.forName("com.louis.reflect.Car");//实例化Car car = (Car)clazz03.getDeclaredConstructor().newInstance();logger.info("car" + car);
}/*[2023-06-26 09:16:13:036] [INFO] - com.louis.reflect.TestCar.testGetClass(TestCar.java:28) - carcom.louis.reflect.Car@2445445a*/

2、获取构造方法

@Test
public void testCacheConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Class<Car> clazz = Car.class;//获取所有构造//getConstructors针对public方法,如果是private则不能够使用这种方法获取,如果构造方法中包含私有的方法,则需要使用getDeclaredConstructorsConstructor<?>[] constructors = clazz.getConstructors();for (Constructor<?> constructor : constructors) {logger.info("constructor" + constructor.getName() + "参数个数" + constructor.getParameterCount());/** [2023-06-26 09:57:16:855] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数0[2023-06-26 09:57:16:858] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数3* */}//指定有参数的构造去创建对象//1、构造是public,如果目标对象是private则会报错/*Constructor<Car> haveParameterPub = clazz.getConstructor(String.class, int.class, String.class);Car car = haveParameterPub.newInstance("野马", 1, "blue");/*[2023-06-26 10:07:47:947] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0[2023-06-26 10:07:47:950] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3汽车Car{bind='野马', lifeTime=1, color='blue'}*///2、构造是privateConstructor<Car> haveParameterPri = clazz.getDeclaredConstructor(String.class, int.class, String.class);haveParameterPri.setAccessible(true);//设置访问权限,如果为false不能够访问Car car1 = haveParameterPri.newInstance("悍马", 2, "yellow");System.out.println("car1 = " + car1);/*[2023-06-26 10:13:58:492] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0[2023-06-26 10:13:58:496] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3car1 = Car{bind='悍马', lifeTime=2, color='yellow'}* */
}

3、获取属性

@Test
public void getAttribute() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//获得类的字节码文件,类、对象和class.forNameClass<?> carClass = Class.forName("com.louis.reflect.Car");//实例化Car car = (Car)carClass.getDeclaredConstructor().newInstance();//获取其中所有的public方法Field[] fields = carClass.getFields();//获取所有的属性,包括私有的属性Field[] declaredFields = carClass.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.println("declaredField.getName() = " + declaredField.getName());//给属性赋值if(declaredField.getName().equals("bind")){//设置允许访问declaredField.setAccessible(true);//传入对象和属性值declaredField.set(car,"野马");}System.out.println("car" + car);}/** declaredField.getName() = bindcarCar{bind='野马', lifeTime=0, color='null'}declaredField.getName() = lifeTimecarCar{bind='野马', lifeTime=0, color='null'}declaredField.getName() = colorcarCar{bind='野马', lifeTime=0, color='null'}* */
}

4、获取方法

@Test
public void getMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Car car = new Car("Benz", 10, "black");Class<? extends Car> clazz = car.getClass();//1、public方法,不会取到私有Method[] methods = clazz.getMethods();for (Method method : methods) {
//            System.out.println(method.getName());//执行方法toStringif(method.getName().equals("toString")){String invoke = (String)method.invoke(car);System.out.println("toString执行了" + invoke);/*toString执行了Car{bind='Benz', lifeTime=10, color='black'}*/}}//2、private方法Method[] methodsAll = clazz.getDeclaredMethods();for (Method methodA : methodsAll) {//执行私有方法if(methodA.getName().equals("use")){methodA.setAccessible(true);methodA.invoke(car);}}/*私有方法..........*/
}

实现Spring的IoC

实现过程

1、创建模块spring-ioc

在这里插入图片描述

2、创建测试类service、dao

在这里插入图片描述

接口实现

@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;public void add(){System.out.println("service......");//调用dao的方法userDao.show();}
}

3、创建两个注解@Bean(创建对象)、 @Di(属性注入)

@Bean

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {//用于创建对象
}

@Di

package com.louis.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {//用于注入属性
}

4、创建bean容器接口ApplicationContext,定义方法、返回对象

public interface ApplicationContext {//在IOC中BeanFactory返回的是一个工厂Object getBean(Class clazz);
}

5、实现bean容器接口,创建实现类

(1)返回对象
(2)根据包规则加载bean(扫描路径下包含@Bean注解的类,并将这些类通过反射实例化)

package com.louis.bean.impl;import com.louis.annotation.Bean;
import com.louis.annotation.Di;
import com.louis.bean.ApplicationContext;import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** @author XRY* @date 2023年06月26日16:39*/
public class AnnotationApplicationContext implements ApplicationContext {//模拟IOC,创建map集合,放置bean对象private Map<Class, Object> beanFactory = new HashMap<>();private static String rootPath;//返回对象@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}//设置包扫描规则//当前包及其子包,将带有@Bean注解的类通过反射实现实例化public AnnotationApplicationContext(String basePackage){//扫描路径//1、将.替换成\String packagePath = basePackage.replaceAll("\\.", "\\\\");//2、获取包的绝对路径,编译之后的路径try {Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);while(urls.hasMoreElements()){URL url = urls.nextElement();//转码String filePath = URLDecoder.decode(url.getFile(), "utf-8");//获取包前面路劲部分,字符串截取rootPath = filePath.substring(0, filePath.length() - packagePath.length());
//                System.out.println("filePath = " + filePath);//根据获得的路径进行包扫描loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}//属性注入loadDi();}/*** 包扫描过程,进行实例化* @param file*/private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1、判断当前路径下的东西是否是文件夹,如果不是就不需要继续往下查找if(file.isDirectory()){//2、获取文件夹里面所有内容File[] childFiles = file.listFiles();//3、判断文件夹里面为空,直接返回if(childFiles == null || childFiles.length == 0){return;}//4、如果文件夹里面不为空,遍历文件夹中所有内容for(File child:childFiles){//4.1遍历得到每个File对象,继续判断,如果是文件夹,递归if(child.isDirectory()){//递归loadBean(child);}else{//4.2遍历得到的File对象不是文件夹//4.3得到包路径+类名称部分String packagePath = child.getAbsolutePath().substring(rootPath.length() - 1);//4.4判断当前文件类型是否为.class,如果是,将路径中的\替换为.并将.class文件去掉if(packagePath.contains(".class")){String allName = packagePath.replaceAll("\\\\", "\\.").replaceAll(".class", "");//4.5判断类上面是否有注解@Bean,如果有进行实例化过程//4.5.1获取类的classClass<?> clazz = Class.forName(allName);//4.5.2判断不是interfaceif(!clazz.isInterface()){Bean annotation = clazz.getAnnotation(Bean.class);if(annotation != null){//4.5.3实例化Object instance = clazz.getConstructor().newInstance();//4.7把对象实例化之后,放到map集合beanFactory//4.7.1判断当前类如果有接口,让接口class作为map的key,如果没有接口将自己的class作为keyif(clazz.getInterfaces().length > 0){beanFactory.put(clazz.getInterfaces()[0], instance);}else{beanFactory.put(clazz,instance);}}}}}}}}//    public static void main(String[] args) {
//        new AnnotationApplicationContext("com.louis");
//    }//属性注入private void loadDi(){//实例化的对象都在beanFactory的map集合里//1、遍历beanFactory的map集合,entrySet()用来获取到对象的集合Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();for (Map.Entry<Class, Object> entry : entries) {//2、获取map集合中每个对象(value),每个对象获取到属性Object value = entry.getValue();//获取对象ClassClass<?> clazz = value.getClass();//获取每个对象中的属性Field[] declaredFields = clazz.getDeclaredFields();//3、遍历得到的每个对象属性的数组,得到每个属性for (Field field : declaredFields) {//4、判断属性上是否有@Di注解,Di annotation = field.getAnnotation(Di.class);if(annotation != null){//如果是私有属性可以设置一个值field.setAccessible(true);//有Di注解就把对象进行设置(注入)try {field.set(value, beanFactory.get(field.getType()));} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}}
}

6、测试

public class TestBean {public static void main(String[] args){ApplicationContext context = new AnnotationApplicationContext("com.louis");UserService userService = (UserService) context.getBean(UserService.class);
//      UserService userService1 = new UserServiceImpl();userService.add();}
}/*
* service......
UserDao................
* */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28660.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JS】设置滚动属性默认自动滚动到底部(overflow:scroll;)

文章目录 核心代码应用场景 核心代码 设置滚动属性默认自动滚动到底部&#xff1a; // 获取设置了滚动属性的div标签 const div document.getElementById(conversationalDiv); // 设置滚动的顶点坐标为滚动的总高度 div.scrollTop div.scrollHeight;应用场景 场景&#xff…

050、事务设计之Percolator事务模型

Percolator 背景 Bigtable: 大表打散每行到各个节点&#xff0c;每一行作为一个kv。解决的问题 一个事务涉及的行在多个节点&#xff0c;如何用单行对一个事务进行控制&#xff0c;实现原子性。 快照隔离级别&#xff08;snapshot &#xff09; 白色点&#xff1a;代表事务开始…

flask基本用法小白教程+按钮跳转到指定页面+python和pip安装(后附)

一、flask学习教程&#xff1a; 1.1 基本程序&#xff1a; 大家可以在pycharm中复制如下代码&#xff0c;先感受一下flask的基本用法&#xff1a; 点击链接可进入浏览器查看程序运行的结果&#xff0c;在127.0.0.1:5000后面添上/test1/等设定的文字&#xff0c;可查看不同函…

[RocketMQ] Broker CommitLogDispatcher 异步构建ConsumeQueue和IndexFile源码解析 (十四)

CommitLogDispatcherBuildConsumeQueue: 异步构建ConsumerQueue。CommitLogDispatcherBuildIndex: 异步构建IndexFile。 文章目录 1.CommitLogDispatcherBuildConsumeQueue构建ConsumeQueue1.1 putMessagePositionInfo写入消息位置信息1.2 findConsumeQueue查找ConsumeQueue1.2…

flutter开发实战-卡片翻转动画效果Transform+IndexedStack+rotateAnimation

flutter开发实战-实现卡片翻转动画效果 之前开发中遇到了商品卡片翻转&#xff0c;商品正面是商品图片、商品名称&#xff1b;背面是商品价格&#xff0c;需要做卡片翻转动画。 动画实现即&#xff1a;在一段时间内&#xff0c;快速地多次改变UI外观&#xff1b;由于人眼会产生…

单例模式、指令重排序、锁、有序性

今天在回顾单例模式时&#xff0c;我们都知道懒汉式单例中有一种叫做双重检查锁的单例模式。 我们来看下下面的代码有没有问题&#xff1a; 这段代码我们可以看到&#xff0c;即优化了性能&#xff0c;在多线程情况下&#xff0c;如果实例不为空了&#xff0c;则直接返回了。…

[element-ui] el-select,虚拟滚动(vue-virtual-scroll-list)

一、问题描述 表单中某下拉框&#xff0c;由于数据过多&#xff0c;选择的时候会因为数据量过大导致页面卡顿&#xff0c;于是对于el-select进行二次封装&#xff0c;实现虚拟滚动。 二、实现如下&#xff1a; 看起来是加载了全部数据&#xff0c;实际上只加载了自己设定的1…

无需学习Python,一个公式搞定领导想看的大屏

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 不要让“做不了”成为数字化转型的障碍 随着数字化的脚步加快&#xff0c;越来越多的企…

Spring Batch之读数据库—JdbcCursorItemReader之自定义PreparedStatementSetter(三十八)

一、自定义PreparedStatementSetter 详情参考我的另一篇博客&#xff1a; Spring Batch之读数据库——JdbcCursorItemReader&#xff08;三十五&#xff09;_人……杰的博客-CSDN博客 二、项目实例 1.项目实例 2.代码实现 BatchMain.java&#xff1a; package com.xj.dem…

制作Visual Studio离线安装包

vs2015之后官网就不提供离线安装包了&#xff0c;使用离线安装包就需要自己手动制作一个&#xff1b; 以vs2019为例&#xff1a; 先去官网下载在线安装器 官网下载地址&#xff1a;Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com) 展开2019的标签…

从小白到大神之路之学习运维第62天--------Ansible自动化运维工具(playbook配置深入了解2.0)

第三阶段基础 时 间&#xff1a;2023年7月17日 参加人&#xff1a;全班人员 内 容&#xff1a; playbook配置深入了解2.0 目录 一、角色 实验案例&#xff1a;&#xff08;安装Mariadb&#xff09; 二、变量 &#xff08;一&#xff09;在playbook中使用自定义变量&#xff1…

STM32芯片型号命名规则

意法半导体STM32和STM8系列的芯片可以通过下面一张图来确定芯片的各项参数&#xff0c;帮助选型。 STM32型号的说明&#xff1a;以STM32F103RBT6这个型号的芯片为例&#xff0c;该型号的组成为7个部分&#xff0c;其命名规则如下&#xff1a; ![在这里插入图片描述](https:…