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都比较眼熟,一般的业务代码中就可以经常见到,不同注解的最大区别在于第二步:具体功能实现。下面来分析一下每一步。
-
定义注解
这个步骤主要描述:
-
注解接收那些参数
实例中
Format
有pattern
和timeZone
,而且还写了默认值。 如果定义了参数且没有提供默认值,在使用注解时必须提供具体的值,不能为null
。 -
保留到什么时候
这个靠
@Rentention()
,可以选择保留到RUNTIME(运行时)、SOURCE(源码,运行的时候就没了)、CLASS(编译器)三个阶段。 -
在哪里可以使用它
这个靠
@Target()
,可以选择字段(FIELD)、方法(METHOD,接口也行的)、参数(PARAMETER)、构造方法(CONSTRUCTOR)、类(TYPE)等。
-
-
实现具体功能
很明显这段代码描述了当出现某个注解时,该对目标进行什么处理,示例中就是针对出现注解的目标将其字段进行格式化。
很明显这也表明注解的定义和注解如何工作并不是写在一起的,定义可以很简单,相当于一个记号,功能实现往往比较复杂。
Typescript 也有注解,但是它的注解准确来说叫做装饰器,装饰器的定义和注解干了什么是写在一起的,其实就是一个函数,官方也说明了就是一个函数,看起来类似于 Java 的
AOP
但写起来没有AOP
复杂。 -
使用注解
这个很简单了,直接@@@就好啦。
容器Demo
此容器非彼容器,不是指链表和数组那种容器。
以前刚接触Spring的时候,经常看到 “容器”、“依赖注入” 这些词,然而都是以 “管理对象生命周期” 一笔带过,初学者可以理解为一个自动生成对象的东西。一个明显的特点就是:有了这个东西,业务代码可以不用出现 new
。接下来实现一个简陋的 Demo,看完可以理解另一个概念。
-
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 { }
-
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); } } }
-
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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步