JAVA之自定义注解

一、注解的作用

   其实在开发中是很少用到自定义注解的,真的做研发之后就会发现自定义的注解是多么的重要以及常用,野心为项目中自己使用了大量的自定义注解,所以想着有必要记录下来,方便自己查看,也可以分享给各位感兴趣的朋友。包 java.lang.annotation 中包含所有定义自定义注解所需用到的原注解和接口。如接口 java.lang.annotation.Annotation 是所有注解继承的接口,并且是自动继承,不需要定义时指定,类似于所有类都自动继承Object。先来说说JAVA中的注解的作用:

      1、减少大量的代码臃肿,使得代码更加的简洁耐用

      2、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等

      3、通过代码里标识的元数据让编译器能够实现基本的编译检查

二、怎么使用

      使用自定义注解首先要明白它的四个标识:Documented,Inherited,Target(作用范围,方法,属性,构造方法等),Retention(生命范围,源代码,class,runtime)

     1、@Inherited 允许子类继承父类中的注解

     2、@Document 该注解包含在javadoc中,代表会被javac提取成为文档,文档中的内容会因为该注解内的内容不同而不同

     3、@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中

        TYPE : 类、接口

        METHOD: 方法上

        PACKAGE: 包上

        PARAMETER: 参数上

        FIELD: 用于局部变量上

        CONSTRUCTOR: 构造器上

     4、@Retention 什么级别保存该注解信息

        RetentionPolicy.SOURCE :编译之后的class文件中注解被丢弃,运行期更加不会有相关注解信息。

        RetentionPolicy.CLASS : 编译之后的class文件中包含有该注解,但是运行期被丢弃 。

        RetentionPolicy.RUNTIME:编译之后class文件中有该注解,并且运行期间不会被丢弃。

三、例子

    现在越来越感觉自定义注解的好处,下面就讲解三个例子来说明其是多么的优美

    1、为属性设置元数据

   自定义注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)//作用到局部变量上
@Retention(RetentionPolicy.RUNTIME)//在class文件中保存,并且在运行期间不会丢失
public @interface MetaField {
	
	boolean store() default true;//是否存储
	boolean readonly() default true;//是否可读
	String name () default "";//属性名称
	boolean searchable() default false;//是否可搜索

}

   使用:


import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class Student {
	
	@MetaField(name = "主键ID" , store = true , readonly = false , searchable = true)
	private Integer id;
	
	@MetaField(name = "主键ID" , store = true , readonly = true , searchable = true)
	private Integer name;
	
	@MetaField(name = "年龄" , store = true , readonly = false , searchable = false)
	private Integer age;
	
	@MetaField(name = "性别" , store = false , readonly = false , searchable = false)
	private Integer sex;
	
	@MetaField(name = "住址" , store = true , readonly = false , searchable = false)
	private Integer address;

	public List<SortableField> init (){
		List<SortableField> list = new ArrayList<SortableField>();  
		Class entity = this.getClass(); 
        if(entity!=null){  
            Field[] fields = entity.getDeclaredFields(); 
            for(Field f : fields){  
                //获取字段中包含fieldMeta的注解  
                MetaField meta = f.getAnnotation(MetaField.class);  
                if(meta!=null){  
                    SortableField sf = new SortableField(meta, f);  
                    list.add(sf);  
                }  
            }  
        }  
        return list;  
	}
}

   封装自定义属性,方便操作:

aField meta;  
    private Field field;  
    private String name;  
    private Class<?> type;  

	public SortableField(){}  
    
    public SortableField(MetaField meta, Field field) {  
        super();  
        this.meta = meta;  
        this.field = field;  
        this.name=field.getName();  
        this.type=field.getType();  
    }  
      
      
    public SortableField(MetaField meta, String name, Class<?> type) {  
        super();  
        this.meta = meta;  
        this.name = name;  
        this.type = type;  
    }  
      
    public MetaField getMeta() {  
        return meta;  
    }  
    public void setMeta(MetaField meta) {  
        this.meta = meta;  
    }  
    public Field getField() {  
        return field;  
    }  
    public void setField(Field field) {  
        this.field = field;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public Class<?> getType() {  
        return type;  
    }  
  
    public void setType(Class<?> type) {  
        this.type = type;  
    }  
    
}

    

使用并获取注解属性值:


import java.util.List;

public class MetaFieldTest {
	public static void main(String[] args) {
		Student student = new Student();
		List<SortableField > sortableFields = student.init();
		if(sortableFields != null && sortableFields.size() > 0) {
			for(SortableField sortableField : sortableFields) {
				System.out.println("是否可读 : " + sortableField.getMeta().readonly());
				System.out.println("是否存储 : " + sortableField.getMeta().store());
				System.out.println("名称 : " + sortableField.getMeta().name());
				System.out.println("是否可搜索 : " + sortableField.getMeta().searchable());
				System.out.println();
			}
		}
	}
}

   2、标识哪些接口是公共接口

     业务需求,我们使用权限:用户《==》角色《==》资源时会出现白名单这个名词,这时候,我们可以使用配置文件或者一个list存储到内存中,但是这样还是不利于我们维护,我们可以使用注解来实现,可读性也比较强。只需要在需要访问的接口上面标识这个接口是一个公共接口即可

    自定义注解:

   

/**
 * 定义公共接口使用的
 */
@Target(METHOD)
@Retention(RUNTIME)
@Documented
public @interface PublicMethod {
    
    boolean value() default false;
    
}

    项目启动并初始化容器后获取通过注解获取对应的接口信息:

    


@Component
public class MyListenerProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
        if (methods != null) {
            for (Method method : methods) {
                //这是公共接口相关注解的扫描
                PublicMethod publicMethod = AnnotationUtils.findAnnotation(method, PublicMethod.class);
                RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
                if (publicMethod != null) {
                    System.out.println(method.getName());
                    boolean result = publicMethod.value();
                    if(result){
                        String [] values =  requestMapping.value();
                        if(values != null && values.length > 0){
                            AnnotationConstant.requestMappingList.add(values[0]);
                        }
                    }
                }

            }
        }
        return bean;
    }
}

  我已经在项目自启动时将相关的url地址放到了一个常量中,使用的时候我只需要遍历这个集合就可以判断哪些url是公共的。

  使用:

 

//公共接口
        List<String > requestMappingList = AnnotationConstant.requestMappingList;
        if(requestMappingList.contains(requestURI)){
            obj =  jp.proceed();//用户拥有该方法权限时执行方法里面的内容
        }

 

  3、代替enum使得代码更简洁,更易于维护

   记得我们项目中大量使用了枚举,当时感觉还是可以满足我们的基本需求的,但是后来发现,枚举写的越来越多,虽然可以解决我们的需求,但是为了减少以后的维护成本,只能寻找其他方式获取相关信息。就拿我在项目中的例子来说明,项目中使用到了大量的反射,因为我们的项目是基于模型开发的,所以有这些关系:模型名称----》表名-----》entity-----》mapper----》service,我想要的结果就是在知道其中一个的值,我可以得到它们中任意一个值,我们也知道,如果我只知道模型名称,想要获取对应实体类中的某个属性的值时,我需要知道实体类的全路径(包名+类名),这样再通过反射就可以调他们对应get方法就可以得到属性的名称和返回类型,返回值相关信息。来例子:

    自定义注解:

    


/**
 * 模型拓展注解
 */
@Target({ElementType.TYPE})
@Retention(RUNTIME)
@Documented
public @interface ModelExpansion {

    String modelName() default "";//模型名称
    String tableName() default "";//数据库表名称
    String entityName() default "";//实体类名称
    String serviceName() default "";//service名称
    String mapperName() default "";//mapper名称

}

     使用:


@TableName("student_student")
@Data
@Component
@ModelExpansion(modelName = "student.student" , entityName = "com.yuqiinfo.student.entity.Student" , tableName = "student_student" , serviceName = "com.yuqiinfo.student.service.StudentService",mapperName = "com.yuqiinfo.student.mapper.StudentMapper")
public class Student extends BaseModelDto implements Serializable {


    /**
     * 姓名
     */
    private CommonTypeString name = new CommonTypeString("姓名");
    /**
     * 性别
     */
    private CommonTypeString sex=new CommonTypeString("性别");
    /**
     * 状态
     */

    private CommonTypeString state=new CommonTypeString("状态");
    /**
     * Active
     */

    private CommonTypeBoolean active=new CommonTypeBoolean("active");
    /**
     * 学校
     */

    @TableField("school_id")
    private CommonTypeM2O school_id=new CommonTypeM2O("学校","student.school");
    /**
     * 年龄
     */

    

    public Student(){
        
    }
}

主要是实体类上面的自定义注解部分。   

在项目启动时,将注解进行扫描,得到保存到内存中:

 


@Component
public class MyListenerProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //模型拓展注解扫描
        ModelExpansion modelExpansion = AnnotationUtils.findAnnotation(bean.getClass(), ModelExpansion.class);
        if(modelExpansion != null){
            AnnotationConstant.modelExpansionList.add(modelExpansion);
        }
        return bean;
    }
}

    

   附加一个使用的工具类:


public class ModelExpansionUtil {

    /**
     * 获取所有的模型名称
     * @param modelExpansionList
     * @return
     */
    public static List<String > getAllModelName(List<ModelExpansion> modelExpansionList){
        List<String > list = new ArrayList<>();
        if(modelExpansionList != null && modelExpansionList.size() > 0){
            for(ModelExpansion modelExpansion: modelExpansionList){
                list.add(modelExpansion.modelName());
            }
        }
        return list;
    }

    /**
     * 通过模型模型名称获取对应的注解对象
     */
    public static ModelExpansion getByModelName(String modelName , List<ModelExpansion> modelExpansionList){
        if(modelExpansionList != null && modelExpansionList.size() > 0){
            for(ModelExpansion modelExpansion: modelExpansionList){
                if(modelName.equals(modelExpansion.modelName())){
                    return modelExpansion;
                }
            }
        }
        return null;
    }

    /**
     * 通过表名称获取对用的注解对象
     */
    public static ModelExpansion getByTableName(String tableName , List<ModelExpansion> modelExpansionList){
        if(modelExpansionList != null && modelExpansionList.size() > 0){
            for(ModelExpansion modelExpansion: modelExpansionList){
                if(tableName.equals(modelExpansion.tableName())){
                    return modelExpansion;
                }
            }
        }
        return null;
    }

    /**
     * 通过Service称获取各个对象
     */
    public static ModelExpansion getByServiceName(String serviceName , List<ModelExpansion> modelExpansionList){
        if(modelExpansionList != null && modelExpansionList.size() > 0){
            for(ModelExpansion modelExpansion: modelExpansionList){
                if(serviceName.equals(modelExpansion.serviceName())){
                    return modelExpansion;
                }
            }
        }
        return null;
    }

    /**
     * 通过实体类名称获取各个对象
     */
    public static ModelExpansion getByEntityName(String entityName , List<ModelExpansion> modelExpansionList){
        if(modelExpansionList != null && modelExpansionList.size() > 0){
            for(ModelExpansion modelExpansion: modelExpansionList){
                if(entityName.equals(modelExpansion.entityName())){
                    return modelExpansion;
                }
            }
        }
        return null;
    }


}

  怎么获取:

public void load_views( List<List<Object>> views , Map<String , Object > options) {
        Integer uid=(Integer) context.get("uid");
        String serviceName = this.getClass().toString();
        serviceName = serviceName.substring(serviceName.indexOf(" ") + 1);
        String modelName = ModelExpansionUtil.getByServiceName(serviceName , 
        AnnotationConstant.modelExpansionList).modelName();
        
    }

 

四、总结

  因为部分代码涉及到公司项目源码问题,所以不易太多的暴漏太多源码,当然,如果有感兴趣的朋友可以添加评论共同讨论。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值