定义相关属性扫描所有 Bean
修改 BeanContainer.java
/** * @author yby6 * @program SpringPro * @date Created in 2023/10/08 008 17:24 * @description **/ @Slf4j @SuppressWarnings("unused") @NoArgsConstructor(access = AccessLevel.PRIVATE) public class BeanContainer { public static BeanContainer getInstance() { return ContainerHolder.HOLDER.instance; } /** * 容器枚举 * * @author yby6 * @date 2023/10/08 */ private enum ContainerHolder { /** * 持有人 */ HOLDER; /** * 实例 */ private final BeanContainer instance; /** * 容器 */ ContainerHolder() { instance = new BeanContainer(); } } /** * bean注释 */ private final List<Class<? extends Annotation>> beanAnnotation = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class); /** * bean映射 */ private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>(); /** * 容器实例的个数 * * @return int */ public int size() { return beanMap.size(); } /** * 加载状态 */ private boolean loaded = false; /** * 容器是否被加载过 * * @return boolean */ public boolean isLoaded() { return loaded; } /** * 加载bean * * @param packageName 包名 */ public synchronized void loadBeans(String packageName) { // 判断容器是否被加载过 if (isLoaded()) { log.warn("容器已经被加载!"); return; } // 1.获取该包下所有的字节码 Set<Class<?>> packageClass = ClassUtil.getPackageClass(packageName); if (null == packageClass || packageClass.isEmpty()) { log.warn("get null from packageName:{}", packageName); return; } // 2.遍历所有的字节码对象 packageClass.forEach(clazz -> beanAnnotation.forEach(annotation -> { // 2.1.判断当前字节码上是否有指定的注解 if (clazz.isAnnotationPresent(annotation)) { // 创建对象存放到集合当中 beanMap.put(clazz, BeanContainer.newInstance(clazz, false)); } })); loaded = true; } private static <T> T newInstance(Class<?> clazz, boolean accessible) { Constructor<?> declaredConstructor; try { declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(accessible); return (T) declaredConstructor.newInstance(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("创建实例失败!"); } } }
改造一下我们之前的测试类代码移动目录位置,移动到 test 包当中如下,顺便改一下我们自定义的 IOC 与 DI 功能代码存放包的位置:
/** * @author yby6 * @program SpringPro * @date Created in 2023/9/30 030 14:36 * @description **/ public class MyTest { private static BeanContainer beanContainer; @BeforeAll public static void init() { beanContainer = BeanContainer.getInstance(); } @Test @SuppressWarnings("all") void testClassUtil() { ClassUtil.getPackageClass("top.it6666").forEach(System.out::println); } @Test void testBeanContainer() { Assertions.assertFalse(beanContainer.isLoaded()); beanContainer.loadBeans("top.it6666"); Assertions.assertEquals(1, beanContainer.size()); Assertions.assertTrue(beanContainer.isLoaded()); } }
创建一个 MyController.java 然后在该类上添加 @Controller
注解导包是我们自己定义的那个注解:
然后运行测试类即可查看结果:
容器相关操作
增加删除操作,可以通过 class 获取对应的实例。获取所有的 key 和所有的 value。通过注解来获取被注解标注的 class。通过传入接口,来获取接口实现类的 class 对象。
/** * 添加bean * * @param clazz clazz * @param obj obj * @return {@link Object} */ public Object addBean(Class<?> clazz, Object obj) { return beanMap.put(clazz, obj); } /** * 删除bean * * @param clazz clazz * @return {@link Object} */ public Object removeBean(Class<?> clazz) { return beanMap.remove(clazz); } /** * 获取Bean * * @param clazz clazz * @return {@link Object} */ public Object getBean(Class<?> clazz) { return beanMap.get(clazz); } /** * 获取容器所有的classes * * @return {@link Set}<{@link Class}<{@link ?}>> */ public Set<Class<?>> getClasses() { return beanMap.keySet(); } /** * 获取容器所有的Bean实例 * * @return {@link Set}<{@link Object}> */ public Set<Object> getBeans() { return new HashSet<>(beanMap.values()); }
添加根据指定注解获取该注解标记的 Bean 的 Class 集合:
/** * 根据指定注解获取该注解标记的 Bean 的 Class 集合 * * @param annotation 注解 * @return {@link Set}<{@link Class}<{@link ?}>> */ public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) { // 1.获取容器当中所有的class Set<Class<?>> classes = this.getClasses(); if (null == classes || classes.isEmpty()) { log.info("没有任何信息!"); return null; } Set<Class<?>> classSet = new HashSet<>(); classes.forEach(clazz -> { if (clazz.isAnnotationPresent(annotation)) { // 保存到集合当中 classSet.add(clazz); } }); return classSet.size() > 0 ? classSet : null; }
添加根据传入的接口,到容器当中找对应的实现类或子类:
/** * 根据传入的接口,到容器当中找对应的实现类或子类 * * @param interfaceOrClass 接口或类 * @return {@link Set}<{@link Class}<{@link ?}>> */ public Set<Class<?>> getClassBySuper(Class<?> interfaceOrClass) { // 1.获取容器当中所有的class Set<Class<?>> classes = getClasses(); if (null == classes || classes.isEmpty()) { log.info("没有任何信息!"); return null; } Set<Class<?>> classSet = new HashSet<>(); classes.forEach(clazz -> { // 判断当前字节码是否为指定字节码的子类或接口并且指定的字节码不等于本身字节码 if (interfaceOrClass.isAssignableFrom(clazz) && !Objects.equals(clazz, interfaceOrClass)) { classSet.add(clazz); } }); return classSet.size() > 0 ? classSet : null; }
依赖注入
定义相关的注解标签,实现创建被注解标记的成员变量实例,并将其注入到成员变量里,依赖注入使用:
创建 Autowired.java
/** * @author yby6 * @program SpringPro * @date Created in 2023/10/25 025 9:01 * @description **/ /* 及作用在成员变量上 * */ @Target(ElementType.FIELD) /* 保留在运行时 * */ @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { String value() default ""; }
依赖注入实现思路:
依赖注入整体实现
DependencyInjector.java
/** * 依赖注入 * * @author yby6 * @date 2023/10/25 */ @Slf4j public class DependencyInjector { /** * Bean容器 */ private final BeanContainer beanContainer; /** * 依赖注入,构造器 */ public DependencyInjector() { beanContainer = BeanContainer.getInstance(); } /** * 执行Ioc操作 */ public void doIoc() { // 1.遍历Bean容器中所有的Class对象 beanContainer.getClasses().forEach(clazz -> { // 2.遍历Class对象的所有成员变量 Field[] declaredFields = clazz.getDeclaredFields(); if (declaredFields.length == 0) { // 当前没有成员变量,继续查找下一个 return; } // 有成员变量 Arrays.stream(declaredFields).forEach(field -> { // 3.找出被Autowired标记的成员变量 if (field.isAnnotationPresent(Autowired.class)) { // 获取注解实例 // 被 Autowired注解标注 // 4.获取这些成员变量的类型 Class<?> fieldType = field.getType(); // 5.获取成员变量的类型在容器里对应的实例 Object instanceObj = this.getFieldInstance(fieldType, field.getAnnotation(Autowired.class).value()); // 6.通过反射将对象的成员变量实例进行赋值操作 if (null == instanceObj) { throw new RuntimeException("成员属性注入失败:" + fieldType.getName()); } else { // 获取当前类实例 // 给当前类实例成员变量赋值 this.setField(field, beanContainer.getBean(clazz), instanceObj, true); } } }); }); } /** * 根据Class在BeanContainer里获取其实例或者实现类 * * @param fieldClass 成员变量类型 * @param autowiredValue autowired注解的内容 * @return {@link Object} */ private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) { Object fieldValue = beanContainer.getBean(fieldClass); // 字段是实现类,直接返回 if (null != fieldValue) { return fieldValue; } else { // 字段是接口 Class<?> implClass = this.getImplClass(fieldClass, autowiredValue); if (implClass != null) { return beanContainer.getBean(implClass); } else { return null; } } } /** * 得到impl类 * * @param fieldClass 接口 * @param autowiredValue autowired的价值 * @return {@link Class}<{@link ?}> */ private Class<?> getImplClass(Class<?> fieldClass, String autowiredValue) { //从容器当中获取该接口的实现类 Set<Class<?>> classSet = beanContainer.getClassBySuper(fieldClass); if (null != classSet && !classSet.isEmpty()) { if (Objects.equals(autowiredValue, "")) { if (classSet.size() == 1) { return classSet.iterator().next(); } else { // 如果有多个实现类 throw new RuntimeException("有多个实现类,请指定具体的实现类名称:" + fieldClass.getName()); } } else { // 注解当中有设置值 for (Class<?> clazz : classSet) { if (autowiredValue.equals(clazz.getSimpleName())) { return clazz; } } } } return null; } /** * 设置字段 * * @param field 字段 * @param targetObject 目标对象 * @param obj obj * @param accessible 是否可访问 */ private void setField(Field field, Object targetObject, Object obj, boolean accessible) { field.setAccessible(accessible); try { field.set(targetObject, obj); } catch (IllegalAccessException e) { log.error("setField error:" + e); throw new RuntimeException(e); } } }
测试依赖注入
创建 Service
IUserService.java
/** * @author yby6 * @program SpringPro * @date Created in 2023/10/25 025 10:03 * @description **/ public interface IUserService { /** * 显示 */ void show(); }
IUserServiceImpl.java
/** * @author yby6 * @program SpringPro * @date Created in 2023/10/25 025 10:04 * @description **/ @Service public class IUserServiceImpl implements IUserService { @Override public void show() { System.out.println("my is IUserServiceImpl"); } }
修改 MyController.java
/** * @author yby6 * @program SpringPro * @date Created in 2023/10/09 009 11:31 * @description **/ @Controller public class MyController { @Autowired private IUserService iUserService; public void testShow() { this.iUserService.show(); } }
修改 MyTest.java 当前 IUserService 只有一个实现类所以 @Autowired 是可以直接标记即可不用指定名称,在 MyTest 添加 testIoc 方法:
@Test void testIoc() { beanContainer.loadBeans("top.it6666"); MyController myController = (MyController) beanContainer.getBean(MyController.class); DependencyInjector dependencyInjector = new DependencyInjector(); dependencyInjector.doIoc(); myController.testShow(); }
运行结果如下:
如上是只有一个实现类,紧接着在来新建一个实现类,在 impl 包当中创建 ITaoBaoServiceImpl.java:
/** * @author yby6 * @program SpringPro * @date Created in 2023/10/25 025 10:42 * @description **/ @Service public class ITaoBaoServiceImpl implements IUserService { @Override public void show() { System.out.println("my is ITaoBaoServiceImpl"); } }
再次运行测试类代码结果如下图:
修改 MyController.java:
再次运行测试类代码运行结果如下图所示:
最后
本期结束咱们下次再见👋~
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗