React 前端导航

爆肝三个小时手写实现SpringIOC

1、 首先定义一个类上的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DadaBean {}

2、定义一个类中属性的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DadaBean {}

3、 编写一个实例化bean的接口

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

4、实现这个实例化接口的类【核心代码

  1. 初始化Bean对象的开发注意点
      • 1.1. 判断当前是否文件夹
      • 1.2. 获取文件夹里面的所有内容
      • 1.3. 判断文件夹里面为空直接返回
      • 1.4. 如果文件夹不为空,遍历文件夹所有内容 
        • 1.4.1. 遍历得到每个File对象,继续判断,如果还是文件夹,递归
        • 1.4.2. 遍历得到File对象不是文件夹,是文件
        • 1.4.3. 得到包的路径+类名称部分-字符串截取
        • 1.4.4. 判断当前文件夹是否为.class结尾
        • 1.4.5. 如果是.class类型,把路径\替换成. 并且把。class去掉【cn.itzd.service.UserServiceImpl】
        • 1.4.6. 判断类上面是否有注解@DadaBean,如果有就实例化过程
        • 1.4.7. 把对象实例化之后放到map集合中
  2. 属性的注入开发注意点
      • 遍历map集合中的实例化对象
      • 获取集合中每个对象的value,得到每个属性
      • 遍历得到每个对象属性数组,得到每个属性
      • 判断属性上是否有@DadaDi注解(如果是私有属性,需要开启设置值,也就是setAccessible(true))
      • 如果有@DadaDi注解,把对象进行设置
package cn.itzd.bean;
import cn.itzd.anno.DadaBean;
import cn.itzd.anno.DadaDi;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @Author: dada
 * @Date: 2023/4/5 22:33
 * @Version: 1.0
 * @Description: 基于注解扫描bean
 */
public class AnnotationApplicationContext implements ApplicationContext {
    //存储bean的容器
    private final Map<Class<?>, Object> beanFactory = new ConcurrentHashMap<>();
    //本地磁盘路径
    private static String rootPath;
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }
    /**
     * 根据包扫描加载bean
     * @param basePackage 传过来的包路径
     */
    public AnnotationApplicationContext(String basePackage) {
        try {
            String packageDirName = basePackage.replaceAll("\\.", "\\\\");
            Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
                rootPath = filePath.substring(0, filePath.length()-packageDirName.length());
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 递归遍历所有的.class类
     * @param file
     * 1. 判断当前是否文件夹
     * 2. 获取文件夹里面的所有内容
     * 3. 判断文件夹里面为空直接返回
     * 4. 如果文件夹不为空,遍历文件夹所有内容
     * 4.1. 遍历得到每个File对象,继续判断,如果还是文件夹,递归
     * 4.2. 遍历得到File对象不是文件夹,是文件
     * 4.3. 得到包的路径+类名称部分-字符串截取
     * 4.4. 判断当前文件夹是否为.class结尾
     * 4.5. 如果是.class类型,把路径\替换成. 并且把。class去掉【cn.itzd.service.UserServiceImpl】
     * 4.6. 判断类上面是否有注解@DadaBean,如果有就实例化过程
     * 4.7. 把对象实例化之后放到map集合中
     */
    private void loadBean(File file) {
        try {
            //判断当前是否文件夹
            if (!file.isDirectory()) {
                return;
            }
            //判断文件夹里面为空直接返回
            File[] childrenFiles = file.listFiles();
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            //遍历得到每个File对象,继续判断,如果还是文件夹,递归
            for (File childrenFile : childrenFiles) {
                if (childrenFile.isDirectory()) {
                    loadBean(childrenFile);
                } else {
                    //得到包的路径+类名称部分-字符串截取
                    String pathWithClass = childrenFile.getAbsolutePath().substring(rootPath.length() - 1);
                    //判断当前文件夹是否为.class结尾
                    if (!pathWithClass.endsWith(".class")) {
                        continue;
                    }
                    String allName = pathWithClass.replaceAll("\\\\", "\\.").replace(".class", "");
                    //判断类上面是否有注解@DadaBean,如果有就实例化过程,结构不需要实例化
                    Class<?> clazz = Class.forName(allName);
                    if (clazz.isInterface()) {
                        continue;
                    }
                    //判断类上面是否有注解@DadaBean,如果有就实例化过程
                    DadaBean annotation = clazz.getAnnotation(DadaBean.class);
                    if (annotation == null) {
                        continue;
                    }
                    Object instance = clazz.getConstructor().newInstance();
                    //判断当前类有接口,就让接口的class作为map的key
                    if (clazz.getInterfaces().length > 0) {
                        beanFactory.put(clazz.getInterfaces()[0],instance);
                    }else {
                        beanFactory.put(clazz,instance);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //支持属性的注入
        loadDi();
    }
    /**
     * 支持属性的注入
     * 1. 遍历map集合中的实例化对象
     * 2. 获取集合中每个对象的value,得到每个属性
     * 3. 遍历得到每个对象属性数组,得到每个属性
     * 4. 判断属性上是否有@DadaDi注解(如果是私有属性,需要开启设置值,也就是setAccessible(true))
     * 5. 如果有@DadaDi注解,把对象进行设置
     */
    private void loadDi() {
        //遍历
        beanFactory.forEach((k,v) -> {
            //获取value对象
            Class<?> clazz = v.getClass();
            //获取每个类中的属性值
            Field[] declaredFields = clazz.getDeclaredFields();
            //判断属性上是否有@DadaDi注解
            for (Field declaredField : declaredFields) {
                DadaDi dadaDi = declaredField.getAnnotation(DadaDi.class);
                if (dadaDi == null) {
                    continue;
                }
                //如果是私有属性,需要开启设置值
                declaredField.setAccessible(true);
                //如果有@DadaDi注解,把对象进行设置
                try {
                    System.out.println("正在给【"+clazz.getName()+"】属性【" + declaredField.getName() + "】注入值【"+ beanFactory.get(declaredField.getType()).getClass().getName() +"】");
                    declaredField.set(v,beanFactory.get(declaredField.getType()));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}
 

5、业务代码中开始调用

  1. 用户接口类
public interface UserService {
    void out();
}
  1. 用户接口实现
@DadaBean
public class UserServiceImpl implements UserService {
    @DadaDi
    private UserDao userDao;
    @Override
    public void out() {
        System.out.println("Service层执行结束");
        userDao.print();
    }
}
  1. dao持久层接口
public interface UserDao {
    void print();
}
  1. dao层接口的实现
@DadaBean
public class UserDaoImpl implements UserDao{
    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}
  1. 测试通过
public class UserTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationApplicationContext("cn.itzd");
        UserService userService = (UserService)context.getBean(UserService.class);
        System.out.println(userService);
        userService.out();
    }
}

声明:本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。邮箱:farmerlzj@163.com。 本站原创内容未经允许不得转载,或转载时需注明出处: 内容转载自: React前端网:https://qianduan.shop/blogs/detail/177
想做或者在做副业的朋友欢迎加微信交流:farmerlzj,公众号:生财空间站。

#java#spring#ioc

相关推荐

单测报错:Transaction synchronization is not active解决办法

使用TransactionSynchronizationManager写单测报错:Transaction synchronization is not active解决办法,技术沉淀

彻底下线fastjson的依赖,Jackson替换FastJson注意事项

近几年fastjson爆出的漏洞非常多,因此可能你所在的公司会要求使用jackson替代fastjson。本文主要就作者自己最近完成fastjson替换记录的一个比较分享。