网站开发招聘需要,代理公司韩剧剧情介绍,利用百度图片做网站外链,为wordpress安装iis rewrite 组件与配置方法本篇博客我们来手写一个IOC#xff0c;就是模拟出IOC里边的实现过程。这过程怎么做呢#xff1f;
咱们主要基于java中的反射#xff0c;再加注解#xff0c;来实现spring框架中IOC的这个效果。
下面我们来具体看看这个过程。首先因为这里边要用到反射#xff0c;咱们把反…本篇博客我们来手写一个IOC就是模拟出IOC里边的实现过程。这过程怎么做呢
咱们主要基于java中的反射再加注解来实现spring框架中IOC的这个效果。
下面我们来具体看看这个过程。首先因为这里边要用到反射咱们把反射中的相关内容我们先做一个复习。复习之后最终让我们来手写spring IOC的这个功能。
1、回顾Java反射
java中的反射机制是什么呢
它指的是对于任何一个类我们都能够知道这个类里面的属性方法。
对于任何一个对象都能调它的任意方法和属性。
而这种动态获取信息以及动态调用对象方法的功能就称为java的反射机制。 说的简单点你要做反射首先要得到类的卡的对象就是咱们通俗说的字节码文件。通过字节码文件能够操作类中所有内容包括你的属性包括你的方法等等。这个是对于反射一个简单的概述。 自定义类
package com.jie.reflect.model;/*** Car类* 用于反射测试** author 阿杰 2416338031qq.com* version 1.0* date 2023/10/26 6:46*/
public class Car {/*** 属性*/private String name;private int age;private String color;/*** 无参数构造*/public Car() {}/*** 有参数构造*/public Car(String name, int age, String color) {this.name name;this.age age;this.color color;}/*** 私有方法*/private void run() {System.out.println(私有方法-run.....);}/*** get和set方法*/public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}public String getColor() {return color;}public void setColor(String color) {this.color color;}Overridepublic String toString() {return Car{ name name \ , age age , color color \ };}
}这个是基本准备。下面咱们基于这个类来用一下反射中的相关内容。
1.1 获取Class对象的多种方式
第一个内容获取Class对象的多种方式。
import com.jie.reflect.model.Car;
import org.junit.jupiter.api.Test;/*** 测试反射类** author 阿杰 2416338031qq.com* version 1.0* date 2023/10/26 6:51*/
public class TestCar {//1、获取Class对象多种方式Testpublic void test01() throws Exception {// 1.1、通过类名.class获取Class clazz1 Car.class;// 1.2、通过对象.getClass()获取Class clazz2 new Car().getClass();// 1.3、通过Class.forName(全类名)获取Class clazz3 Class.forName(com.jie.reflect.model.Car);// 1.4、通过类加载器获取ClassLoader classLoader TestCar.class.getClassLoader();Class clazz4 classLoader.loadClass(com.jie.reflect.model.Car);//实例化Car car (Car)clazz4.getConstructor().newInstance();System.out.println(car);}
} 1.2 获取构造方法
刚才咱们完成了第一个操作获取class对象的多种方式演示最终进行实例化。下面我们演示第二个内容通过反射来获取构造方法。 // 2 、 获取构造方法Testpublic void test02() throws Exception {// 2.1、获取所有的构造方法Class clazz Class.forName(com.jie.reflect.model.Car);// getConstructors()获取所有的公有构造方法Constructor[] constructors clazz.getConstructors();for (Constructor constructor : constructors) {System.out.println(方法名称constructor.getName() 参数个数constructor.getParameterCount());}System.out.println();// getDeclaredConstructors()获取所有的构造方法 包括私有的Constructor[] constructors2 clazz.getDeclaredConstructors();for (Constructor constructor : constructors2) {System.out.println(方法名称constructor.getName() 参数个数constructor.getParameterCount());}// 2.2、获取指定有参数的构造方法构造对象// getConstructor 获取公有的构造方法Constructor constructor clazz.getConstructor(String.class, int.class, String.class);Car car (Car)constructor.newInstance(奔驰, 20, 黑色);System.out.println(car);// 2.3、获取私有的构造方法// getDeclaredConstructor 获取私有的构造方法Constructor c2 clazz.getDeclaredConstructor(String.class, int.class, String.class);c2.setAccessible(true);Car car2 (Car)c2.newInstance(捷达, 15, 白色);System.out.println(car2);}1.3 获取属性
下面我们演示第三个获取属性 // 3、获取属性Testpublic void test03() throws Exception {// 3.1、获取所有的属性Class clazz Class.forName(com.jie.reflect.model.Car);Car car (Car) clazz.getDeclaredConstructor().newInstance();// getFields()获取所有的公有属性System.out.println(获取所有的公有属性);System.out.println(clazz.getFields().length);// 因为Car类没有公有属性所以获取不到
// System.out.println(clazz.getFields()[0].getName());System.out.println();// getDeclaredFields()获取所有的属性 包括私有的System.out.println(获取所有的属性);System.out.println(clazz.getDeclaredFields().length);Field[] declaredFields clazz.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.getName().equals(name)) {// 私有属性需要设置访问权限declaredField.setAccessible(true);declaredField.set(car, 奔驰);}System.out.println(declaredField.getName());System.out.println(car);}// 3.2、获取指定的属性// getField 获取公有的属性// 因为Car类没有公有属性所以获取不到
// System.out.println(获取指定的公有属性);
// System.out.println(clazz.getField(name));System.out.println();// getDeclaredField 获取私有的属性System.out.println(获取指定的属性);System.out.println(clazz.getDeclaredField(color));}1.4 获取方法
然后再看第四个就是如何来操作方法。 // 4、获取方法Testpublic void test04() throws Exception {// 4.1、获取所有的方法Class clazz Class.forName(com.jie.reflect.model.Car);Car car (Car) clazz.getDeclaredConstructor().newInstance();car.setName(奔驰);car.setAge(20);car.setColor(黑色);// getMethods()获取所有的公有方法System.out.println(获取所有的公有方法);System.out.println(clazz.getMethods().length);// 因为Car类没有公有方法所以获取不到Method[] methods clazz.getMethods();for (Method method : methods) {// 获取方法名称
// System.out.println(method.getName());// 执行方法 toStringif(method.getName().equals(toString)) {System.out.println(method.invoke(car));}}// 4.2 getDeclaredMethods()获取所有的方法 包括私有的System.out.println(获取所有的方法);System.out.println(clazz.getDeclaredMethods().length);Method[] methodsAll clazz.getDeclaredMethods();for (Method m:methodsAll) {//执行方法 runif(m.getName().equals(run)) {m.setAccessible(true);m.invoke(car);}}}2、实现Spring的IoC
我们知道IoC控制反转和DI依赖注入是Spring里面核心的东西那么我们如何自己手写出这样的代码呢下面我们就一步一步写出Spring框架最核心的部分。
2.1 搭建子模块
搭建模块guigu-spring搭建方式如其他spring子模块 2.2 准备测试需要的bean
创建UserDao接口
package com.jie.spring.dao;/*** UserDao** author 阿杰 2416338031qq.com* date 2023/10/27 6:39* version 1.0
*/
public interface UserDao {public void print();
}创建UserDaoImpl实现
package com.jie.spring.dao.impl;import com.jie.spring.dao.UserDao;/*** UserDaoImpl** author 阿杰 2416338031qq.com* version 1.0* date 2023/10/27 6:44*/
public class UserDaoImpl implements UserDao {Overridepublic void print() {System.out.println(Dao层执行结束);}
}
创建UserService接口
package com.jie.spring.service;/*** UserService** author 阿杰 2416338031qq.com* date 2023/10/27 6:45* version 1.0
*/
public interface UserService {public void out();
}创建UserServiceImpl实现类
package com.jie.spring.service.impl;import com.jie.spring.service.UserService;public class UserServiceImpl implements UserService {Overridepublic void out() {System.out.println(Service层执行结束);}
}
2.3 定义注解
我们通过注解的形式加载bean与实现依赖注入。
package com.jie.spring.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** Bean 用于创建对象* 作用于类上* 运行时生效** author 阿杰 2416338031qq.com* version 1.0* date 2023/10/27 6:51*/
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
public interface Bean {
}package com.jie.spring.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** Di 用于依赖注入* 作用于属性上* 运行时生效** author 阿杰 2416338031qq.com* version 1.0* date 2023/10/27 6:52*/
Target({ElementType.FIELD})
Retention(RetentionPolicy.RUNTIME)
public interface Di {
}说明上面两个注解可以随意取名 然后我们就可以在我们的UserDaoImpl 使用我们定义的注解。 2.4定义bean容器接口
package com.jie.spring.bean;/*** ApplicationContext 用于获取bean** author 阿杰 2416338031qq.com* date 2023/10/27 7:03* version 1.0
*/
public interface ApplicationContext {Object getBean(Class clazz);
}2.5 编写注解bean容器接口实现
AnnotationApplicationContext基于注解扫描bean。
package com.jie.spring.bean;import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;/*** ApplicationContext 用于获取bean** author 阿杰 2416338031qq.com* version 1.0* date 2023/10/27 7:06*/
public class ApplicationContextImpl implements ApplicationContext {/*** 创建Map集合放Bean对象*/private MapClass, Object beanFactory new HashMap();Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 创建有参构造器传递包路径设置包扫描规则* 扫描包路径下的所有类判断类上是否有Bean注解如果有创建对象放入Map集合** param basePackage 包路径*/public ApplicationContextImpl(String basePackage) {}
}
2.6 编写扫描bean逻辑
我们通过构造方法传入包的base路径扫描被Bean注解的java对象完整代码如下
package com.jie.spring.bean;import com.jie.spring.anno.Bean;import java.io.File;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;/*** ApplicationContext 用于获取bean** author 阿杰 2416338031qq.com* version 1.0* date 2023/10/27 7:06*/
public class ApplicationContextImpl implements ApplicationContext {/*** 创建Map集合放Bean对象*/private static final MapClass, Object BEAN_FACTORY new HashMap();private static String rootPath;Overridepublic Object getBean(Class clazz) {return BEAN_FACTORY.get(clazz);}/*** 创建有参构造器传递包路径设置包扫描规则* 扫描包路径下的所有类判断类上是否有Bean注解如果有创建对象放入Map集合** param basePackage 包路径*/public ApplicationContextImpl(String basePackage) {try {// 包路径都是 com.jie.spring// 1 我们需要把 . 替换成 \String packagePath basePackage.replaceAll(\\., \\\\);// 2 获取包的绝对路径EnumerationURL urls Thread.currentThread().getContextClassLoader().getResources(packagePath);// 3 遍历包下的所有类while (urls.hasMoreElements()) {URL url urls.nextElement();// 4 获取类的绝对路径String filePath URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8);// 5 获取包前面的路径部分字符串截取rootPath filePath.substring(0, filePath.length() - packagePath.length());// 6 调用方法获取包下的所有类loadBean(new File(filePath));}} catch (Exception e) {e.printStackTrace();}}/*** 包扫描过程 递归** param file 文件* return: void* author 阿杰 2416338031qq.com* date: 2023/10/29 17:48*/private static void loadBean(File file) throws Exception {// 1 判断是否是文件夹if (file.isDirectory()) {// 2 获取文件夹下的所有文件File[] childrenFiles file.listFiles();// 3 判断文件夹里面为空直接返回if (childrenFiles null || childrenFiles.length 0) {return;}// 4 如果文件夹里面有文件遍历文件夹所有内容for (File childrenFile : childrenFiles) {// 4.1 遍历得到每个File对象继续判断如果还是文件夹递归调用if (childrenFile.isDirectory()) {// 递归调用loadBean(childrenFile);} else {// 4.2 遍历得到每个File对象不是文件夹是文件// 4.3 得到包路径类名称部分字符串截取过程String patgWithClass childrenFile.getAbsolutePath().substring(rootPath.length() - 1);// 4.4 判断是否是class文件if (patgWithClass.contains(.class)) {// 4.5 如果是class文件把路径\替换成.把.class去掉得到类的全限定名String allName patgWithClass.replaceAll(\\\\, .).replaceAll(.class, );// 4.6 判断类上是否有Bean注解如果有就进行实例化// 4.6.1 获取类的字节码对象Class? clazz Class.forName(allName);// 4.6.2 判断不是接口if (!clazz.isInterface()) {// 4.6.3 判断类上是否有Bean注解Bean annotation clazz.getAnnotation(Bean.class);if (annotation ! null) {// 4.6.4 如果有创建对象Object instance clazz.getConstructor().newInstance();// 4.7 把类的全限定名和对象放入Map集合// 4.7.1 判断当前类如果实现了接口把接口的class对象作为keyif (clazz.getInterfaces().length 0) {BEAN_FACTORY.put(clazz.getInterfaces()[0], instance);} else {// 4.7.2 如果没有实现接口把当前类的class对象作为keyBEAN_FACTORY.put(clazz, instance);}}}}}}}}public static void main(String[] args) {ApplicationContextImpl applicationContext new ApplicationContextImpl(com.jie.spring);}
}
2.7 java类标识Bean注解 2.8 测试Bean加载 2.9 依赖注入实现
我们实现了Bean 的加载现在来实现 依赖注入 /*** 属性注入** return: void* author 阿杰 2416338031qq.com* date: 2023/10/29 18:50*/
private void loadDi() {// 1 遍历BEAN_FACTORY Map集合SetMap.EntryClass, Object entries BEAN_FACTORY.entrySet();for (Map.EntryClass, Object entry : entries) {// 2 获取map 集合每个对象value,获取每个对象属性Object value entry.getValue();// 获取class对象Class? 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);// 5 如果有 Di 注解把对象进行设置注入try {field.set(value, BEAN_FACTORY.get(field.getType()));} catch (IllegalAccessException e) {e.printStackTrace();}}}}
}2.10 测试依赖注入