javaAnnotationSimpleFactoryStrategyMode - java注解生命周期 & 简单工厂以及策略设计模式
0 直接看实现
- 1 手写依赖扫描版本
- 2 spring boot 版本
1 自定义注解 Annotation
这里给出自定义注解的例子:1
2
3
4
5
6
7
8
9
10
11package com.soul.weapon.algorithm.annotation;
import java.lang.annotation.*;
public 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 | public class Driver1 { |
使用简单工厂模式就是说,来一个carFactory类,我所有车的实例的创建和使用都通过carFactory,然后传具体的参数就能实例化相应的类:
1 | public class Driver2 { |
但是这里有一个问题,没有遵循开闭原则(开放扩展,关闭修改的原则),那么造成没有遵循的原因是什么?因为每一个实现的类的名称是我们手动写到代码里的,当这些相关的类的名称是我们通过代码可以获取的时候,我们就可以解决该问题了,于是我们使用注解
3 使用注解的反射来完善包含简单工厂模式的策略模式
- 策略模式 将具体的算法封装到一个context类,context可以根据传入的参数自动调取相关的算法,简单工厂模式还是与之很像的
- 解决了什么问题
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完全的都是相同的工作,只是实现不同,用户通过context以相同的方式调用所有的算法,减少了各种算法实现类与使用类之间的耦合 - 和工厂模式的区别
- 1 工厂模式,主要是将对象的创建,和使用进行解耦,而策略模式,主要将策略的定义创建,和使用进行解耦,主要是他们针对的对象不同,一个主体是实体类,另一个是策略类,我个人感觉核心思想都是一样的
3.1 示例分析:
接着上面#1的注解,一个策略模式的例子如下(个人理解,欢迎交流)
- 1 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
1
2
3
4
5
6
7
8
9public interface Algorithm {
/**
*
* 使用指定的工厂里的函数来处理input
* @param input
* @return
*/
String exAlgo(String input);
} - 2 为这个接口实现一个算法:
1
2
3
4
5
6
7
8
9
10
public class AirMissilePipeTest implements Algorithm {
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
42public 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
2AlgoFactoryContext ctx = new AlgoFactoryContext("airMissilePipeTest");
ctx.execAlgo("info to process!");
4 当使用spritboot的自动装配来简化以免手动写reflections
- 1 定义一个接口,这样所有的算法类的调用都按照这个接口调用即可:
1
2
3
4
5
6
7
8
9public interface Algorithm {
/**
*
* 使用指定的工厂里的函数来处理input
* @param input
* @return
*/
String exAlgo(String input);
} - 2 为这个接口实现一个算法:
1
2
3
4
5
6
7
8
9
10
public class AirMissilePipeTest implements Algorithm {
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
public class AlgoFactoryContext {
private static final Logger LOG = LoggerFactory.getLogger(AlgoFactoryContext.class);
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
public class FreePipeTestController {
private AlgoFactoryContext ctx; // by autowire, so ctx will scan and get all
// implement of the algorithm interface
private final PipeTestService pipeTestService;
public PipeTest getById( String id)
{
// add test for the algo factory:
ctx.execAlgo("airMissilePipeTest", "telegram from socket!");
return pipeTestService.getById(id);
}
...
}