注解的原理

189 阅读4分钟

辛苦写作,觉得还可以的朋友赏个赞吧! 这里补上一篇文章反射破坏单例模式的案例

class SingleExample{
    private static SingleExample instance;

    public SingleExample() {
        //加了这个判断才避免单例模式被反射破坏
        if(instance!=null){
            throw new RuntimeException("instance must be single");
        }
    }

    public static SingleExample getInstance(){
        if(instance==null){
            instance = new SingleExample();
        }
        return instance;
    }
}
//单例模式下这两个实例相同
SingleExample example = SingleExample.getInstance();
SingleExample example1 = SingleExample.getInstance();
System.out.println(example == example1);//true
//利用反射方式调用默认构造函数创建对象
Class<? extends SingleExample> clazz = example.getClass();
Constructor<? extends SingleExample> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingleExample example2 = declaredConstructor.newInstance();
System.out.println(example == example2);//false

注解的概念及其作用

JDK1.5引入的新特性,其主要作用是 编写文档、代码分析、编译检查

例如 @Override 就对实现的方法名进行了编译检查

注释中的@Param @Return等说明信息 可以通过javadoc命令生成文档

像源码中一些过时的早期版本的api 通过 @Deprecated 注解实现方法被划横线 不建议使用效果

@SuppressWarnings 忽略警告

jdk8引入预定义注解,@FunctionalInterface 约束一个接口为函数式接口,该接口有且仅有一个抽象方法

注解的本质实际上就是一个接口,可以通过javap命令查看生成的class文件验证。

注解中定义的方法称为属性,可以是int类型 string类型 数组类型 枚举类型 class类型 或者 注解类型

元注解

@Retention(RetentionPolicy.RUNTIME) //作用域 编译期 SOURCE CLASS 运行期 RUNTIME @Target(ElementType.type) //使用目标表示注解用于 类 方法 属性 接口 @Documented //是否可以生成文档 @Inherited //是否可以被继承

注解解析器

编译器工作,将.java文件生成语法树,解析注解处理器,生成字节码,最终输出.class文件

image.png

java中提供了 Abstractrocessor 以用于程序员自定义注解 只需要继承这个抽象类并重写方法

首先看初始化方法

public synchronized void init(ProcessingEnvironment var1) {
    if (this.initialized) {
        throw new IllegalStateException("Cannot call init more than once.");
    } else {
        Objects.requireNonNull(var1, "Tool provided null ProcessingEnvironment");
        this.processingEnv = var1;
        this.initialized = true;
    }
}

然后看支持的注解类型

public Set<String> getSupportedAnnotationTypes() {
    SupportedAnnotationTypes var1 = (SupportedAnnotationTypes)this.getClass().getAnnotation(SupportedAnnotationTypes.class);
    if (var1 == null) {
        if (this.isInitialized()) {
            this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedAnnotationTypes annotation found on " + this.getClass().getName() + ", returning an empty set.");
        }

        return Collections.emptySet();
    } else {
        return arrayToSet(var1.value());
    }
}

起源版本

public SourceVersion getSupportedSourceVersion() {
    SupportedSourceVersion var1 = (SupportedSourceVersion)this.getClass().getAnnotation(SupportedSourceVersion.class);
    SourceVersion var2 = null;
    if (var1 == null) {
        var2 = SourceVersion.RELEASE_6;
        if (this.isInitialized()) {
            this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedSourceVersion annotation found on " + this.getClass().getName() + ", returning " + var2 + ".");
        }
    } else {
        var2 = var1.value();
    }

    return var2;
}

注解处理器的核心方法 这是一个抽象方法需要继承实现

public abstract boolean process(Set<? extends TypeElement> var1, RoundEnvironment var2);

自定义注解实践

模拟实现lombok

首先自定义一个注解 并添加上源注解

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface FiledAnno {
    String value() default "hello marx";
}

然后编写注解处理器 重写 process方法

@SupportedAnnotationTypes("com.zld.cloud.FiledAnno") //需要解析的注解
@SupportedSourceVersion(SourceVersion.RELEASE_1)
public class FiledAnnotationProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }

    /**
     * 扫描实体类上有@FieldAnno 的属性
     * @param annotations
     * @param roundEnv
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //扫描所有带@FiledAnno的类
        Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(FiledAnno.class);
        for (Element element : elementsAnnotatedWith) {
            VariableElement varField = (VariableElement) element;//这里强转是为了取值
            TypeElement typeElement = (TypeElement) varField.getEnclosingElement();
            String clssName = typeElement.getQualifiedName().toString();
            Name fieldName = varField.getSimpleName();
            //拿到类名 拿到属性名 接下来可以为所欲为了
            ...
        }
            return false;
    }
}

接下来需要将注解处理器注册spring 需要遵循java的spi扩展规范,将注解处理器的完整 路径名 写入到 resource > META-INF > service ;

此方法有替代方案, 导入google的自动注册依赖包

    <!-- 自动注册注解库 -->
    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service</artifactId>
        <version>1.0-cr5</version>
    </dependency>
@AutoService(Processor.class) //使用google的自动注册meta-info的注解
@SupportedAnnotationTypes("com.zld.cloud.FiledAnno") //需要解析的注解
@SupportedSourceVersion(SourceVersion.RELEASE_1)
public class FiledAnnotationProcessor extends AbstractProcessor {
        ...
}

注解处理器实现了代码增强和动态扩展的功能,除此之外也可以利用反射实现,实际上 jdk的动态代理(Proxy.newProxyInstance())和cglib 底层的实现原理就是反射

//使用代理方式实现代码增强
ChildClass target = new ChildClass();//待增强目标类
//JDK动态代理
ChildClass myClass = (ChildClass) Proxy.newProxyInstance(
        target.getClass().getClassLoader(),//类加载器
        target.getClass().getInterfaces(),//类所有接口
        (proxy, method, params) -> { //反射方式实现代码增强
            Class<? extends ChildClass> clazz1 = target.getClass();//获取目标类
            Method[] declaredMethods = clazz1.getDeclaredMethods();//获取目标类中的方法
            for (Method declaredMethod : declaredMethods) {
                // 查找方法中加了自定义注解的
                if (declaredMethod.getAnnotation(FiledAnno.class) != null) {
                    System.out.println("开始执行代码增强逻辑!");
                }
            }
            return method.invoke(target, params);
        }
);
myClass.customerDefine("代理类调用方法 ");

工厂设计模式简单实现 实际上是面向过程编程不算设计模式

public class BreakFastBars {
    public static void main(String[] args) throws IOException {
        BreakFastBars breakFastBars = new BreakFastBars();
        BreakFast breakFast = breakFastBars.buy(readCustomerIn());
        System.out.println("Please pay for $"+breakFast.getPrice());
    }

    private BreakFast buy(String breakfast) {
        if(StringUtils.isEmpty(breakfast)){
            throw new RuntimeException("The food will not be served for the time being!");
        }
        if(breakfast.equals("Dumplings"))
            return new Dumplings();
        if(breakfast.equals("Noodles"))
            return new Noodles();
        if(breakfast.equals("Milk"))
            return new Milk();
        return null;
    }

    private static String readCustomerIn() throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("what can i help u!");
        String input = bufferedReader.readLine();
        return input;
    }
}

利用反射实现工厂设计模式,不够灵活,性能消耗

/**
 * 利用反射 和 类名创建对应的实例
 * @param className
 * @return
 */
private BreakFast create(String className) throws Exception{
    if(className == null){
        throw new RuntimeException("The food will not be served for the time being!");
    }
    Class<?> clazz = Class.forName("xxxxxxxx.xxxx.xx." + className);
    return (BreakFast) clazz.newInstance();
}

利用注解处理器实现工厂模式

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Factory { //自定义Factory注解
    String instanceName();
    Class instanceType();
}
@AutoService(Processor.class) //谷歌的自动配置spi 省的在META-INFO写路径
@SupportedAnnotationTypes("com.zld.cloud.Factory")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class FactoryAnnotationProcessor extends AbstractProcessor {

    private Types typeUtils;//类型处理工具
    private Elements elementUtils;//数据元素对象
    private Filer filer;//文件
    private Messager messager;//警告||异常提示消息

    private Map<String,FactoryGroupedClasses> factoryAnnotatedClassMap = new HashMap<>();

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {//初始化构造函数注入
        typeUtils = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(Factory.class);
        //从上下文中获取所有加了@Factory注解的元素
        try{
            for (Element element : elementsAnnotatedWith) {
                if(element.getKind() != ElementKind.CLASS){
                    throw new CustomerProcessorException(element,"only class could be annotated with  @%s",Factory.class.getSimpleName());
                }
                TypeElement typeElement = (TypeElement) element;
                //封装注解为注解类
                FactoryAnnotatedClass factoryAnnotatedClass = new FactoryAnnotatedClass(typeElement);
                //factoryAnnotatedClassMap<@Factory,Map<"Milk",FactoryAnnotatedClass>>
                FactoryGroupedClasses factoryGroupedClasses = factoryAnnotatedClassMap.get(factoryAnnotatedClass.getQualifiedFactoryGroupName());
                if(factoryGroupedClasses == null){
                    String qualifiedFactoryGroupName = factoryAnnotatedClass.getQualifiedFactoryGroupName();
                    factoryGroupedClasses = new FactoryGroupedClasses(qualifiedFactoryGroupName);
                    factoryAnnotatedClassMap.put(qualifiedFactoryGroupName,factoryGroupedClasses);
                }
                factoryGroupedClasses.add(factoryAnnotatedClass);
                factoryGroupedClasses.generateCode(elementUtils,filer);
                factoryAnnotatedClassMap.clear();
            }
        }catch (CustomerProcessorException e){
            error(e.getElement(),e.getMessage());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return false;
    }

    public void error(Element e,String message){
        messager.printMessage(Diagnostic.Kind.ERROR,message,e);
    }
}

class CustomerProcessorException extends Exception{
    Element element;

    public CustomerProcessorException(Element element, String message, Object ... args) {
        super(String.format(message,args));
        this.element = element;
    }

    public Element getElement(){
        return element;
    }
}
@Data
class FactoryAnnotatedClass{

    private TypeElement typeElement; //被注解的元素
    private String qualifiedFactoryGroupName; //指定类型类合法全名
    private String simpleFactoryGroupName; //指定类型类简单名
    private String instanceName;//注解中指定的ID

    /**
     * aClass是一个真正的class对象
     * @param typeElement
     */
    public FactoryAnnotatedClass(TypeElement typeElement) {
        try { //增加过@Factory注解的类已经编译过
            Factory annotation = typeElement.getAnnotation(Factory.class);

            instanceName = annotation.instanceName();
            Class<? extends Annotation> aClass = annotation.annotationType();

            qualifiedFactoryGroupName = aClass.getCanonicalName();
            simpleFactoryGroupName = aClass.getSimpleName();
        }catch (MirroredTypeException e){ // 如果还未编译会抛出编译异常
            DeclaredType declaredType = (DeclaredType)e.getTypeMirror();
            TypeElement element = (TypeElement) declaredType.asElement();
            qualifiedFactoryGroupName = element.getQualifiedName().toString();
            simpleFactoryGroupName = element.getSimpleName().toString();
        }

    }
}

class FactoryGroupedClasses{

    private static final String SUFFIX = "Factory";

    private String qualifiedClassName;

    private Map<String, FactoryAnnotatedClass> itemsMap = new LinkedHashMap<String, FactoryAnnotatedClass>();

    public FactoryGroupedClasses(String qualifiedClassName) {
        this.qualifiedClassName = qualifiedClassName;
    }

    public void add(FactoryAnnotatedClass factoryAnnotatedClass) {
        FactoryAnnotatedClass existing = itemsMap.get(factoryAnnotatedClass.getInstanceName());
        if (existing != null) {

            // Already existing
            throw new CustomerProcessingException(factoryAnnotatedClass.getTypeElement(),
                    "Conflict: The class %s is annotated with @%s with id ='%s' but %s already uses the same id",
                    factoryAnnotatedClass.getTypeElement().getQualifiedName().toString(), Factory.class.getSimpleName(),
                    factoryAnnotatedClass.getInstanceName(), existing.getTypeElement().getQualifiedName().toString());
        }
        itemsMap.put(factoryAnnotatedClass.getInstanceName(), factoryAnnotatedClass);
    }

    //java poet 优雅生成java代码 参照 https://www.jianshu.com/p/d0686ce07f4f
    public void generateCode(Elements elementUtils, Filer filer) throws IOException {
        TypeElement superClassName = elementUtils.getTypeElement(qualifiedClassName);
        String factoryClassName = superClassName.getSimpleName() + SUFFIX;
        String qualifiedFactoryClassName = qualifiedClassName + SUFFIX;
        PackageElement pkg = elementUtils.getPackageOf(superClassName);
        String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();

        MethodSpec.Builder method = MethodSpec.methodBuilder("create")
                .addModifiers(Modifier.PUBLIC)
                .addParameter(String.class, "id")
                .returns(TypeName.get(superClassName.asType()));

        // check if id is null
        method.beginControlFlow("if (id == null)")
                .addStatement("throw new IllegalArgumentException($S)", "id is null!")
                .endControlFlow();

        // Generate items map

        for (FactoryAnnotatedClass item : itemsMap.values()) {
            method.beginControlFlow("if ($S.equals(id))", item.getInstanceName())
                    .addStatement("return new $L()", item.getTypeElement().getQualifiedName().toString())
                    .endControlFlow();
        }

        method.addStatement("throw new IllegalArgumentException($S + id)", "Unknown id = ");

        TypeSpec typeSpec = TypeSpec.classBuilder(factoryClassName).addModifiers(Modifier.PUBLIC).addMethod(method.build()).build();

        // Write file
        JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
    }
}