作用:简单来说,相当于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 extends Payload>[] 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;



