容器工厂

容器工厂

介绍

简单工厂->超级工厂(简单工厂+反射+泛型)->容器工厂(超级工厂+提前获取并创建需要被创建的类)

原理

容器工厂创建时,创建实例或者原型放入到容器(Map)中
通过容器工厂方法获取实例

语法

properties读取配置文件(初级)

容器工厂加载时,创建实例放入到Map集合中
通过容器工厂方法获取实例

public class ContainerFactory {

    /**
     * 容器
     */
    private static Map<String, Object> container = new HashMap<>();

    /**
     * 在静态代码块中初始化整个容器工厂,解析properties文件中配置的对象,
     * 创建实例放入到Map集合中
     */
    static {
        // 返回输入流
        try (InputStream input = ContainerFactory.class.getClassLoader().getResourceAsStream("config.properties")){
            Properties prop = new Properties();
            // 加载并读取 properties 文件的内容
            prop.load(input);
            // 循环遍历prop对象,得到每一个properties的键值对
            prop.forEach((k, v) ->
                    // 将key和创建好的对象(将v通过newInstance方法创建实例)保存到容器中
                    container.put((String) k, newInstance((String) v))
            );
        } catch (Exception e) {
            throw new RuntimeException("解析失败:", e);
        }
    }

    /**
     * 创建实例
     * @param className 类的路径
     * @return
     */
    private static Object newInstance(String className) {
        try {
            return Class.forName(className).getConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("实例创建失败:", e);
        }
    }

    /**
     * 容器工厂方法
     */
    public static <T> T getBean(String name) {
        return (T) container.get(name);
    }
}

反射获取类的容器工厂(终极版)

file

首先导入 ClassGraph(类图) 第三方库
<!-- https://mvnrepository.com/artifact/io.github.classgraph/classgraph -->
<dependency>
    <groupId>io.github.classgraph</groupId>
    <artifactId>classgraph</artifactId>
    <version>4.8.158</version>
</dependency>
扫描指定的包的工具类
public class ScanUtils {
    /**
     * 扫描指定的包,并返回相关的Class对象
     * @param packages
     * @return
     */
    public static List<Class<?>> scan(String... packages) {
        try(ScanResult scan = new ClassGraph().enableAllInfo().acceptPackages(packages).scan()){
            return scan.getAllClasses().loadClasses();
        }
    }
}
识别是否存入容器的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
    /**
     * 声明一个value属性,用来定义Bean的别名
     * 用做容器中的key
     * @return
     */
    String value();

    /**
     * 是否唯一
     * @return
     */
    boolean sole() default true;
}
容器工厂

其中有两个容器,一个负责单例 一个负责原型

public class ContainerFactory {

    /**
     * 单例容器
     */
    private static Map<String, Object> container = new HashMap<>();
    /**
     * 原型容器
     */
    private static Map<String, Class<?>> archetype = new HashMap<>();

    /**
     * 初始化容器
     * 参数表示要扫描的包路径
     */
    public ContainerFactory(String... packages) {
        List<Class<?>> classList = ScanUtils.scan(packages);
        resolveClass(classList);
    }

    /**
     * 解析class集合,找到带有@Bean注解的类
     */
    private void resolveClass(List<Class<?>> classList) {
        classList.forEach(clazz -> {
            if (clazz.isAnnotationPresent(Bean.class)) {
                Bean bean = clazz.getAnnotation(Bean.class);
                // 获取 Bean 的作为 k
                String k = bean.value();
                // 判断是否需要唯一
                if (bean.sole()) {
                    // 唯一,添加到单例容器
                    container.put(k, newInstance(clazz));
                } else {
                    // 不唯一,添加到原型容器
                    archetype.put(k, clazz);
                }
            }
        });
    }

    /**
     * 创建实例
     *
     * @param clazz 类
     * @return
     */
    private static Object newInstance(Class<?> clazz) {
        try {
            return clazz.getConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("实例创建失败:", e);
        }
    }

    /**
     * 容器工厂方法
     */
    public static <T> T getBean(String name) {
        // 判断单例容器中是否有
        if (container.containsKey(name)) {
            return (T) container.get(name);
        } else {
            return (T) newInstance(archetype.get(name));
        }
    }
}

优缺点

优点

  1. 单例容器:不会随意创建实例,只会创建一次(仅限于所在的容器工厂内)
  2. 原型容器:存储实例的 Class<?> ,在 getBean 执行是创建

思路

为什么要原型容器
因为在多线程中单例有线程安全问题
所以使用原型给每个线程分配一个对象就不会有线程安全的问题