使用自定义注解,动态代理和多线程模拟计算

简介

因为最近项目中完成了一个比较复杂的计算功能,核心就是在计算类上添加自定义注解,通过扫描注解得到需要计算的类,开启多线程通过动态代理执行该类的计算方法,一下是简化版的代码,供大家参考.

1.包结构

包结构

2.自定义计算注解

创建自定义注解CalcAnnotation,通过扫描带有该注解的类,执行该类的计算方法

/**
 * 自定义计算注解
 * 添加该注解说明该dao需要计算
 */
@Component
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CalcAnnotation {
    String name() default "";
}

3.添加需要计算的类

创建Calc1_1_1Dao的类,可以完成自己的计算的逻辑,根据需求可以创建多个


/**
 * 计算表1-1-1
 */
@CalcAnnotation(name = "1-1-1")
public class Calc1_1_1Dao extends CalcBaseDao {


    @Override
    public void calculate() {
        /*
        模拟计算的方法
         */
        List<String> list = super.calcCacheMap.get(super.almId);
        list.add(year+"年"+month+"月计算表1-1-1的计算结果");
        calcCacheMap.put(almId,list);
        System.out.println(year+"年"+month+"月执行表1-1-1的计算完成");
    }
}

4.添加需要计算的父类

这个类实现了计算的接口和初始化计算的接口,重写了其中的一些方法

public   class CalcBaseDao implements CalcService, CalcInitDestory {
    /**
     * 缓存表计算数据
     */
    public static Map<String, List<String>> calcCacheMap = new HashMap<>();

    /**
     * 计算入参:年份
     */
    public static String year;

    /**
     * 计算入参:月份
     */
    public static String month;

    /**
     * 计算的id:根据id可以查询计算的结果
     */
    public static String almId;


    @Override
    public void init() {
        /*
        初始化一个随机的计算id,初始化缓存表
         */
        System.out.println("执行计算baseDao的初始化方法");
        List<String> list = new ArrayList<>();
        this.almId = UUID.randomUUID().toString();
        calcCacheMap.put(this.almId,list);
    }

    @Override
    public  void destory(){
        System.out.println("执行计算的销毁方法");
    }

    @Override
    public  void calculate(){

    }

    @Override
    public List<String> queryData(String almId) {
        System.out.println("执行计算的查询方法查询计算结果");
        return calcCacheMap.get(almId);
    }
    @Override
    public void setCalcParameter(Map<String,Object> parameter){
        if(parameter!=null){
            this.year = getString(parameter.get("year"));
            this.month = getString(parameter.get("month"));
        }
    }

    @Override
    public String getAlmId() {
        return almId;
    }

    /**
     * 一个工具方法,可以在工具类中写成静态方法
     * @param obj
     * @return String
     */
    String getString(Object obj){
        if (null == obj || "".equals(obj) || obj.toString().startsWith("null")) {
            return "";
        } else {
            return obj.toString().trim();
        }
    }
}

5.创建计算接口和计算计算初始化的接口

创建计算接口

/**
 * 计算接口
 */
public interface CalcService {

    /**
     * 执行计算
     */
    public void calculate();

    /**
     * 查询计算结果
     * @param almId 计算id
     * @return
     */
    public List<String> queryData(String almId);

    /**
     * 设置计算的入参
     * @param parameter
     */
    public void setCalcParameter(Map<String,Object> parameter);

    /**
     * 获取计算id
     * @return
     */
    public String getAlmId();
}

创建初始化接口

/**
 * 计算初始化和结束方法
 */
public interface CalcInitDestory {
    public void init();

    public void destory();
}

6.创建动态代理的处理类

创建目标类为CalcService的处理类

/**
 * 动态代理的处理器,目标类为CalcService
 */
public class MyHandler implements InvocationHandler {

    private CalcService calcService;

    public MyHandler(CalcService calcService) {
        this.calcService = calcService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行动态代理拦截器的方法");
        Object obj = method.invoke(calcService,args);
        System.out.println("执行动态代理拦截器的方法完毕");
        return obj;
    }

}

创建目标类为CalcInitDestory 的处理类

/**
 * 动态代理的处理类,目标类为CalcInitDestory
 */
public class MyInitHandler implements InvocationHandler {
    private CalcInitDestory calcInitDestory;

    public MyInitHandler(CalcInitDestory calcInitDestory) {
        this.calcInitDestory = calcInitDestory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行初始化接口的动态代理方法");
        Object object = method.invoke(calcInitDestory,args);
        System.out.println("执行初始化接口的动态代理方法完毕");
        return object;
    }
}

7.创建扫描spring容器的工具类

/**
 * Springboot的工具类
 */
@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }

    //通过注解,获取包含该注解的bean对象
    public static  Map<String,Object> getBeansWithAnnotation(Class<? extends Annotation> clazz){
        ApplicationContext applicationContext = getApplicationContext();
        Map<String,Object> map = applicationContext.getBeansWithAnnotation(clazz);
       return map;
    }
}

8.创建计算的核心类

计算类提供了
1.计算的前置方法before(),处理计算前的逻辑,判断能否进行计算
2.计算方法calculate()通过扫描spring容器中带有计算注解CalcAnnotation的类,然后开启多线程,通过动态代理执行这些类的计算方法
3.计算的后置处理方法after(),处理计算完成后的逻辑,比如关闭线程池等
4.查询方法queryData(String almid),通过计算的id查询计算的结果

/**
 * 计算类
 */
public class Calculate {
    String year;
    String month;
    Map<String,Object> parameter = new HashMap<>();
    /**
     * 计算对象的集合
     */
    private List<CalcService> tables  = new ArrayList<>();
    /**
     * 多线程计算线程池
     */
    private ExecutorService pool;

    public Calculate(String year, String month) {
        this.year = year;
        this.month = month;
        parameter.put("year",year);
        parameter.put("month",month);
    }

    public boolean before(){
        /*
        动态代理初始化方法的接口执行初始化方法
         */
        CalcInitDestory proxy = (CalcInitDestory) Proxy.newProxyInstance(CalcInitDestory.class.getClassLoader(),new Class[]{CalcInitDestory.class},new MyInitHandler(new CalcBaseDao()));
        proxy.init();
        /*
        计算前的逻辑:1.可以判断是否可以计算;2.初始化线程池
         */
        pool = Executors.newFixedThreadPool(2);
        return true;
    }

    public void after(){
        /*
        计算后的逻辑:销毁线程池
         */
        pool.shutdown();
    }

    /**
     * 执行计算
     * @return 计算生成的id
     */
    public String calculate() {
        String almid = null;
        try {
            if(before()){
                List<Future<CalcService>> futures = new ArrayList<>();
                addCalcService();
                for(CalcService calcService:tables){
                    /*
                    开始多线程计算
                     */
                    futures.add(pool.submit(new Callable<CalcService>() {
                        @Override
                        public CalcService call() throws Exception {
                            //动态代理
                            System.out.println("执行动态代理方法");
                            CalcService proxy = (CalcService) Proxy.newProxyInstance(CalcService.class.getClassLoader(),
                                    new Class[]{CalcService.class},new MyHandler(calcService));

                            proxy.setCalcParameter(parameter);
                            proxy.calculate();
                            System.out.println("动态代理方法执行完毕");
                            return calcService;
                            }
                        })
                    );
                }
                /*
                多线程计算的结果集
                 */
               for(Future<CalcService> future:futures){
                    CalcService calcService = future.get();
                    System.out.println("计算结果中的计算id为:"+calcService.getAlmId());
                    almid =calcService.getAlmId();
                    List<String> queryData = calcService.queryData(calcService.getAlmId());
                   for (String str:queryData
                        ) {
                       System.out.println("计算结果为:"+str);
                   }

                }
                after();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return almid;
    }



    /**
     * 通过获取spring容器中带CalcAnnotation的bean得到所有的calcService
     */
    private void addCalcService() {

        Set<Map.Entry<String, Object>> entrySet = SpringUtil.getBeansWithAnnotation(CalcAnnotation.class).entrySet();
        for(Iterator<Map.Entry<String,Object>> iterator = entrySet.iterator();iterator.hasNext();){
            Map.Entry<String,Object> entry = iterator.next();
            CalcService calcService = (CalcService) entry.getValue();
            tables.add(calcService);
        }
    }

    /**
     * 查询计算结果
     * @param almId 计算的id
     * @return 计算结果
     */
    public List<String> queryData(String almId) {
        CalcService proxy = (CalcService) Proxy.newProxyInstance(CalcService.class.getClassLoader(),
                new Class[]{CalcService.class},new MyHandler(new CalcBaseDao()));
        return proxy.queryData(almId);
    }
}

9.测试

	@Test
    void test(){
        Calculate calculate = new Calculate("2020","7");
        String almid = calculate.calculate();
        List<String> queryData = calculate.queryData(almid);
        for (String str:queryData
        ) {
            System.out.println("计算结果为:"+str);
        }
    }

测试执行结果
计算执行结果

一些废话

以上的计算是一个简化版的计算代码,实际业务中我使用的计算配置执行的计算,计算配置有四种:
1.通过sql查询数据如图红色部分;
2.在当前计算表中像Excel表格中A1+B1这样执行一些计算,如图绿色部分
3.通过在计算配置中添加方法名和入参,计算类中添加该计算方法的逻辑,如图蓝色部分
4.直接传值,一般是固定的值
计算配置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值