自定义实现IOC容器的自动装配和自动注入

该文章介绍了一个自定义注解的实现,@MyBean用于类上的自动装配,@MyAutowired用于属性的自动注入。通过一个名为AnnotationApplicationContext的类,实现了基于包路径扫描和注解处理的bean容器,加载并管理带有@MyBean注解的类实例,同时处理@MyAutowired注解实现依赖注入。测试显示,该机制能正常工作,提供类似Spring的IoC功能。
摘要由CSDN通过智能技术生成
自定义注解@MyBean用于实现自动装配
@Target({ ElementType.TYPE })//类上面有效
@Retention(RetentionPolicy.RUNTIME)//运行时有效
public @interface MyBean {
}
自定义注解@MyAutowired用于实现自动注入
@Target({ ElementType.FIELD })//属性上面有效
@Retention(RetentionPolicy.RUNTIME)//运行时有效
public @interface MyAutowired {
}

定义bean容器接口
public interface ApplicationContext {

    /**
     * 接口中定义此方法,让所有实现类去实现
     * @param clazz 根据class
     * @return 容器中的对象
     */
    Object getBean(Class<?> clazz);
}
自定义AnnotationApplicationContext作为ApplicationContext 的实现类
package cn.ttc.bean;

import cn.ttc.annotation.MyAutowired;
import cn.ttc.annotation.MyBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationApplicationContext implements ApplicationContext {

    private final static Logger logger = LoggerFactory.getLogger(AnnotationApplicationContext.class);

    private static String rootPath;

    private final Map<Class, Object> beanFactory = new ConcurrentHashMap<>();

    @Override
    public Object getBean(Class<?> clazz) {
        return clazz != null ? beanFactory.get(clazz) : null;
    }

    /**
     * 根据有参构造,传递包路径,设置包扫码规则
     * 当前包及子包下只有存在@MyBean注解,通过反射实例化
     *
     * @param basePackage
     */
    public AnnotationApplicationContext(String basePackage) {
        if (!StringUtils.hasText(basePackage)) {
            return;
        }
        //basePackage --> cn.ttc  找到包路径下的绝对路径获取到file文件夹
        try {
            //1. 把.替换成\
            String packageDirName = basePackage.replaceAll("\\.", "\\\\");//处理转义
            //2. 获取包编译后的绝对路径(类加载器可以知道其绝对路径)
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (urls.hasMoreElements()) {
                // url ---> /D:/workspace/spring6/customization-ioc/target/test-classes/cn%5cttc
                URL url = urls.nextElement();
                //解决/被url编码的问题,需要转码。 得到完整的绝对路径
                String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
                //获取包前面路径的部分
                rootPath = filePath.substring(0, filePath.length() - packageDirName.length());
                //根据绝对路径获取文件夹后加载文件夹下所有存在@MyBean注解的类实例化到容器中
                loadBean(new File(filePath));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //加载自动注入功能
        loadMyAutowired();
    }

    /**
     * 使@MyAutowired拥有自动注入的功能
     * 实例化对象在beanFactory的map集合里面
     */
    private void loadMyAutowired() {
        //1. 遍历beanFactory的map集合
        //2. 得到每个对象的属性数组,得到每个属性
        //3. 判断属性上是否村存在@MyAutowired注解
        //4. 如果存在,把对象进行注入
        if (!CollectionUtils.isEmpty(beanFactory)) {
            Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
            for (Map.Entry<Class, Object> entry : entries) {
                Object obj = entry.getValue();
                //获取对象Class
                Class<?> clazz = obj.getClass();
                //得到对象的全部属性数组
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    //判断此属性上是否存在@MyAutowired注解
                    MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                    if (annotation != null) {
                        //打破私有方法不能访问的限制
                        field.setAccessible(true);
                        try {
                            logger.info("正在给【"+obj.getClass().getName()+"】属性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");
                            Class<?> classType = field.getType();
                            field.set(obj,beanFactory.get(classType));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 根据绝对路径获取文件夹后加载文件夹下所有存在@MyBean注解的类实例化到容器中
     *
     * @param fileParent 文件夹
     */
    private void loadBean(File fileParent) {
        //判断不是null还是文件夹
        if (fileParent != null && fileParent.isDirectory()) {
            //得到所有的子文件或子文件夹
            File[] childrenFiles = fileParent.listFiles();
            if (childrenFiles != null && childrenFiles.length > 0) {
                for (File child : childrenFiles) {
                    //如果还是文件夹
                    if (child.isDirectory()) {
                        //递归调用
                        loadBean(child);
                    } else {
                        //通过文件路径转变成全类名,第一步把绝对路径部分去掉
                        String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                        //文件夹中可能存在其他文件,只处理.class后缀的文件
                        if (pathWithClass.endsWith(".class")) {
                            //去掉.class后缀,并且把 / 替换成 .
                            String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                            try {
                                //根据全类限定名获取class
                                Class<?> clazz = Class.forName(fullName);
                                //把非接口的类实例化放在map中
                                if (!clazz.isInterface()) {
                                    //判断里面是否存在注解@MyBean
                                    MyBean annotation = clazz.getAnnotation(MyBean.class);
                                    //根据反射创建实例化对象
                                    if (annotation != null) {
                                        Object instance = clazz.getDeclaredConstructor().newInstance();
                                        //判断此对象是否存在接口
                                        if (clazz.getInterfaces().length > 0) {
                                            logger.info("正在加载【" + clazz.getInterfaces()[0] + "】,实例对象是:" + instance.getClass().getName());
                                            //如果有接口把接口的class当成key,实例对象当成value
                                            beanFactory.put(clazz.getInterfaces()[0], instance);
                                        } else {
                                            logger.info("正在加载【" + clazz + "】,实例对象是:" + instance.getClass().getName());
                                            beanFactory.put(clazz, instance);
                                        }
                                    }
                                }

                            } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }
}

}

测试

在项目中使用@@MyBean和@MyAutowired

    @org.junit.jupiter.api.Test
    public void test001(){
        AnnotationApplicationContext context = new AnnotationApplicationContext("cn.ttc");
        StudentService studentService = (StudentService) context.getBean(StudentService.class);
        String learning = studentService.learning();
        System.out.println("learning ----------= " + learning);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值