Java 注解简介、原理、分类和常见使用场景【持续更新中】

java注解-最通俗易懂的讲解

简介

  1. 一种特殊的类, 相当于“标签”,用于注释说明,可修饰类、接口、方法、参数等。
  2. 注解本身不直接影响代码的操作,但可以被编译器或运行时环境用来生成额外的代码、文件等,或用来控制代码的行为。

实现原理

Java注解是通过反射(Reflection)API和注解处理器(Annotation Processors,在Java 6中引入)来实现的。注解处理器可以在编译时处理注解,生成新的源代码、文件或其他输出。而在运行时,可以通过反射API查询注解信息,并根据这些信息执行相应的逻辑。

自定义注解

注解的属性也叫做成员变量。注解只有成员变量没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
    String text() default "";
}

分类

元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
可以这样理解:元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。
元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

元注解中的RetentionPolicy、ElementType

@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
    String text() default "";
}

RetentionPolicy

RetentionPolicy 定义了注解的保留策略,即注解在何时有效。它有三个分类:

  1. SOURCE:注解只在源码中有效,在编译成.class文件时会被丢弃,不会被加载到JVM中。

    • 示例:虽然@Override注解实际上使用的是CLASS保留策略(因为它在编译时需要被检查),但如果你自定义一个只在源码中使用的注解,可以想象它是为了标记某些代码段或提供IDE中的特定功能,如@Todo("未完成的功能")
  2. CLASS:注解在编译时会被保留在.class文件中,但JVM加载类时不会保留注解,默认情况下注解会使用此策略。

    • 示例@Deprecated注解,它用于标记某个类或方法已过时,这个信息在编译时会被编译器读取,并可能生成警告,但不会被加载到JVM运行时环境中。
  3. RUNTIME:注解在运行时可通过反射API被读取。

    • 示例@Autowired(在Spring框架中),这个注解在运行时由Spring容器通过反射机制查找并注入依赖。

ElementType

ElementType 定义了注解可以应用的Java元素类型。它包含以下分类:

  1. TYPE:类、接口(包括注解类型)或枚举声明。

    • 示例@Component注解应用于类上,表示该类是一个Spring组件。
  2. FIELD:字段声明(包括枚举常量)。

    • 示例@Inject注解应用于字段上,表示该字段需要被注入依赖。
  3. METHOD:方法声明。

    • 示例@Test注解(JUnit框架中),用于标记某个方法是测试方法。
  4. PARAMETER:参数声明。

    • 示例@RequestParam注解(Spring MVC中),用于将HTTP请求参数绑定到控制器方法的参数上。
  5. CONSTRUCTOR:构造器声明。

    • 示例:虽然Java标准库中可能没有直接应用于构造器的注解示例,但在自定义注解时,你可以将其应用于构造器上,以表示某种特殊的构造器用途。
  6. LOCAL_VARIABLE:局部变量声明。

    • 示例@Var(假设的注解),用于标记局部变量具有某种特殊含义或需要特殊处理(注意:在Java标准库中,没有直接应用于局部变量的注解,这主要是出于实用性和性能考虑)。
  7. ANNOTATION_TYPE:注解类型声明。

    • 示例@Retention注解自身就是一个注解,它被应用于其他注解上以指定其保留策略。
  8. PACKAGE:包声明。

    • 示例@java.lang.annotation.PackageMarker(注意:Java标准库中并没有直接使用@PackageMarker注解,但你可以自定义一个注解来标记包,并通过反射或注解处理器在编译时读取这些信息)。
  9. TYPE_PARAMETER(Java 8 引入):类型参数声明。

    • 示例@javax.inject.Qualifier(虽然它本身不直接应用于类型参数,但可以用于创建特定于类型参数的注解,以便在依赖注入时区分不同类型的实例)。
  10. TYPE_USE(Java 8 引入):使用类型的任何声明。

    • 示例@java.lang.SuppressWarnings注解可以用于类、方法、字段、参数等几乎所有使用类型的地方,以抑制编译器警告。

使用

@Description可修饰类的成员变量

	/**
     * 医院编码
     */
    @Description(text = "医院编码")
    @Column(name="hospital_code")
    private String hospitalCode;

注解内容的提取

在需要获取注解内容的时候,可通过反射提取。

成员变量注解

public static <T, U> List<ChangeContentDto> generateChangeContent(T source, U target) {
	List<Field> sourceFields = Arrays.asList(source.getClass().getDeclaredFields());
	for (Field sourceField : sourceFields) {
	   Description descAnno = sourceField.getAnnotation(Description.class);
	   Column columnAnno = sourceField.getAnnotation(Column.class);
	   if (descAnno == null || columnAnno == null) {
	       continue;
	   }
	   if (descAnno.text().contains("授权范围")) {
	   	//todo
	   }
	}
}

类注解

//首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

//获取 Annotation 对象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
public Annotation[] getAnnotations() {}

@TestAnnotation()
public class Test {
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
    }
}


常见注解示例

在Spring框架中,这些注解扮演着重要的角色,它们用于开启或启用特定的Spring特性。下面是对您提到的每个注解的详细解释:

@EnableAspectJAutoProxy(proxyTargetClass = true)

  • 作用:这个注解用于启用Spring对AspectJ的自动代理支持。AspectJ是一个面向切面的编程(AOP)框架,它允许开发者定义横切关注点(如日志、事务管理等)作为“切面”,并将这些切面织入到业务逻辑中。
  • proxyTargetClass = true:这个属性指定了代理的创建方式。当设置为true时,Spring会使用CGLIB库来生成基于类的代理(class-based proxies),而不是默认的基于接口的代理(interface-based proxies)。这意味着,即使目标类没有实现任何接口,也可以被代理。这对于那些需要代理非接口类的场景非常有用。

@EnableTransactionManagement(proxyTargetClass = true)

  • 作用:这个注解用于启用Spring的声明式事务管理。声明式事务管理允许开发者通过注解(如@Transactional)来声明事务的边界和属性,而不是通过编程方式(如通过编程式事务API)来管理事务。
  • proxyTargetClass = true:与@EnableAspectJAutoProxy类似,这个属性也指定了代理的创建方式。设置为true时,Spring将使用CGLIB来创建代理,这对于需要代理非接口类的事务管理场景非常有用。

@EnableConfigurationProperties

  • 作用:这个注解用于启用对@ConfigurationProperties注解的支持。@ConfigurationProperties注解允许将配置文件(如application.propertiesapplication.yml)中的属性绑定到JavaBean上。这样做的好处是可以减少配置项的硬编码,使得配置更加灵活和易于管理。
  • 使用方式:通常,这个注解会结合@Configuration注解的类一起使用,并且会指定一个或多个@ConfigurationProperties注解的类的prefix属性,以便Spring能够找到并绑定对应的配置项。

@Autowired 和 @Resource 区别

来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;
依赖查找的顺序不同:@Autowired 根据类型查询,多个实现类时需结合@Qualifier(“person1”) 使用; 而 @Resource 先根据名称再根据类型查询,多个时需要指定name@Resource(name = “renlei”) ;
支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;
依赖注入的用法支持不同
@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;
编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。

@Autowired 使用示例

构造方法注入
@Component
public class SomeBean {
    private final AnotherBean anotherBean;

    @Autowired
    public SomeBean(AnotherBean anotherBean) {
        this.anotherBean = anotherBean;
    }

    // ...
}
属性注入(字段注入)
@Component
public class SomeBean {
    @Autowired
    private AnotherBean anotherBean;

    // ...
}
Setter 方法注入
@Component
public class SomeBean {
    private AnotherBean anotherBean;

    @Autowired
    public void setAnotherBean(AnotherBean anotherBean) {
        this.anotherBean = anotherBean;
    }

    // ...
}
@Autowired与@Qualifier结合使用

当Spring容器中存在多个相同类型的Bean时,我们可以使用@Autowired注解与@Qualifier注解结合来指定注入哪一个Bean。

// Person类,一个Bean  
@Component("person1")  
public class Person {  
    // ...  
}  
  
// 另一个Person类实例,同一个接口的不同实现  
@Component("person2")  
public class PersonImpl2 implements Person {  
    // ...  
}  
  
// House类,需要指定注入哪一个Person实例  
@Component  
public class House {  
    // 使用@Autowired和@Qualifier注解结合来指定注入哪一个Person实例  
    @Autowired  
    @Qualifier("person1")  
    private Person person;  
  
    // ...  
}

@Resource 使用示例

属性注入(字段注入)
@Component
public class SomeBean {
    @Resource
    private AnotherBean anotherBean;

    // ...
}

在这个例子中,@Resource 会首先尝试按照 anotherBean 字段的名称来查找 Bean,如果没有找到,则会尝试按照 AnotherBean 的类型来查找。

Setter 方法注入
@Component
public class SomeBean {
    private AnotherBean anotherBean;

    @Resource
    public void setAnotherBean(AnotherBean anotherBean) {
        this.anotherBean = anotherBean;
    }

    // ...
}
基于名称的注入(@Resource)
// Person类,一个Bean  
@Component(value = "renlei")  
public class Person {  
    // ...  
}  
  
// House类,需要注入Person类的实例  
@Component  
public class House {  
    // 使用@Resource注解通过名称注入Person实例  
    // 注意:这里的name属性值与Person类的@Component注解的value值相对应  
    @Resource(name = "renlei")  
    private Person person;  
  
    // ...  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值