自定义容器

实现一个简单容器

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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值