栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

自定义注解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

自定义注解

作用:简单来说,相当于java里面的特殊标志,这个标志里含有一些信息,在运行(类加载、编译)的时候可以读取到这个标志及包含的信息,然后根据这些信息值去做相应的逻辑处理

元注解:

 元注解

什么是源注解:标记注解的注解

@Documented - 表示该注解会被javadoc文档记录

@Target - 表示设置当前注解的标记位置
可选值:
ElementType.ANNOTATION_TYPE - 表示当前注解可以标记其他的注解
ElementType.CONSTRUCTOR - 表示当前注解可以标记构造方法
ElementType.FIELD - 表示当前注解可以标记属性(全局变量)
ElementType.LOCAL_VARIABLE - 表示当前注解可以标记局部变量
ElementType.METHOD - 表示当前注解可以标记方法
ElementType.PACKAGE - 表示当前注解可以标记包
ElementType.PARAMETER - 表示当前注解可以标记方法形式参数
ElementType.TYPE - 表示当前注解可以标记类、接口、枚举类

@Retention - 表示当前注解的有效范围
可选值:
RetentionPolicy.SOURCE - 表示当前注解只能存在于源码,一旦编译就会丢失
RetentionPolicy.CLASS - 表示当前注解只能存在于源码和字节码文件,一旦运行就会丢失
RetentionPolicy.RUNTIME - 表示当前注解能存在于源码、字节码文件以及运行时状态

注意:如果需要通过反射操作注解,则注解必须是RUNTIME类型的

@Documented - 注解包含在javadoc中
  
@Inherited - 子类可以继承父类中的该注解
  

三、自定义注解中的方法

方法的声明格式:


方法的返回值类型 方法名() [default 默认值]

注意: 
    1、注解中的方法没有形参列表和方法体
    2、标记注解时,必须制定所有方法的返回值
    3、如果某个方法设置了default默认值,则标记注解时可以忽略该方法返回值的设置,如果设置了就会覆盖默认值
    4、如果有个注解的方法名为value,而且在使用注解时只设置这一个方法,则可以忽略不写方法名
    5、方法的返回值可以是一个数组,如果只给一个值,则无需大括号,如果需要给多个值,就需要大括号

通过反射机制获取到自定义注解的元素数据,获取注解信息:

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
    public String name() default "曾国藩";

    public int age() default 32;
}

@MyAnnotation(name = "胡雪岩", age = 31)
class Test {
    @MyAnnotation(name = "左宗棠", age = 30)
    public static void getNameAge() {
    }

    public static void main(String[] args) {
        Class test = Test.class;  // 获得我们需要的解析类

        // 解析的第一种方式
        //先判断类上面是否有MyAnnotation注解
        boolean present = test.isAnnotationPresent(MyAnnotation.class);
        if (present) { // 如果存在
            // 获得注解
            MyAnnotation annotation = test.getAnnotation(MyAnnotation.class);
            System.out.println("name: " + annotation.name() + "tage: " + annotation.age());
        }
        System.out.println("=================================");

        //解析的第二种方式
        // 解析方法中的注解,先解析类中所有方法
        Method[] methods = test.getMethods();
        for (Method m : methods) {
            // 判断方法中是否有MyAnnotation注解
            boolean annotationPresent = m.isAnnotationPresent(MyAnnotation.class);
            if (annotationPresent) {
                MyAnnotation annotation = m.getAnnotation(MyAnnotation.class);
                System.out.println("name: " + annotation.name() + "tage: " + annotation.age());
                
                System.out.println(m.getAnnotations());
                System.out.println(m.getDeclaredAnnotations());
            }
        }
    }
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyField {
    String description();

    int length();
}

class MyFieldTest {

    //使用我们的自定义注解
    @MyField(description = "用户名", length = 12)
    private String username;

    public static void main(String[] args) {
        // 获取类模板
        Class c = MyFieldTest.class;

        // 获取所有字段
        for (Field f : c.getDeclaredFields()) {
            // 判断这个字段是否有MyField注解
            if (f.isAnnotationPresent(MyField.class)) {
                MyField annotation = f.getAnnotation(MyField.class);
                System.out.println("字段:[" + f.getName() + "], 描述:[" + annotation.description() + "], 长度:[" + annotation.length() + "]");
            }
        }
    }
}

  

 

 Field f = c1.getDeclaredFileld("name"); // 获得的值:

 

应用场景:自定义注解+AOP 实现日志打印

先导入切面需要的依赖包


      org.springframework.boot
     spring-boot-starter-aop

自定义一个注解@MyLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    
}

定义一个切面类,见如下代码注释理解:

@Aspect // 1.表明这是一个切面类
@Component
public class MyLogAspect {

    // 2. PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
    // 切面最主要的就是切点,所有的故事都围绕切点发生
    // logPointCut()代表切点名称
    @Pointcut("@annotation(me.zebin.demo.annotationdemo.aoplog.MyLog)")
    public void logPointCut(){};

    // 3. 环绕通知
    @Around("logPointCut()")
    public void logAround(ProceedingJoinPoint joinPoint){
        // 获取方法名称
        String methodName = joinPoint.getSignature().getName();
        // 获取入参
        Object[] param = joinPoint.getArgs();

        StringBuilder sb = new StringBuilder();
        for(Object o : param){
            sb.append(o + "; ");
        }
        System.out.println("进入[" + methodName + "]方法,参数为:" + sb.toString());

        // 继续执行方法
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(methodName + "方法执行结束");

    }
}

------------------------------------------------------------------------------------------------------------------------

使用场景实例:

package com.ssm.test.anno;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// jdbc:sqlserver://localhost:1433;DataBaseName=sqlserverdb

@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface JDBCConfig {

    String ip();

    int port() default 1433;

    String database();

    String encoding();

    String loginName();

    String password();
    
    String driverName();
}
  • 接下来直接在数据交互层通过注解的形式注入使用就OK了
  •     @JDBCConfig(database = "sqlserverdb", driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver", encoding = "UTF-8", ip = "192.168.1.191", loginName = "sa", password = "123")
        public Connection getConnection() {
            Connection conn = null;
            try {
                JDBCConfig jconfg=DBManager.class.getAnnotation(JDBCConfig.class);
                String url = jconfg.ip();
                String user = jconfg.loginName();
                String password = jconfg.password();
                String driver = jconfg.driverName();
                Class.forName(driver);
                conn = DriverManager.getConnection(url, user, password);
            } catch (ClassNotFoundException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            } catch (SQLException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return conn;
        }
    }

    另外,注解不仅可以用于配置信息的注入,还可以在注解中追加对字段的验证哦~这里举个验证的例子

  • 验证注解@NotEmpty源码

  • package org.hibernate.validator.constraints;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import javax.validation.ReportAsSingleViolation;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;
    
    import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
    import static java.lang.annotation.ElementType.CONSTRUCTOR;
    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.ElementType.PARAMETER;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    @Documented
    @Constraint(validatedBy = { })
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @ReportAsSingleViolation
    @NotNull
    @Size(min = 1)
    public @interface NotEmpty {
        String message() default "{org.hibernate.validator.constraints.NotEmpty.message}";
    
        Class[] groups() default { };
    
        Class[] payload() default { };
    
        
        @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
        @Retention(RUNTIME)
        @Documented
        public @interface List {
            NotEmpty[] value();
        }
    }

    使用

    @NotEmpty(message = "密码不能为空", groups = {AccountGroup.Add.class})
        @Size(min = 6, max = 20, message = "密码长度必须在{min}和{max}之间", groups = {AccountGroup.Add.class})
        private String password;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/884960.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号