IOC理解

news/2024/10/21 10:09:02/

1. Java反射

  Java反射机制存在于运行时,对于任意一个类,都能获取这个类的所有属性和方法,对于任意一个对象,都能通过反射机制去调用它的属性和方法,这种动态获取类信息及动态调用对象方法的功能称为Java反射机制,即在运行时获取Java类的相关信息。

2. 反射

2.1 实体类Student

public class Student {private String name;private Integer age;private String sex;public Student() {}public Student(String name, Integer age, String sex) {this.name = name;this.age = age;this.sex = sex;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}
}

2.2 测试类StudentTest

2.2.1 获取Class对象

获取Class对象总共有三种方法:

  1. 通过类名.class获取;
  2. 通过实例名.getClass()获取;
  3. 通过Class.forName(“全路径”)进行获取;

示例代码:

public void studentTest() throws Exception {/*** 获取Student类有三种方法* 1. 通过类名.class获取   Student.class* 2. 通过对象名.getClass()获取 new Student().getClass()* 3. 获取Class.forName("全路径")获取*///1Class clazz1 = Student.class;//2Class clazz2 = new Student().getClass();//3Class clazz3 = Class.forName("pers.liyongxing.reflect.Student");//实例化Student car = (Student) clazz3.getDeclaredConstructor().newInstance();}

2.2.2 获取构造方法

获取构造方法分为两种,分别为通过getConstructors和getDeclaredConstructors来获取。

  1. getConstructors方法是获取所有公共的构造方法
  2. getDeclaredConstructors方法是获取所有的构造方法

示例代码:

public void constructorTest() throws Exception {Class clazz = Student.class;//1. 获取无参数的构造方法Constructor[] constructors = clazz.getConstructors();Constructor[] constructors1 = clazz.getDeclaredConstructors();for(Constructor constructor : constructors){System.out.println("构造类型:" + constructor.getName() + ",构造参数个数:" + constructor.getParameterCount());}//2. 获取有参数的构造方法并实例化 公共构造方法Student student = (Student)clazz.getConstructor(String.class, Integer.class, String.class).newInstance("张三", 18, "男");System.out.println(student);//2. 获取有参数的构造方法并实例化 私有构造方法Constructor c2 = clazz.getDeclaredConstructor(String.class, Integer.class);c2.setAccessible(true);//允许方法私有构造方法Student student1 = (Student) c2.newInstance("张华", 23);System.out.println(student1);}

2.2.3 获取属性和方法

和上面情况类似,如果是私有方法,需要通过declared*方法进行获取,并对相应的属性或者方法设置允许访问(setAccessible(true));
获取属性示例代码:

public void attributeTest() throws Exception{Class clazz = Student.class;Student student = (Student) clazz.getDeclaredConstructor().newInstance();//获取属性Field[] fields = clazz.getDeclaredFields();for(Field field : fields){if(field.getName().equals("name")){field.setAccessible(true);field.set(student,"李四");}System.out.println(field.getName());System.out.println(student);}}

获取方法并通过执行invoke()方法执行示例代码:

public void methodTest() throws  Exception{Class clazz = Student.class;Student student = (Student) clazz.getDeclaredConstructor().newInstance();Method[] methods = clazz.getDeclaredMethods();for(Method method : methods){if(method.getName().equals("toString")){String invoke = (String)method.invoke(student);System.out.println("toString方法执行了.." + invoke);}}}

3 手写IOC(简单版)

  自定义Bean,主要分为两个:类型注入和属性注入:

3.1 自定义Bean

  1. 类型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 {
}
  1. 属性Bean
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 {
}

3.2 定义测试类

  1. UserService:
public interface UserService {public void add();
}
  1. UserServiceImpl
@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;@Overridepublic void add() {System.out.println("service.....");userDao.add();}
}
  1. UserDao
public interface UserDao {public void add();
}
  1. UserDaoImpl
@Bean
public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("add...");}
}
  1. 测试类AnnotationTest
public void test(){ApplicationContext context = new AnnotationApplicationContext("pers.liyongxing");UserService userService = (UserService) context.getBean(UserService.class);System.out.println(userService);userService.add();}

3.3 自定义ApplicationContext

自定义ApplicationContext中的AnnotationApplicationContext,实现对路径中的注解进行扫描,如果为类型注入,则将通过反射机制创建该类的对象,并将其存储到Map集合中,最后遍历Map集合,如果当前对象中存在Di属性注入,则将该属性注入其对应的类型。

3.3.1 自定义ApplicationContext接口:

public interface ApplicationContext {Object getBean(Class clazz);
}

3.3.2 创建AnnotationApplicationContext类:

  创建AnnotationApplicationContext类并实现ApplicationContext接口;
属性:
beanFactory用于存储类型注入的bean对象;rootPath用于存储文件路径的公共部分。

private static Map<Class,Object> beanFactory = new HashMap<>();
private static String rootPath;
  1. 格式化处理包路径,并解析出包路径下的所有文件及文件夹,用于后面的类型注入
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());//包扫描loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}//属性注入loadDi();}

注意下面操作的文件路径为target目录下:
2. 类型注入:

  1. 首先判断当前File对象是否为文件夹,如果是文件夹则进行遍历文件判断,如果不是则返回;
  2. 如果当前文件夹为空,直接返回;
  3. 遍历当前文件夹下的所有File对象,如果当前File仍然为文件夹,则返回第二步进行执行,如果不是,则进行下一步。
  4. 截取掉文件头的路径,获取相对于包的绝对路径。
  5. 判断当前文件是否为.class文件,如果是.class对象,进行下一步
  6. 格式化处理类路径,通过Class.forName(“路径”)获取Class对象,通过isInterface()判断当前对象是否为接口类型,如果当前对象不是接口类型,则进行下一步。
  7. 判断当前对象是否包含Bean注解,如果包含,进行实例化,并将实例化对象存储到beanFactory集合中:
    7.1如果当前类实现接口,则key为接口类型,value为实例化对象。
    7.2如果当前类没有实现接口,则key为class对象,value为实例化对象.
    示例代码:
private static void loadBean(File file) throws Exception {//1. 判断当前内容是否为文件夹if(!file.isDirectory()){return ;}//2 获取文件夹中的内容File[] childrenFiles = file.listFiles();//3 判断文件夹为空 直接返回if(childrenFiles == null || childrenFiles.length == 0){return ;}//4 文件夹不为空,遍历文件夹所有内容for(File childFile : childrenFiles){//4.1 判断得到每个File对象,继续判断,如果还是文件夹,继续遍历if(childFile.isDirectory()){//继续遍历loadBean(childFile);}else{//4.2 遍历得到File对象不是文件夹,是文件,//4.3 得到包路径+类名称-字符串截取String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);//4.4 判断当前文件类型是否为.classif(pathWithClass.contains(".class")){//4.5 如果是.class类型,把路径中\换成.  把.class去掉String pathName = pathWithClass.replaceAll("\\\\", ".").replace(".class","");//4.6 判断类上是否有注解@Bean,如果有,则进行实例化//4.6.1 获取类的class对象Class<?> clazz = Class.forName(pathName);//4.6.2 判断是否为接口if(!clazz.isInterface()){//4.6.3判断是否有注解Bean annotation = clazz.getAnnotation(Bean.class);if(annotation != null){//4.6.4 进行实例化Object instance = clazz.getDeclaredConstructor().newInstance();//4.7 实例化之后,将实例化的bean放到map中//4.7.1 如果当前类有接口,就让接口的class作为keyif(clazz.getInterfaces().length > 0){beanFactory.put(clazz.getInterfaces()[0],instance);}else{beanFactory.put(clazz,instance);}}}}}}}
  1. 属性注入
    属性注入,主要判断beanFactory中的class对象是否包含Di注解的属性,如果包含则将属性通过字段类型进行注入,
    示例代码:
  private void loadDi(){//1. 实例化对象都在beanFactoryMap集合中,if(beanFactory.isEmpty()){return;}//1. 遍历map集合for (Map.Entry<Class,Object> entry: beanFactory.entrySet()){//2. 获取map集合中的每个对象(value),每个对象属性获取到Object obj = entry.getValue();//获取对象的class部分Class<?> clazz = obj.getClass();//3 遍历得到每个对象属性的数组,得到每个属性Field[] fields = clazz.getDeclaredFields();for(Field field : fields){Di annotation = field.getAnnotation(Di.class);if(annotation != null){//4 判断属性上面是否有Di注解field.setAccessible(true);//5 如果有Di注解 将对象进行注入try {field.set(obj,beanFactory.get(field.getType()));} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}   }

总结

总体来说,理解的比较简单,具体的IOC实现原理还需要看源码多加理解。


http://www.ppmy.cn/news/96519.html

相关文章

驱动开发——嵌入式(驱动)软开基础(九)

1 系统调用的作用? (1)为应用程序提供访问硬件资源的统一接口,以至于应用程序不必关心具体的硬件操作细节。 (2)对系统内核进行保护,保证系统的稳定和安全,因为系统调用规定了用户进程进入内核的具体方式以及所能访问的数据范围。 2 BootLoader、Linux内核、根文件系…

Java并发常见面试题

参考:javauide、程序员大斌、面试宝典 1.并发与并行的区别 并发:两个及两个以上的作业在同一 时间段 内执行。并行:两个及两个以上的作业在同一 时刻 执行。2.同步和异步的区别 同步:发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。异步:调用在发…

WPF界面设计

目录 1.设计一个优美的注册界面1.实现效果2.代码展示 2.简易登录按钮设计1.实现效果2.代码展示 3.设计一个优美的注册登录界面&#xff08;连接数据库&#xff09;1.实现效果2.代码展示 4.设计一个简单的在线教育系统界面1.实现效果2.代码展示 5. 设计一个Dashboard1.实现效果2…

医院管理系统详细设计说明书

详细设计说明书 1.引言 1.1编写目的 在完成了针对北京工商大学校医院的前期调查&#xff0c;同时与多位学生进行了全面深入的探讨和分析的基础上&#xff0c;提出了这份概要设计说明书。 此概要设计说明书对北京工商大学校医院管理系统网站做了全面细致的概要设计&#xff0c;…

低代码平台

aims https://aisuda.bce.baidu.com/amis/zh-CN/docs/start/getting-started

AntV X6结合Vue组件渲染节点,并与节点组件进行双向的数据交互

项目使用 Vue2.0 Element-ui 框架&#xff0c;在开发流程功能时自定义节点&#xff0c;为实现稍复杂的样式以及操作交互&#xff0c;使用 markup attrs的方式的话&#xff0c;比较麻烦&#xff0c;且难以结合使用 Element-ui的交互组件&#xff0c;因此换成结合 Vue组件渲染方…

SAP-MM-内向外向交货单

1、内向&外向交货单概念 外向交货&#xff08;outbound delivery&#xff09;是用在客户与企业之间的交货单&#xff0c;而内向交货&#xff08;inbound delivery&#xff09;则是用在供应商与企业之间的交货单&#xff1b;换言之&#xff0c;外向交货多用于SD 模块&#…

Unity之SpriteShapeController

Detail&#xff1a;精灵形状的质量 高中低三种质量 Is Open Ended&#xff1a;是否是开放的&#xff0c;不封闭的 Adaptive UV&#xff1a;自适应UV&#xff0c;如果开启&#xff0c;会自动帮助我们判断是平铺还是拉伸 开启后只有宽度够才会平铺&#xff0c;如果宽度不够会拉…