Java 基础4 - 注解

注解

注解就是给程序添加一些信息,用字符 @ 开头,这些信息用于修饰它后面紧挨着的其他代码元素,比如类、接口、字段、方法、方法中的参数、构造方法等。注解可以被编译器、程序运行时和其他工具使用,用于增强或修改程序行为等。

示例

接下来先来写一个注解试试,结合注解实现字段格式化。

1.注解定义:

// 第一个
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Format {
    String pattern() default "yyyy-MM-dd HH:mm:ss";
    String timeZone() default "GMT+8";
}

// 第二个
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Label {
    String value() default "";
}

2.实现注解功能:

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class SimpleFormatterDemo {
    public static String format(Object obj) throws IllegalAccessException {
        Class<?> cls = obj.getClass();
        StringBuilder sb = new StringBuilder();

        for(Field f: cls.getDeclaredFields()) {
            if(!f.isAccessible()) {
                f.setAccessible(true);
            }
            // 处理这个字段上的 Label 注解
            Label label = f.getAnnotation(Label.class);

            String name = label != null ? label.value() : f.getName();
            Object value = f.get(obj);

            if(value != null && f.getType() == Date.class) {
                value = formatData(f, value);
            }
            sb.append(name + ": " + value + "\n");
        }
        return sb.toString();
    }

    private static Object formatData(Field field, Object value) {
        // 看看这个字段上有没有 Format 这个注解
        // 这也说明实际上所有的注解类型都是扩展的Annotation
        Format format = field.getAnnotation(Format.class);
        // 有的话做一次转换
        if(format != null) {
            SimpleDateFormat sdf = new SimpleDateFormat(format.pattern());
            sdf.setTimeZone(TimeZone.getTimeZone(format.timeZone()));
            return sdf.format(value);
        }
        return value;
    }
}

3.使用注解:

import java.text.SimpleDateFormat;
import java.util.Date;

public class App {
    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
        Student student = new Student("电刀", sdf.parse("2000-01-01"), 89.1d);
        System.out.printf(SimpleFormatterDemo.format(student));
    }
}

class Student {
    @Label("姓名")
    private String name;

    @Label("生日")
    @Format(pattern = "yyyy/MM/dd")
    private Date birthday;

    @Label("分数")
    private double score;

    public Student(String name, Date birthday, double score) {
        this.name = name;
        this.birthday = birthday;
        this.score = score;
    }
}

分析

可以看到以上三步的1、3都比较眼熟,一般的业务代码中就可以经常见到,不同注解的最大区别在于第二步:具体功能实现。下面来分析一下每一步。

  1. 定义注解

    这个步骤主要描述:

    1. 注解接收那些参数

      实例中 FormatpatterntimeZone ,而且还写了默认值。 如果定义了参数且没有提供默认值,在使用注解时必须提供具体的值,不能为null

    2. 保留到什么时候

      这个靠 @Rentention(),可以选择保留到RUNTIME(运行时)、SOURCE(源码,运行的时候就没了)、CLASS(编译器)三个阶段。

    3. 在哪里可以使用它

      这个靠 @Target() ,可以选择字段(FIELD)、方法(METHOD,接口也行的)、参数(PARAMETER)、构造方法(CONSTRUCTOR)、类(TYPE)等。

  2. 实现具体功能

    很明显这段代码描述了当出现某个注解时,该对目标进行什么处理,示例中就是针对出现注解的目标将其字段进行格式化。

    很明显这也表明注解的定义和注解如何工作并不是写在一起的,定义可以很简单,相当于一个记号,功能实现往往比较复杂。

    Typescript 也有注解,但是它的注解准确来说叫做装饰器,装饰器的定义和注解干了什么是写在一起的,其实就是一个函数,官方也说明了就是一个函数,看起来类似于 Java 的 AOP 但写起来没有 AOP 复杂。

  3. 使用注解

    这个很简单了,直接@@@就好啦。

容器Demo

此容器非彼容器,不是指链表和数组那种容器。

以前刚接触Spring的时候,经常看到 “容器”、“依赖注入” 这些词,然而都是以 “管理对象生命周期” 一笔带过,初学者可以理解为一个自动生成对象的东西。一个明显的特点就是:有了这个东西,业务代码可以不用出现 new 。接下来实现一个简陋的 Demo,看完可以理解另一个概念。

  1. SimpleInject 注解

    package inject;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface SimpleInject {
    }
    
  2. SimpleContainer 容器

    package inject;
    
    import java.lang.reflect.Field;
    
    public class SimpleContainer {
        public static <T> T getInstance(Class<T> cls) {
            try {
                T obj = cls.newInstance();
    
                Field[] fields = cls.getDeclaredFields();
    
                for(Field f: fields) {
                    // 没 SimpleInject 这个注解则不管
                    if(!f.isAnnotationPresent(SimpleInject.class)) {
                        continue;
                    }
                    if(!f.isAccessible()) {
                        f.setAccessible(true);
                    }
    
                    Class<?> filedClass = f.getType();
                    // “注入” 依赖
                    f.set(obj, getInstance(filedClass));
                }
                return obj;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    
        }
    }
    
  3. DIDemo 测试类

    package inject;
    
    public class DIDemo {
        public static void main(String[] args) {
            ServiceA serviceA = SimpleContainer.getInstance(ServiceA.class);
            serviceA.callB(); // 输出BBB
        }
    }
    class ServiceA {
        @SimpleInject
        ServiceB serviceB;
        public void callB() {
            serviceB.action();
        }
    }
    class ServiceB {
        public void action() {
            System.out.println("BBB");
        }
    }
    

上边其实就是一个依赖注入

这里的容器只有一个制造对象的功能。

所谓的依赖注入就是:A中的一个变量需要持有一个B的实例对象(这个步骤叫A依赖B),容器给这个变量赋值(这个步骤叫注入),合起来叫做依赖注入。当然完整的依赖管理不是这么简单,还有循环依赖等问题需要解决。

本文作者:为何匆匆

本文链接:https://www.cnblogs.com/nyfblog/p/16519131.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   为何匆匆  阅读(53)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起