首页 > 其他分享 >Spring IoC 常用注解手写实现

Spring IoC 常用注解手写实现

时间:2022-08-14 20:11:08浏览次数:69  
标签:Spring myspring bean com import 手写 IoC class wjq

执行流程

1.    初始化Spring容器时传入配置类,通过配置类的@ComponentScan注解告知Spring需要扫描的包路径,不在扫描包路径下的@Component等注解修饰的Bean不会被IoC容器创建;
2.    从工程根目录/target/classes中获取全类名,使用类加载器加载全类名得到class对象;
3.    通过无参构造函数来实例化单例bean并放入单例池中,beanName默认是类名首字母小写;
4.    调用getBean方法时根据beanName从单例池中获取Bean。

代码(注解类忽略)

Address类

package com.wjq.bean;

import com.wjq.myspring.annotation.Component;

@Component("address")
public class Address {
    public String getAddressStr() {
        return "北京";
    }
}

User类

package com.wjq.bean;

import com.wjq.myspring.annotation.Autowired;
import com.wjq.myspring.annotation.Component;

@Component
public class User {
    @Autowired
    private Address address;

    public String getAddressStr() {
        return address.getAddressStr();
    }
}

ApplicationConfig类

package com.wjq.myspring;

import com.wjq.myspring.annotation.ComponentScan;

/**
 * 配置类,告知扫描的包路径
 */
@ComponentScan("com.wjq.bean")
public class ApplicationConfig {

}

ApplicationContext类

package com.wjq.myspring;

import com.wjq.myspring.annotation.Autowired;
import com.wjq.myspring.annotation.Component;
import com.wjq.myspring.annotation.ComponentScan;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Spring容器类
 */
public class ApplicationContext {
    private ConcurrentHashMap<String, Object> singletonPool = new ConcurrentHashMap<>();

    public ApplicationContext(Class<?> configClass) {
        // 解析配置类上的@ComponentScan注解,获取扫描的包路径com.wjq.bean
        String packagePath = configClass.getDeclaredAnnotation(ComponentScan.class).value();

        // 扫描并创建Bean
        scanAndCreateBean(packagePath);
    }

    private void scanAndCreateBean(String packagePath) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        URL url = loader.getResource(packagePath.replace(".", "/"));

        // filePath是com.wjq.bean下java文件编译后生成的class文件所在的绝对路径
        String filePath;
        try {
            filePath = URLDecoder.decode(url.getFile(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

        File file = new File(filePath);
        List<String> classNames = new ArrayList<>();
        // 从target/classes中获取全类名
        for (File childFile : file.listFiles()) {
            String childFilePath = childFile.getPath();
            if (childFilePath.endsWith(".class")) {
                // \\表示\
                childFilePath = childFilePath.substring(childFilePath.lastIndexOf("\\") + 1, childFilePath.lastIndexOf("."));
                // childFilePath结果是全类名com.wjq.bean.Address和com.wjq.bean.User
                classNames.add(packagePath + "." + childFilePath);
            }
        }

        for (String className : classNames) {
            try {
                Class<?> clazz = loader.loadClass(className);
                // 判断类上是否有@Component注解
                if (clazz.isAnnotationPresent(Component.class)) {
                    // 类名首字母小写作为beanName
                    String curClassName = className.substring(className.lastIndexOf('.') + 1);
                    String beanName = curClassName.substring(0, 1).toLowerCase() + curClassName.substring(1);

                    // 通过无参构造函数来创建bean
                    Object obj = clazz.getDeclaredConstructor().newInstance();

                    // 依赖注入
                    for (Field declaredField : clazz.getDeclaredFields()) {
                        // 判断成员变量是否有@Autowired注解
                        if (declaredField.isAnnotationPresent(Autowired.class)) {
                            // 根据beanName获取对应bean
                            Object fieldBean = getBean(declaredField.getName());
                            declaredField.setAccessible(true);
                            declaredField.set(obj, fieldBean);
                        }
                    }

                    singletonPool.put(beanName, obj);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public Object getBean(String beanName) {
        if (singletonPool.containsKey(beanName)) {
            return singletonPool.get(beanName);
        }

        throw new RuntimeException("bean not exist");
    }
}

Main类

import com.wjq.bean.User;
import com.wjq.myspring.ApplicationConfig;
import com.wjq.myspring.ApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ApplicationContext(ApplicationConfig.class);
        User user = (User) applicationContext.getBean("user");
        System.out.println(user.getAddressStr());
    }
}

执行结果

标签:Spring,myspring,bean,com,import,手写,IoC,class,wjq
From: https://www.cnblogs.com/WJQ2017/p/16586193.html

相关文章

  • SpringBoot-----SpringBoot @Conditional注解自动配置报告
    一、@Conditional简介@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册Bean。SpringBoot是根据配置文件的内容决定是否创建Bean,以......
  • 【SpringBoot】学习笔记-MVC
     自动配置了ViewResolver,就是我们之前学习的SpringMVC的视图解析器;即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。我们去看看这里的源码......
  • Springboot项目-学生管理系统
    1.静态资源1.1网页静态资源获取网页模板(静态资源)从bootstarap出下载。下载网址:https://mb.bootcss.com/2.项目静态资源导入狂神项目静态资源包:创建springboot......
  • 【Spring5学习笔记】Bean管理:
    Bean管理:(1)Bean管理指的是两个操作(2)Spring创建对象(3)Spring注入属性Bean管理操作有两种方式:1、基于xml配置文件方式(1)在Spring配置文件中,使用bean标签,标签里添加对应的属......
  • 深入理解Spring注解机制(二):元注解解析与属性映射
    前言众所周知,spring从2.5版本以后开始支持使用注解代替繁琐的xml配置,到了springboot更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往......
  • 深入理解Spring别名机制
    前言在spring容器中,允许通过名称或别名来获取bean,这个能力来自于顶层接口AliasRegistry,分析类下属的关系图,可以看到,几乎所有主要容器都直接或间接的实现了AliasRegi......
  • 20220814 idea_springboot_启动 Cannot load driver class: com.mysql.cj.jdbc.
    1问题Cannotloaddriverclass:com.mysql.cj.jdbc.Driver 2解决方案2.1已解决2.1.1首先,去查看项目中MySQL的版本如果找不到,说......
  • 20220814 idea_SpringBoot_启动 jpa 启动 Access to DialectResolutionInfo canno
    1问题AccesstoDialectResolutionInfocannotbenullwhen'hibernate.dialect'notset 2解决方案2.1未解决直接用这个问题搜索,使用了很......
  • MinimalAPI---MinimalAPI+EFCore+IOC
    1.项目结构数据库:SQLServer项目类型:MinimalAPI   2.MinimalApi.Db类库(1)引入相关的NuGet包Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.Sq......
  • IDEA2022新建springMVC项目
    SpringMVC学习笔记新建项目新建一个空项目项目下添加一个maven的module,记得用ArcheType那个mavenweb模板记得Archetype的位置用maven-archetype-webapp模板,然后......