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);
}
}
}