剑指Spring框架-IOC容器设计

IOC由DL(依赖查找-被抛弃啦,有侵入) + DI(依赖注入,spring只有这个)组成。

  • setter
  • interface
  • constructor
  • annotation
    依赖倒置原则 -》 控制反转 --》 控制反转容器
  1. 避免在各处new来创建类,做到统一维护
  2. 创建实例的时候不需要了解其中的细节
  3. 反射+工厂模式的合体,满足开闭原则

IOC容器设计

// 作用在类上的
@Target(ElementType.TYPE)
// 运行时获得
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
提取标记对象
  • 指定范围,获取范围内的所有类
  • 遍历所有类,获取被注解标记的类并加载进容器里
public class ClassUtil {

    public static final String FILE = "file";

    /**
     * 获取包下类集合
     *
     *
     *
     * @param packageName 包名
     * @return 类集合
     */
    public static Set<Class<?>> extractPackageClass(String packageName){
        // 1.获取类加载器
        ClassLoader classLoader = getClassLoader();
        // 2.通过类加载器获取到加载的资源信息 -- 不能让用户传绝对路径 通过url获取
        URL url = classLoader.getResource(packageName.replace(".","/"));
        if ( url == null ){
            log.warn("获取不到任何路径鸭");
            return null;
        }
        // 3.依据不同的资源类型,采用不同的方式获取资源的集合
        Set<Class<?>> classSet = null;
        // 过滤出文件类型的资源
        if (url.getProtocol().equalsIgnoreCase(FILE)){
            classSet = new HashSet <>();
            File packageDirectory = new File(url.getPath());
            // 提取所有的class文件
            extractClassFile(classSet, packageDirectory, packageName);
        }
        // todo 增加对其他资源的处理 (如jar)
        return classSet;
    }

    /**
     * 递归-获取目标package里面里面所有的class文件
     * (包括子package里面的)
     * @param emptyClassSet 装载目标类的集合
     * @param fileSource 文件 or 目录
     * @param packageName 包名
     */
    private static void extractClassFile(Set<Class<?>> emptyClassSet,File fileSource,String packageName) {
        // 找到文件停住
        if (!fileSource.isDirectory()){
            return;
        }
        //如果是一个文件夹,则调用获取所有的文件/文件夹
        File[] files = fileSource.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.isDirectory()){
                    return true;
                } else {
                    // class文件 - 获取绝对值路径
                    String absoluteFilePath = fileSource.getAbsolutePath();
                    if (absoluteFilePath.endsWith(".class")){
                        // 加载class
                        addToClassSet(absoluteFilePath);
                    }
                }
                return false;
            }

            private void addToClassSet(String absoluteFilePath) {
                //1.从class文件的绝对值路径里提出包含package的类名
                absoluteFilePath = absoluteFilePath.replace(File.separator,".");
                String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
                className = className.substring(0,className.lastIndexOf("."));
                //2.通过反射机制获取对应的class对象并加入到classSet里
                Class targetClass = loadClass(className);
                emptyClassSet.add(targetClass);
            }
        });
        // foreach遇到files为空会异常
        if (files != null){
            for ( File f : files ){
                // 递归调用
                extractClassFile(emptyClassSet,f,packageName);
            }
        }

    }

    /**
     * 获取Class对象
     * @param className
     * @return
     */
    public static Class<?> loadClass(String className){
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("load error");
            throw new RuntimeException(e);
        }
    }
    /**
     * 获取ClassLoader
     * @return
     */
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }
}
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    /**
     * 存放所有被配置标记的目标对象的Map
     * 将目标类本身作为键,目标类的实例作为值
     */
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap();
    /**
     * 加载bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);

    /**
     * 获取Bean容器实例
     *
     * @return BeanContainer
     */
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder {
        HOLDER;
        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }
    }

    /**
     * 容器是否已经加载过bean
     */
    private boolean loaded = false;

    /**
     * 是否已加载过Bean
     *
     * @return 是否已加载
     */
    public boolean isLoaded() {
        return loaded;
    }

    /**
     * Bean实例数量
     *
     * @return 数量
     */
    public int size() {
        return beanMap.size();
    }

    /**
     * 扫描加载所有Bean
     *
     * @param packageName 包名
     */
    public synchronized void loadBeans(String packageName) {
        //判断bean容器是否被加载过
        if (isLoaded()) {
            log.warn("BeanContainer has been loaded.");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("extract nothing from packageName" + packageName);
            return;
        }
        for (Class<?> clazz : classSet) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                //如果类上面标记了定义的注解
                if (clazz.isAnnotationPresent(annotation)) {
                    //将目标类本身作为键,目标类的实例作为值,放入到beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }

    /**
     * 添加一个class对象及其Bean实例
     *
     * @param clazz Class对象
     * @param bean  Bean实例
     * @return 原有的Bean实例, 没有则返回null
     */
    public Object addBean(Class<?> clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }

    /**
     * 移除一个IOC容器管理的对象
     *
     * @param clazz Class对象
     * @return 删除的Bean实例, 没有则返回null
     */
    public Object removeBean(Class<?> clazz) {
        return beanMap.remove(clazz);
    }

    /**
     * 根据Class对象获取Bean实例
     *
     * @param clazz Class对象
     * @return Bean实例
     */
    public Object getBean(Class<?> clazz) {
        return beanMap.get(clazz);
    }
    /**
     * 获取容器管理的所有Class对象集合
     *
     * @return Class集合
     */
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }
    /**
     * 获取所有Bean集合
     *
     * @return Bean集合
     */
    public Set<Object> getBeans(){
        return new HashSet<>( beanMap.values());
    }
    /**
     * 根据注解筛选出Bean的Class集合
     *
     * @param annotation 注解
     * @return Class集合
     */
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.通过注解筛选被注解标记的class对象,并添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //类是否有相关的注解标记
            if(clazz.isAnnotationPresent(annotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }
    /**
     * 通过接口或者父类获取实现类或者子类的Class集合,不包括其本身
     *
     * @param interfaceOrClass 接口Class或者父类Class
     * @return Class集合
     */
    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.判断keySet里的元素是否是传入的接口或者类的子类,如果是,就将其添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //判断keySet里的元素是否是传入的接口或者类的子类  isAssignableFrom 本身or超类
            if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }

}
Spring框架有多种作用域
  • singleton (单例:整个ioc容器只有一个)
  • prototype(每次请求实例都是新的)
  • request (每次http请求产生不同的bean)
  • session
  • globalsession
IOC容器实现
  1. 创建注解 (Controller、Service、Repository、Component)
  2. 提取标记对象(URL提取、获取类加载器、获取Class对象)
  3. 依赖注入(Autowired、成员变量实例的注入(解决多个实现类))
  4. 实现容器(单例模式(饿汉懒汉)、创建容器载体、实现容器加载)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值