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

0 直接看实现

1 自定义注解 Annotation

这里给出自定义注解的例子:

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方法,那最普通的就如下:
分别实现其类然后调用,这未免有点难看:

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,然后传具体的参数就能实例化相应的类:

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 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
public interface Algorithm {
    
    
    /**
     *
     * 使用指定的工厂里的函数来处理input
     * @param input
     * @return
     */
    String exAlgo(String input);
}
  • 2 为这个接口实现一个算法:
@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 从用户层面考虑,为用户实现一个上下文类,这个上下文类帮助用户实例化对应的算法类,然后调用相关的算法类:
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 调用示例:
        AlgoFactoryContext ctx = new AlgoFactoryContext("airMissilePipeTest");
        ctx.execAlgo("info to process!");

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

  • 1 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
public interface Algorithm {
    
    
    /**
     *
     * 使用指定的工厂里的函数来处理input
     * @param input
     * @return
     */
    String exAlgo(String input);
}
  • 2 为这个接口实现一个算法:
@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 创建上下文类然后自动注入实现接口的那些类:
@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无法发挥作用
@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);
    }
...
}

Guess you like

Origin blog.csdn.net/cxy_hust/article/details/120255908