【Java】注解(Annotation)

Posted by 西维蜀黍 on 2025-06-26, Last Modified on 2025-07-22

In Java, an annotation is a form of metadata that provides information to the compiler or runtime environment but does not directly affect program execution.

Common Java Annotations

Annotation Purpose
@Override Indicates a method is overriding a method from a superclass
@Deprecated Marks a method/class as deprecated (not recommended for use)
@SuppressWarnings Tells the compiler to suppress specific warnings
@FunctionalInterface Marks an interface as a functional interface (used in lambda expressions)
@SafeVarargs Suppresses warnings for varargs when used with generics

@Resource

@Resource is part of Java’s standard (JSR-250), not Spring specifically — but Spring supports it fully for dependency injection.

@Resource is a Java EE annotation used to inject a dependency by name (not type by default).

import javax.annotation.Resource;

public class MyComponent {

    @Resource
    private MyService myService; // Injected by name
}

Key Characteristics of @Resource

Feature Description
Source Comes from JSR-250 (javax.annotation.Resource)
Injection type By name first, falls back to type if no match
Works on Fields and setter methods
Spring support Fully supported by Spring (like @Autowired)

Comparison with @Autowired

Feature @Resource @Autowired
Injection type By name, then by type fallback By type
Provided by Java standard (JSR-250) Spring Framework
Nullable by default No, throws exception if no match Yes, unless @Autowired(required = false)
Primary use case Integration with standard Java EE APIs Preferred in Spring-only projects

Example: Field Injection

@Component
public class OrderService {

    @Resource
    private PaymentProcessor paymentProcessor;

    // Spring will look for a bean named "paymentProcessor"
}

If there’s a bean named paymentProcessor, it gets injected. Otherwise, Spring looks for a bean of type PaymentProcessor. If multiple beans match and the name doesn’t resolve, you’ll get a NoUniqueBeanDefinitionException.

Tip: Use with Bean Naming

@Bean(name = "fastProcessor")
public PaymentProcessor fastPaymentProcessor() {
    return new FastPaymentProcessor();
}

// or
@Resource(name = "fastProcessor")
private PaymentProcessor processor;

This works well when injecting by specific bean name.

✅ When to Use @Resource**

  • When integrating with legacy or Java EE code
  • When you want to inject by bean name deliberately
  • In simple apps where you want clear, explicit injection behavior

But in modern Spring apps, it’s generally preferred to use:

  • @Autowired (for injection by type)
  • @Qualifier (to specify which bean when multiple candidates exist)
  • @Constructor injection (best practice)

@value

What is @Value?

@Value is a Spring annotation used to inject values into fields, constructor arguments, or method parameters. These values can come from:

  • application.properties or application.yml
  • System environment variables
  • Computed Spring Expression Language (SpEL)
  • Other bean properties

Basic Usage

# application.properties
app.name=MyApp
app.timeout=30

@Component
public class MyService {

    @Value("${app.name}")
    private String appName;

    @Value("${app.timeout}")
    private int timeout;
}

Constructor Injection

@Component
public class ConfigService {

    private final String appName;

    public ConfigService(@Value("${app.name}") String appName) {
        this.appName = appName;
    }
}

Advanced Features

Use case Example Description
Default value @Value("${app.port:8080}") Use 8080 if app.port is not set
System variable @Value("${JAVA_HOME}") Injects value from system env
SpEL expression @Value("#{2 * 60}") Injects result of expression (120)
Access bean field @Value("#{myBean.property}") Injects a value from another bean
List injection @Value("#{’${app.list}’.split(’,’)}") Splits string into list/array

Lombok

  • @Data: Generates getters, setters, toString, equals, and hashCode.
  • @Builder: Enables the builder pattern for object creation.
  • @Slf4j: Adds a logger field (private static final Logger log = LoggerFactory.getLogger(…);).

@Data

@Data 是 Lombok 的复合注解,作用是:

  • 自动为所有字段生成:
  • getter / setter 方法
  • equals() 和 hashCode() 方法(基于所有字段)
  • toString() 方法
  • 自动生成一个 全参构造方法(如果没有 @Builder 或手动定义构造器)
  • 字段为 final 时不会生成 setter

适用于需要完整 POJO 功能的数据类。

@Builder

@Builder 为类提供 建造者模式(Builder Pattern):

  • 自动生成一个静态内部类(Builder),允许链式调用设置字段。
  • 自动生成 build() 方法,返回构造好的对象。
  • 提供类型安全、可读性强的对象构造方式,适合字段多、参数可选性强的类。

适用于需要灵活构造对象的场景,避免多参数构造器出错。

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class User {
    private String name;
    private int age;
    private String email;
}

// usage
User user = User.builder()
    .name("Alice")
    .age(30)
    .email("alice@example.com")
    .build();

// 生成的代码等价于
public class User {
    private String name;
    private int age;

    private User(UserBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // 静态 builder() 方法
    public static UserBuilder builder() {
        return new UserBuilder();
    }

    // 静态内部类
    public static class UserBuilder {
        private String name;
        private int age;

        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }

    // Getter 需手动加或用 @Getter/@Data
    public String getName() { return name; }
    public int getAge() { return age; }
}

@SuperBuilder

@SuperBuilder 是 Lombok 提供的注解,用于支持继承类之间的 Builder 模式。它是 @Builder 的增强版本,专门用于处理 父类 + 子类 的构造场景。

为什么需要 @SuperBuilder?

Lombok 的 @Builder 不支持继承结构(父类字段不会自动被子类 Builder 继承),而 @SuperBuilder 解决了这个问题。

import lombok.Getter;
import lombok.experimental.SuperBuilder;

@Getter
@SuperBuilder
class Parent {
    private String parentField;
}

@Getter
@SuperBuilder
class Child extends Parent {
    private String childField;
}

// usage
Child child = Child.builder()
    .parentField("from parent")
    .childField("from child")
    .build();

Reference