Java高级之注解

本文详细介绍了Java注解的原理、创建、查看和应用。注解用于给程序添加元信息,可被编译器、运行时系统或其他工具使用。通过示例展示了如何创建和使用注解,包括定制序列化和实现DI容器。通过反射获取注解信息,并利用注解实现对象的依赖注入。此外,还讨论了单例容器的实现,以防止无限递归和循环依赖问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java高级之注解

注解介绍

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

注意:本质上注解就是继承Annotation接口、形式上使用@annotation、如果定义@Test会被编译成publi interface Test extends Annotation{}

创建注解

先通过一个例子来说明、先看@Override定义:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

参数说明

1、@Target:标识注解的目标定义是一个可选择的值

  • TYPE:表示类、接口(包括注解),或者枚举声明;
  • FIELD:字段,包括枚举变量;
  • METHOD:方法;
  • PARAMETER:方法中的参数;
  • CONSTRUCTOR:构造方法;
  • LOCAL_VARIABLE:本地变量;
  • Module:模块(since Java9)

目标可以标识多个。如果没有则默认全部

2、@Retention:标识注解信息保留到什么时候,取值只能有一个,类型为RetentionPolicy

,它是一个枚举、有三个取值;

  • SOURCE:只在源代码保留,编译器将代码编译为字节码文件后就会丢弃;
  • CLASS:保留到字节码文件,但Java虚拟机将记载进去内存不一定会保存。
  • RUNTIME:一直保留到运行时。

ps:带有@Inherited注解的注解可以让其子类来继承

查看注解信息

通过反射相关类来获取有关注解的方法

//获得所有注解
public Annotation[] getAnnotaions()
 //获得本元素上的所有声明的注解,忽略inherited来的
public Annotation[] getDeclaredAnnotations()
//获取指定类型的注解,没有返回null
public <A extends Annotaion> A getAnnotation(Class<A> annotaionClass)
 //判断是否有指定类型的注解
public boolean isAnnotaionPresent(class<? extends Annotaion> annotationClass)

Demo反射获取注解信息

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultValue {
    String value() default "";
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryParam {
    String value();
}
public class MethodAnnotations {

   public void hello(@QueryParam("action") String action,
         @QueryParam("sort") @DefaultValue("asc") String sort){
      // ...
   }
   
   public static void main(String[] args) throws Exception {
     //反射获得类信息,根据名称以及参数类型获得对应方法。
      Class<?> cls = MethodAnnotations.class;
      Method method = cls.getMethod("hello", new Class[]{String.class, String.class});
      //获得参数的注解。
      Annotation[][] annts = method.getParameterAnnotations();
      for(int i=0; i<annts.length; i++){
         System.out.println("annotations for paramter " + (i+1));
         Annotation[] anntArr = annts[i];
         for(Annotation annt : anntArr){
            if(annt instanceof QueryParam){
               QueryParam qp = (QueryParam)annt;
               System.out.println(qp.annotationType().getSimpleName()+":"+ qp.value());
            }else if(annt instanceof DefaultValue){
               DefaultValue dv = (DefaultValue)annt;
               System.out.println(dv.annotationType().getSimpleName()+":"+ dv.value());
            }
         }
      }
   }
}

注解应用

一、定制序列化

1、定义两个序列化注解

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

2、定义实体类

public class Student {
    @Lable("姓名")
    private String name;
    @Lable("出生日期")
    @Format(pattern = "yyyy/MM/dd")
    private Date born;
    @Lable("分数")
    private Double source;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBorn() {
        return born;
    }

    public void setBorn(Date born) {
        this.born = born;
    }

    public Double getSource() {
        return source;
    }

    public void setSource(Double source) {
        this.source = source;
    }
}

3、定义方法来实现定制序列化

/**
 * @Author Zzneko
 * @Date 2022/9/9 14:14
 **/
public class Transfer {
    public static String format(Object obj){
        //反射获得对象所有字段
        try {
            Class<?> aClass = obj.getClass();
            Field[] fields = aClass.getDeclaredFields();
            StringBuilder sb = new StringBuilder();
            for (Field field : fields) {
                if(!field.isAccessible()){
                    field.setAccessible(true);
                }
                //获得该字段约定的信息
                Lable lable = field.getAnnotation(Lable.class);
                String name = lable!=null ? lable.value:field.getName();
                //获取当前字段
                Object value = field.get(obj);
                if (value!=null&&field.getType()== Date.class) {
                    //日期类型单独处理
                    value = formatDate(field,value);
                }
                sb.append(name+":"+value+"\n");
            }
            return sb.toString();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private static Object formatDate(Field field,Object value){
        Format format = field.getAnnotation(Format.class);
        if (format!=null) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format.pattern());
            simpleDateFormat.setTimeZone(TimeZone.getTimeZone(format.timezone()));
            return simpleDateFormat.format(value);
        }
        return value;
    }
}

返回结果:在这里插入图片描述

二、DI容器

1、定义注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleInject {
}

2、定义两个service

public class ServiceB {
    @SimpleInject
    private ServiceA a;
    public void callA(){
        a.action();
    }
}
/**
 * @Author Zzneko
 * @Date 2022/9/9 14:56
 **/

public class ServiceA {
//    @SimpleInject
//    ServiceB b;
    public void action(){
        System.out.println("hello world");
    }
}

3、容器类

package Annotaion.diInject;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * @Author Zzneko
 * @Date 2022/9/9 14:53
 **/
public class DiContainer {
    /**
     * 思路:
     * 1、首先扫描到类的所有字段、
     * 2、看字段上是否拥有SimpleInject注解如果拥有该注解则进行对象的创建。
     * */
    public static<T> T getInstance(Class<T> tClass){
        try {
            T obj = tClass.getConstructor().newInstance();
            Class<?> aClass = obj.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if (field.isAnnotationPresent(SimpleInject.class)) {
                    Class<?> type = field.getType();
                    field.set(obj,getInstance(type));
                }
            }
            return obj;
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

4、测试

/**
 * @Author Zzneko
 * @Date 2022/9/9 14:25
 **/
public class Entering {
    public static void main(String[] args) {
        ServiceB instance = DiContainer.getInstance(ServiceB.class);
        instance.callA();
    }
}
/**
*结果:hello world
*/

Ps:这里提一嘴。如果把ServiceA中的注解打开。那么就会无限递归来进行创建对象。这里就类似于Spring的循环依赖问题了。感兴趣的可以看看源码是如何解决的。

单例容器
package Annotaion.diInject;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author Zzneko
 * @Date 2022/9/9 15:32
 **/
public class SingletonContainer {
    /**
     * 缓存所有以及创建过的对象
     * */
    private static Map<Class<?>,Object> container = new ConcurrentHashMap<>();

    /**
     * 创建对象的实例
     * */
    public static<T> T getInstance(Class<?> clz){
        //如果没有标记为单例直接创建对象
        if (!clz.isAnnotationPresent(SingletonBean.class)) {
            return createInstance(clz);
        }
        //否则直接从容器中获取
        Object obj = container.get(clz);
        //如果对象不为空、则直接返回、否则在容器里进行创建。
        if (obj != null) {
            return ((T) obj);
        }
        //double check单例
        synchronized (clz){
            obj = container.get(clz);
            if(obj==null){
                obj = createInstance(clz);
                container.put(clz,obj);
            }
        }
        return ((T) obj);
    }

    /**
     * 创建对象实例
     * */
    private static <T> T createInstance(Class<?> clz) {
        try {
            T obj = (T) clz.getConstructor().newInstance();
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if(field.isAnnotationPresent(SimpleInject.class)){
                    Class<?> type = field.getType();
                    field.set(obj,getInstance(type));
                }
            }
            return obj;
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值