介绍
简单工厂->超级工厂(简单工厂+反射+泛型)->容器工厂(超级工厂+提前获取并创建需要被创建的类)
原理
容器工厂创建时,创建实例或者原型放入到容器(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);
}
}
反射获取类的容器工厂(终极版)
首先导入 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));
}
}
}
优缺点
优点
- 单例容器:不会随意创建实例,只会创建一次(仅限于所在的容器工厂内)
- 原型容器:存储实例的
Class<?>
,在getBean
执行是创建
思路
为什么要原型容器
因为在多线程中单例有线程安全问题
所以使用原型给每个线程分配一个对象就不会有线程安全的问题