javaAnnotationSimpleFactoryStrategyMode - java注解生命周期 & 简单工厂以及策略设计模式

0 直接看实现

  • 1 手写依赖扫描版本
  • 2 spring boot 版本

    1 自定义注解 Annotation

    这里给出自定义注解的例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.soul.weapon.algorithm.annotation;

    import java.lang.annotation.*;

    @Target({ElementType.TYPE, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface WeaponAlgorithm {
    String algoName () default "";
    }

注意注解的申明是用@interface来声明的:
然后这里对每一个参数做简单说明:

  • @Target 说明注解的使用范围
    • TYPE: 用于描述类、接口(包括注解类型) 或enum声明
    • FILED: 用于描述域
  • @Retention 说明注解可以被保留到什么地方
    • 1 RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
    • 2 RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
    • 3 RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

      这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
      明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如接下来要讲到的在运行过程过获得某种注解的所有的类;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife 和 mapstruct 等),就用 CLASS注解;如果只是做一些检查性的操作,比如** @Override**和 @SuppressWarnings,则可选用 SOURCE 注解。

  • @Documentation: 用于生成javadoc
  • @Inherited: 说明被改注解注释的子类会继承该注解

2 简单工厂模式 简单甚至不简单的工厂Ref

简单说明一下,比如你有一个业务要求是,实现n种车的drive方法,那最普通的就如下:
分别实现其类然后调用,这未免有点难看:

1
2
3
4
5
6
7
8
9
10
public class Driver1 {

public static void main(String[] args) {
Car1 car = new Car1();
Car2 car = new Car2();
Car3 car = new Car3();
Car4 car = new Car4();
}

}

使用简单工厂模式就是说,来一个carFactory类,我所有车的实例的创建和使用都通过carFactory,然后传具体的参数就能实例化相应的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Driver2 {
public Car car;
public static void createCar(String name) {
switch (name) {
case "car1":
car = new Car3();
break;
case "car2":
car = new Car3();
break;
case "car3":
car = new Car3();
break;
default:
car = null;
break;
}
LOG.info("Created car name is {}", name);
}
public drive() { car.drive();}
}

但是这里有一个问题,没有遵循开闭原则(开放扩展,关闭修改的原则),那么造成没有遵循的原因是什么?因为每一个实现的类的名称是我们手动写到代码里的,当这些相关的类的名称是我们通过代码可以获取的时候,我们就可以解决该问题了,于是我们使用注解

3 使用注解的反射来完善包含简单工厂模式的策略模式

  • 策略模式 将具体的算法封装到一个context类,context可以根据传入的参数自动调取相关的算法,简单工厂模式还是与之很像的
  • 解决了什么问题
    策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完全的都是相同的工作,只是实现不同,用户通过context以相同的方式调用所有的算法,减少了各种算法实现类与使用类之间的耦合
  • 和工厂模式的区别
    • 1 工厂模式,主要是将对象的创建,和使用进行解耦,而策略模式,主要将策略的定义创建,和使用进行解耦,主要是他们针对的对象不同,一个主体是实体类,另一个是策略类,我个人感觉核心思想都是一样的

3.1 示例分析:

接着上面#1的注解,一个策略模式的例子如下(个人理解,欢迎交流)

  • 1 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface Algorithm {
    /**
    *
    * 使用指定的工厂里的函数来处理input
    * @param input
    * @return
    */
    String exAlgo(String input);
    }
  • 2 为这个接口实现一个算法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @WeaponAlgorithm(algoName = "airMissilePipeTest")
    public class AirMissilePipeTest implements Algorithm {

    @Override
    public String exAlgo(String input) {
    Logger LOG = LoggerFactory.getLogger(AirMissilePipeTest.class);
    LOG.info("airMissilePipeTest algo executing!");
    return input;
    }
    }
  • 3 从用户层面考虑,为用户实现一个上下文类,这个上下文类帮助用户实例化对应的算法类,然后调用相关的算法类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class AlgoFactoryContext {
    private static final Logger LOG = LoggerFactory.getLogger(AlgoFactoryContext.class);
    private static Map<String, Class> allStrategies;

    static {
    Reflections reflections = new Reflections("com.soul.weapon.algorithm.impl",
    new SubTypesScanner(),
    new TypeAnnotationsScanner(),
    new FieldAnnotationsScanner());
    Set<Class<?>> annotatedClasses =
    reflections.getTypesAnnotatedWith(WeaponAlgorithm.class);
    allStrategies = new ConcurrentHashMap<String, Class>();
    for (Class<?> classObject : annotatedClasses) {
    WeaponAlgorithm annotatedAlgo = (WeaponAlgorithm) classObject
    .getAnnotation(WeaponAlgorithm.class);
    allStrategies.put(annotatedAlgo.algoName(), classObject);
    }
    allStrategies = Collections.unmodifiableMap(allStrategies);
    }

    private Algorithm algoExecutor;

    public AlgoFactoryContext (String requiredAlgoName){
    if(allStrategies.containsKey(requiredAlgoName)) {
    LOG.info("algo name is {}", requiredAlgoName);
    try {
    algoExecutor = (Algorithm) allStrategies.get(requiredAlgoName).getDeclaredConstructor().newInstance();
    } catch (NoSuchMethodException | InstantiationException
    | InvocationTargetException | IllegalAccessException ex) {
    LOG.error("Instantiate algo Failed: ", ex);
    }
    } else {
    LOG.error("algo with name: {} not exist!", requiredAlgoName);
    }
    }

    public void execAlgo(String dataString) {
    algoExecutor.exAlgo(dataString);
    }
    }


  • 4 调用示例:
    1
    2
    AlgoFactoryContext ctx = new AlgoFactoryContext("airMissilePipeTest");
    ctx.execAlgo("info to process!");

4 当使用spritboot的自动装配来简化以免手动写reflections

  • 1 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface Algorithm {
    /**
    *
    * 使用指定的工厂里的函数来处理input
    * @param input
    * @return
    */
    String exAlgo(String input);
    }
  • 2 为这个接口实现一个算法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Service(value = "airMissilePipeTest")
    public class AirMissilePipeTest implements Algorithm {

    @Override
    public String exAlgo(String input) {
    Logger LOG = LoggerFactory.getLogger(AirMissilePipeTest.class);
    LOG.info("airMissilePipeTest algo executing!");
    return input;
    }
    }
  • 3 创建上下文类然后自动注入实现接口的那些类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Service
    public class AlgoFactoryContext {
    private static final Logger LOG = LoggerFactory.getLogger(AlgoFactoryContext.class);

    @Autowired(required = true)
    private Map<String, Algorithm> allStrategies;

    private Algorithm algoExecutor;

    public void execAlgo(String requiredAlgoName, String dataString) {
    if(allStrategies.containsKey(requiredAlgoName)) {
    LOG.info("algo name is {}", requiredAlgoName);
    algoExecutor = (Algorithm) allStrategies.get(requiredAlgoName);
    } else {
    LOG.error("algo with name: {} not exist!", requiredAlgoName);
    }
    algoExecutor.exAlgo(dataString);
    }
    }
  • 4 调用:这里需要注意,如果是单独new一个AlgoFactoryContext,并没有办法完成自动装配,猜测原因是,由于使用了sprintboot的autoWire,那么所有依赖都要保证有@component和@sevice/@resouce等去注解,这些注解保证了sprinboot会将这些依赖给管理起来,所以如果new,那么autoWire无法发挥作用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Slf4j
    @RestController
    @RequestMapping("/free/test")
    @RequiredArgsConstructor
    public class FreePipeTestController {

    @Autowired
    private AlgoFactoryContext ctx; // by autowire, so ctx will scan and get all
    // implement of the algorithm interface

    private final PipeTestService pipeTestService;

    @Api
    @GetMapping(value = "/{id}")
    public PipeTest getById(@PathVariable("id") String id)
    {
    // add test for the algo factory:
    ctx.execAlgo("airMissilePipeTest", "telegram from socket!");
    return pipeTestService.getById(id);
    }
    ...
    }