实现一个简单容器
1、导入依赖
导入第三方的扫描包工具依赖
<dependencies>
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.158</version>
</dependency>
</dependencies>
2、创建扫描包工具类
public class ScanUtils {
/**
* 扫描指定的包,并返回相关的Class对象
* @param packages
* @return
*/
public static List<Class<?>> scan(String... packages) {
//创建核心的类图对象(它是扫描的核心类)
ClassGraph graph = new ClassGraph();
//启用所有的扫描机制(支持包、类、方法、注解级别的扫描)
graph.enableAllInfo();
//设置要扫描的包路径
graph.acceptPackages(packages);
//执行扫描并返回扫描的结果集(注意:结果集需要放在try资源中用完需要关闭)
try(ScanResult result = graph.scan()) {
//从结果集中获取所有的class信息,并加载到jvm中
return result.getAllClasses().loadClasses();
}
}
public static void main(String[] args) {
List<Class<?>> list = scan("edu.nf.container");
list.forEach(System.out::println);
}
}
3、定义标注注解
//该注解只能用在类上
@Target(ElementType.TYPE)
//该注解在运行时一直保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
/**
* 声明一个value属性,用来定义Bean的别名
* 用做容器中的key
* @return
*/
String value();
/**
* scope属性用来标识容器保存对象时是否是单例还是原型,
* 默认为true就表示创建的是单例
* @return
*/
boolean scope() default true;
}
4、定义支付类
首先这个案例是这样,我们模拟一个支付的功能,对每个支付的方式进行管理,将标记了注解的支付方式类都放入到容器中。
定义一个支付接口:
/**
* 抽象的支付接口,会有很多不同的支付实现
*/
public interface Payment {
void pay();
}
支付包支付方式
@Bean("aliPay")
public class AliPay implements Payment {
@Override
public void pay() {
System.out.println("使用支付宝支付...");
}
}
微信支付方式
@Bean("weChatPay")
public class WeChatPay implements Payment {
@Override
public void pay() {
System.out.println("使用微信支付...");
}
}
5、定义容器工厂类
public class ContainerFactory {
/**
* 核心容器(单例),存放事先创建好的对象
*/
private static Map<String, Object> singleton = new HashMap<>();
/**
* 核心容器(原型),存放Class对象,需要时再通过这个Class创建实例
*/
private static Map<String, Class<?>> prototype = new HashMap<>();
/**
* 初始化容器
* 参数表示要扫描的包路径
*/
public ContainerFactory(String... packages) {
//执行扫描,返回class集合
List<Class<?>> list = ScanUtils.scan(packages);
//解析所有的class对象找到带有@Bean注解的类
resolveClass(list);
}
/**
* 解析class集合,找到带有@Bean注解的类
*/
private void resolveClass(List<Class<?>> list) {
list.forEach(clazz -> {
//判断是否声明了@Bean注解
if(clazz.isAnnotationPresent(Bean.class)) {
//获取@Bean注解的value属性值,这个值就是作为容器的key
//1. 先得到@Bean注解对象
//Bean anno = clazz.getAnnotation(Bean.class);
//2. 得到注解的value属性的值
//String value = anno.value();
String value = clazz.getAnnotation(Bean.class).value();
//获取scope注解来决定保存的事单例还是原型
if(clazz.getAnnotation(Bean.class).scope()) {
//利用class创建实例
Object instance = newInstance(clazz);
//最后将实例和key保存到容器中
singleton.put(value, instance);
} else {
//否则只保存当前的class对象到原型的容器中
prototype.put(value, clazz);
}
}
});
}
/**
* 根据class对象创建实例
* @param clazz
* @return
*/
private Object newInstance(Class<?> clazz) {
try {
return clazz.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("实例化失败", e);
}
}
/**
* 从容器中获取对象
* @param name
* @return
* @param <T>
*/
public <T> T getBean(String name) {
//先从单例的容器中获取,如果存在则直接返回
Object instance = singleton.get(name);
//如果单例容器中为null,则从原型的容器中获取Class对象来新建实例
//否则就直接返回单例的对象即可
if(instance == null) {
//否则取出原型的Class对象创建实例再返回
Class<?> type = prototype.get(name);
//新建实例
instance = newInstance(type);
}
return (T)instance;
}
}
6、策略上下文
public class PaymentContext {
/**
* 声明支付接口
*/
private Payment payment;
/**
* 声明策略实现类的包名,用于扫描
*/
private final static String PKG_NAME = "edu.nf.strategy.impl";
/**
* 声明一个静态的容器工厂
*/
private static ContainerFactory containerFactory;
/**
* 初始化容器工厂
*/
static {
containerFactory = new ContainerFactory(PKG_NAME);
}
/**
* 在构造方法中创建具体的策略,
* 参数就是页面提交的支付类型
*/
public PaymentContext(String payType) {
payment = containerFactory.getBean(payType);
}
/**
* 封装支付策略的支付调用
*/
public void pay() {
//在这里调用支付策略的真正方法
payment.pay();
}
}
测试:
public class Main {
public static void main(String[] args) {
//从页面接收支付类型
String payType = "aliPay";
//创建支付上下文
PaymentContext context = new PaymentContext(payType);
context.pay();
}
}