简单实现SpringIoC的bean的创建和自动注入

        我们知道,在单例模式下,SpringBean的生命周期大致有以下几个过程:

        1、默认情况下,IoC容器启动后,会立即实例化响应的Bean。只有设置了懒加载或者bean标签的scope属性设置为prototype时,才会在调用getBean()方法时创建实例化对象。

        2、容器启动时,会扫描xml配置文档和指定包及其子下的所有Spring实例化注解,生成对应的BeanDefinition信息存储在一个HashMap集合中。

        3、通过反射机制,获得bean的Class类对象,从而调用构造方法或工厂方法实例化bean对象。

        4、使用依赖注入完成bean中所有属性值的配置注入。

        5、Spring调用BeanPostProcessor接口的前置处理方法,对bean进行功能增强。

        6、调用默认或指定的初始化方法,对bean对象进行初始化。

        7、Spring调用BeanPostProcessor接口的后置处理方法。

        8、将该bean对象放入IoC容器的缓存池中,供程序运行时调用。

        9、当IoC容器关闭时,Spring调用指定或默认的destroy()方法销毁该bean对象。

        为了更深入的理解Spring框架IoC和DI的实现过程,在翻阅了一些资料和课件后,尝试手动定义两个注解@MyComponent@MyAutowired,来简单实现Spring中控制反转和依赖注入的功能。

        步骤如下:

        第一步:创建自定义注解(@MyComponent、@MyAutowired)

@Target(ElementType.TYPE)  // 该注解只能标注在类上
@Retention(RetentionPolicy.RUNTIME)    // 该注解在运行时生效
public @interface MyComponent {
}
@Target({ElementType.FIELD})  // 该注解只能标注在类的属性上
@Retention(RetentionPolicy.RUNTIME)    // 该注解在运行时生效
public @interface MyAutowired {
}

        第二步:创建自定义的bean容器接口 MyApplicationContext ,定义一个getBean()方法,传入需要获取的类Class对象,返回bean的实例对象。

public interface MyApplicationContext {

    Object getBean(Class clazz);
}

        第三步:创建一个类AnnotationApplicationContext实现MyApplicationContext接口。我们知道,Spring容器创建好bean对象后,会将其放入到IoC容器的缓存池中,这里我们通过定义一个静态Map<Class, Object>集合的方式,保存创建出来的bean实例对象。

public class AnnotationApplicationContext implements MyApplicationContext {

    /**
     * 创建一个map集合,用于存放bean对象
     */
    private static Map<Class, Object> beanFactory = new ConcurrentHashMap<>();

    /**
     * 根据类型获取bean对象
     *
     * @param clazz
     * @return
     */
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }
}

        第四步: 在类AnnotationApplicationContext中创建一个带参构造函数,传入要扫描的包路径,程序扫描参数中指定包及其子包里面所有类中的@MyComponent注解,并将注解所标注的类通过反射进行实例化。然后对@MyAutowired所标注的属性进行自动装配。

    private static String rootPath;    

    /**
     * 创建带参构造函数,传递要扫描的包路径
     * 扫描参数中指定包及其子包里面所有类中的@MyComponent注解,并将注解所标注的类通过反射进行实例化
     * 然后对@MyAutowired所标注的属性进行自动装配
     *
     * @param annoPackagePath
     */
    public AnnotationApplicationContext(String annoPackagePath) {
        //com.atguigu
        try {
            // 1、 把路径中的.替换成\
            String packagePath = annoPackagePath.replaceAll("\\.", "\\\\");
            // 2、 获取包的绝对路径
            Enumeration<URL> packagePathUrl = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (packagePathUrl.hasMoreElements()) {
                URL url = packagePathUrl.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                // 获取包前面的路径部分,字符串截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                // 包扫描
                loadMyAnno(new File(filePath));
                // 依赖注入
                autowired();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描参数中指定包及其子包里面所有类中的@MyComponent注解,并将注解所标注的类通过反射进行实例化
     */
    private static void loadMyAnno(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1、 判断file对象是否是文件夹
        if (file.isDirectory()) {
            // 2、 获取文件夹里面的所有内容
            File[] childFiles = file.listFiles();
            // 3、 若文件夹里面为空,直接返回,若不为空,则遍历文件夹
            if (childFiles == null || childFiles.length == 0) {
                return;
            }
            // 3.1、 遍历得到每个File对象
            for (File childFile : childFiles) {
                // 3.2、 继续判断,如果还是文件,递归
                if (childFile.isDirectory()) {
                    loadMyAnno(childFile);
                } else {
                    // 3.3、 当遍历得到的File对象不是文件夹,是一个文件,获取“包路径+类名"部分,
                    String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
                    // 3.4、 判断当前文件类型是否是.class文件
                    if (pathWithClass.contains(".class")) {
                        // 3.5、 如果是.class类型,把路径'\'替换成'.',并把.class去掉
                        String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                        // 3.6、 获取类的Class对象,判断该类是否是接口
                        Class<?> clazz = Class.forName(allName);
                        if (!clazz.isInterface()) {
                            // 3.7、 判断该类是否标注了@MyComponent,如果有注解,则实例化该类的对象
                            MyComponent annotation = clazz.getAnnotation(MyComponent.class);
                            if (annotation != null) {
                                Object instance = clazz.getConstructor().newInstance();
                                // 3.8、 判断该类是否实现了接口,如果实现了接口,就把对应接口的class作为key,如果没有实现接口,就将该类自己的class作为key,保存到beanFactory中
                                if (clazz.getInterfaces().length > 0) {
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                } else {
                                    beanFactory.put(clazz, instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 对@MyAutowired所标注的属性进行自动装配
     */
    private void autowired() throws IllegalAccessException {
        // bean实例化对象均保存在beanFactory集合中
        // 1、遍历beanFactory的map集合
        Set<Map.Entry<Class, Object>> entrySet = beanFactory.entrySet();
        for (Map.Entry<Class, Object> beanDefinition : entrySet) {
            // 2、得到map集合每个元素的value,并获取到value对象的属性
            Object obj = beanDefinition.getValue();
            Class<?> clazz = obj.getClass();
            Field[] fields = clazz.getDeclaredFields();
            // 3、遍历得到的每个对象的属性数组,得到每个属性
            for (Field field : fields) {
                // 4、判断属性上是否标注了@MyAutowired注解,如果有,把对象进行设置(属性注入)
                MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                if (annotation != null) {
                    // 设置私有属性允许访问
                    field.setAccessible(true);
                    field.set(obj, beanFactory.get(field.getType()));
                }
            }
        }
    }

        第五步:IoC和DI的基本功能完成后,创建service和dao的接口及实现类,并标注自定义注解。

public interface UserDao {

    void add();
}
@MyComponent
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("UserDao add...");
    }
}
public interface UserService {

    void add();
}
@MyComponent
public class UserServiceImpl implements UserService {

    @MyAutowired
    private UserDao userDao;

    @Override
    public void add() {
        System.out.println("UserService add...");
        userDao.add();
    }
}

        测试功能:

         如此,便手动完成了一个简单的SpringBean创建及管理的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值