¥
立即购买

Java注解使用示例生成器

8 浏览
1 试用
0 购买
Dec 6, 2025更新

本提示词专为Java开发者设计,能够根据指定的注解名称生成完整的使用示例。它提供注解的功能说明、使用场景分析、代码实现示例以及最佳实践建议,帮助开发者快速掌握各种Java注解的正确使用方法。通过结构化的输出格式和详细的技术说明,确保生成的代码示例符合Java开发规范,具有实际应用价值。

注解功能概述

@ConfigurationProperties 用于将外部化配置(application.properties / application.yml / 环境变量 / 命令行参数)按指定前缀类型安全地绑定到Java对象。其核心目标是:

  • 将散落的配置项聚合为结构化的POJO,以类型安全方式访问
  • 支持嵌套对象、集合、Map、Duration、DataSize、URI等常见类型的自动转换
  • 支持JSR-303校验(需配合@Validated和校验注解)
  • 可选择忽略未知或无效配置项,提升配置的可维护性

核心参数说明

参数名 类型 默认值 描述
value String "" 与prefix互为别名;用于声明绑定前缀(不建议与prefix同时使用)
prefix String "" 与value互为别名;用于声明绑定前缀,如"app"、"app.datasource"
ignoreUnknownFields boolean true 是否忽略未知配置项;false时遇到未映射字段会抛出异常,常用于严格校验配置完整性
ignoreInvalidFields boolean false 是否忽略无效值(类型转换失败等);一般保持默认false以尽早暴露问题

说明:value与prefix是别名,推荐使用prefix以提升可读性。

使用场景分析

  • 聚合应用级配置:将多个app.*配置统一绑定到AppProperties,避免硬编码和散乱读取,提升可读性与可测试性。
  • 组件/第三方类配置:为第三方配置类(如连接池、客户端配置)进行方法级绑定,实现外部化配置与类型安全。
  • 配置校验:结合@Validated与JSR-303注解(如@NotBlank),在启动时对必需项进行校验,防止运行期错误。
  • 绑定策略控制:通过ignoreUnknownFields与ignoreInvalidFields控制配置严格程度;生产环境通常保持默认严格绑定(ignoreInvalidFields=false),必要时开启严格未知项校验(ignoreUnknownFields=false)。

完整代码示例

以下示例基于 Spring Boot 3.x 与 JDK 17,演示通过@ConfigurationPropertiesScan扫描绑定的方式。包含:

  • 类型安全的配置类AppProperties(支持嵌套对象、集合、Map、Duration、URI)
  • 启动类DemoApplication(打印配置)
  • 示例配置文件application.properties
  1. src/main/java/com/example/demo/config/AppProperties.java
package com.example.demo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotBlank;

import java.net.URI;
import java.time.Duration;
import java.util.List;
import java.util.Map;

import org.springframework.boot.convert.DurationUnit;
import java.time.temporal.ChronoUnit;

@Validated
@ConfigurationProperties(prefix = "app", ignoreUnknownFields = true)
public record AppProperties(
        @NotBlank String name,
        // 未指定单位时可直接使用30s/1m等,也可通过@DurationUnit指定默认单位
        @DurationUnit(ChronoUnit.SECONDS) Duration timeout,
        List<URI> servers,
        Map<String, Boolean> features,
        Security security
) {
    // 防御式处理:避免集合/嵌套对象为 null
    public AppProperties {
        servers = servers == null ? List.of() : List.copyOf(servers);
        features = features == null ? Map.of() : Map.copyOf(features);
        security = security == null ? new Security("", "", List.of()) : security;
    }

    public record Security(
            @NotBlank String username,
            @NotBlank String password,
            List<String> roles
    ) {
        public Security {
            roles = roles == null ? List.of() : List.copyOf(roles);
        }
    }
}
  1. src/main/java/com/example/demo/DemoApplication.java
package com.example.demo;

import com.example.demo.config.AppProperties;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@ConfigurationPropertiesScan // 扫描@ConfigurationProperties类型并注册为Bean
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    CommandLineRunner showConfig(AppProperties props) {
        return args -> {
            System.out.println("App name        : " + props.name());
            System.out.println("Timeout         : " + props.timeout());
            System.out.println("Servers         : " + props.servers());
            System.out.println("Features        : " + props.features());
            System.out.println("Security user   : " + props.security().username());
            System.out.println("Security roles  : " + props.security().roles());
        };
    }
}
  1. src/main/resources/application.properties
# 绑定前缀为 app 的配置
app.name=Demo Service
app.timeout=30s

# 列表与Map绑定
app.servers[0]=http://localhost:8080
app.servers[1]=https://api.example.com
app.features.cache=true
app.features.metrics=true

# 嵌套对象绑定
app.security.username=admin
app.security.password=secret
app.security.roles[0]=USER
app.security.roles[1]=ADMIN

运行后,应用启动时会进行属性绑定与校验,并在控制台输出绑定结果。

技术要点说明

  • Bean注册方式:
    • 推荐使用@ConfigurationPropertiesScan进行扫描(如上示例)。
    • 或使用@EnableConfigurationProperties(AppProperties.class)显式注册。
    • 不建议给@ConfigurationProperties类加@Component,避免与扫描/注册方式混用。
  • 校验依赖:若使用@Validated和JSR-303注解,请确保引入spring-boot-starter-validation。
  • 绑定规则:
    • 支持“宽松绑定”(kebab-case、snake_case、camelCase均可对应字段名)。
    • 常见类型自动转换(Duration、DataSize、URI、List、Map 等);Duration可使用30s、1m、2h等格式,或通过@DurationUnit指定默认单位。
  • 严格性控制:
    • ignoreUnknownFields=false可在出现未映射配置项时快速失败,适用于严格配置管控。
    • ignoreInvalidFields一般保持默认false,以在类型转换失败时尽早暴露问题。
  • 结构设计:
    • 将配置按领域划分并使用稳定的prefix,减少变更影响面。
    • 嵌套对象使用独立类型,便于分组与测试。
    • 对集合/Map进行不可变封装(如List.copyOf/Map.copyOf)提高健壮性。
  • 版本与语言:
    • 示例基于 Spring Boot 3.x(需JDK 17+);使用record可获得更简洁的不可变配置对象。
  • 测试建议:
    • 使用@SpringBootTest或ApplicationContextRunner校验绑定与校验行为,确保关键配置必填项在启动时生效。

注解功能概述

@Entity 用于将普通的Java类声明为JPA实体,使其受持久化上下文(EntityManager)管理。被@Entity标注的类:

  • 具备持久化能力(可进行增删改查、级联、缓存、延迟加载、脏检查等)
  • 必须定义主键(@Id 或复合主键)
  • 在JPQL/Criteria中通过“实体名”进行引用(默认为类名简单名,或通过参数name自定义)
  • 与数据库表的物理映射通常结合 @Table、@Column、@OneToMany 等注解完成

核心参数说明

参数名 类型 默认值 描述
name String 类名的简单名称 实体名,用于JPQL/EntityGraph/Metamodel等处的逻辑命名。未显式指定时等同于类的简单类名;不影响物理表名(物理表名由@Table或默认命名策略决定)。

使用场景分析

  • 领域模型持久化:将领域对象建模为JPA实体,以获得ORM映射、关联关系、生命周期回调、缓存与乐观锁等能力。
  • 自定义实体名避免冲突:当类名与数据库保留字冲突,或跨包存在同名类时,通过 @Entity(name="Alias") 为JPQL/EntityGraph提供唯一实体名。
  • JPQL查询与实体图:使用实体名进行 JPQL 查询(如 SELECT c FROM Customer c),并结合 @NamedEntityGraph 指定抓取策略,控制性能与N+1问题。
  • 进阶特性整合:与 @MappedSuperclass、@Version(乐观锁)、@Cacheable(二级缓存)、@Converter(值对象映射)、@Embedded(值对象内联)等配合,构建健壮的实体模型。

完整代码示例

说明:

  • 采用 Jakarta Persistence (jakarta.persistence.*) 命名空间
  • 使用内存数据库 H2,Java SE 环境下通过 persistence.xml 启动
  • 展示 @Entity 的 name 参数、JPQL 查询、实体图加载、乐观锁、值对象与转换器、双向关联、生命周期回调等

文件:src/main/java/com/example/jpa/domain/BaseEntity.java

package com.example.jpa.domain;

import jakarta.persistence.*;
import java.time.Instant;

@MappedSuperclass
public abstract class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version
    private long version;

    @Column(name = "created_at", nullable = false, updatable = false)
    private Instant createdAt;

    @Column(name = "updated_at", nullable = false)
    private Instant updatedAt;

    @PrePersist
    protected void onCreate() {
        Instant now = Instant.now();
        this.createdAt = now;
        this.updatedAt = now;
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = Instant.now();
    }

    public Long getId() {
        return id;
    }

    public long getVersion() {
        return version;
    }

    public Instant getCreatedAt() {
        return createdAt;
    }

    public Instant getUpdatedAt() {
        return updatedAt;
    }
}

文件:src/main/java/com/example/jpa/domain/Address.java

package com.example.jpa.domain;

import jakarta.persistence.Embeddable;

@Embeddable
public class Address {
    private String street;
    private String city;
    private String zipCode;
    private String country;

    public Address() {}

    public Address(String street, String city, String zipCode, String country) {
        this.street = street;
        this.city = city;
        this.zipCode = zipCode;
        this.country = country;
    }

    public String getStreet() { return street; }
    public String getCity() { return city; }
    public String getZipCode() { return zipCode; }
    public String getCountry() { return country; }

    public void setStreet(String street) { this.street = street; }
    public void setCity(String city) { this.city = city; }
    public void setZipCode(String zipCode) { this.zipCode = zipCode; }
    public void setCountry(String country) { this.country = country; }
}

文件:src/main/java/com/example/jpa/domain/Money.java

package com.example.jpa.domain;

import java.math.BigDecimal;
import java.util.Currency;
import java.util.Objects;

public class Money {
    private final BigDecimal amount;
    private final Currency currency;

    public Money(BigDecimal amount, Currency currency) {
        if (amount == null || currency == null) {
            throw new IllegalArgumentException("amount and currency must not be null");
        }
        this.amount = amount;
        this.currency = currency;
    }

    public static Money of(String currencyCode, String amount) {
        return new Money(new BigDecimal(amount), Currency.getInstance(currencyCode));
    }

    public BigDecimal getAmount() { return amount; }
    public Currency getCurrency() { return currency; }

    @Override
    public String toString() {
        return currency.getCurrencyCode() + " " + amount.toPlainString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Money)) return false;
        Money money = (Money) o;
        return amount.compareTo(money.amount) == 0 &&
               currency.equals(money.currency);
    }

    @Override
    public int hashCode() {
        return Objects.hash(amount.stripTrailingZeros(), currency);
    }
}

文件:src/main/java/com/example/jpa/domain/MoneyAttributeConverter.java

package com.example.jpa.domain;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

import java.math.BigDecimal;
import java.util.Currency;

@Converter(autoApply = false)
public class MoneyAttributeConverter implements AttributeConverter<Money, String> {
    // 存储格式:<CURRENCY_CODE>:<AMOUNT> 例如 "USD:123.45"
    @Override
    public String convertToDatabaseColumn(Money attribute) {
        if (attribute == null) return null;
        return attribute.getCurrency().getCurrencyCode() + ":" + attribute.getAmount().toPlainString();
        // 注意:实际生产可考虑两列存储(币种/数值)或DECIMAL+外键,便于查询与校验
    }

    @Override
    public Money convertToEntityAttribute(String dbData) {
        if (dbData == null || dbData.isEmpty()) return null;
        String[] parts = dbData.split(":");
        if (parts.length != 2) throw new IllegalArgumentException("Invalid Money format: " + dbData);
        Currency currency = Currency.getInstance(parts[0]);
        BigDecimal amount = new BigDecimal(parts[1]);
        return new Money(amount, currency);
    }
}

文件:src/main/java/com/example/jpa/domain/OrderStatus.java

package com.example.jpa.domain;

public enum OrderStatus {
    NEW, PAID, SHIPPED, CANCELLED
}

文件:src/main/java/com/example/jpa/domain/Customer.java

package com.example.jpa.domain;

import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Entity(name = "Customer") // 自定义实体名(用于JPQL),等同于默认简单类名,这里显式声明便于演示
@Table(
    name = "customers",
    uniqueConstraints = {
        @UniqueConstraint(name = "uk_customer_email", columnNames = "email")
    }
)
@NamedQuery(
    name = "Customer.findByEmail",
    query = "SELECT c FROM Customer c WHERE c.email = :email"
)
@NamedEntityGraph(
    name = "Customer.withOrders",
    attributeNodes = {
        @NamedAttributeNode("orders")
    }
)
@Cacheable(true)
public class Customer extends BaseEntity {

    @Column(nullable = false, length = 100)
    private String name;

    @Column(nullable = false, length = 120, unique = true)
    private String email;

    @Embedded
    private Address address;

    @OneToMany(
        mappedBy = "customer",
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        fetch = FetchType.LAZY
    )
    private List<PurchaseOrder> orders = new ArrayList<>();

    public Customer() {}

    public Customer(String name, String email, Address address) {
        this.name = name;
        this.email = email;
        this.address = address;
    }

    // 双向关系维护
    public void addOrder(PurchaseOrder order) {
        order.setCustomer(this);
        this.orders.add(order);
    }

    public void removeOrder(PurchaseOrder order) {
        order.setCustomer(null);
        this.orders.remove(order);
    }

    public String getName() { return name; }
    public String getEmail() { return email; }
    public Address getAddress() { return address; }
    public List<PurchaseOrder> getOrders() { return orders; }

    public void setName(String name) { this.name = name; }
    public void setEmail(String email) { this.email = email; }
    public void setAddress(Address address) { this.address = address; }

    // 以业务唯一键(email)实现 equals/hashCode,避免使用瞬时id
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Customer)) return false;
        Customer other = (Customer) o;
        return Objects.equals(email, other.email);
    }

    @Override
    public int hashCode() {
        return Objects.hash(email);
    }

    @Override
    public String toString() {
        return "Customer{name='" + name + "', email='" + email + "'}";
    }
}

文件:src/main/java/com/example/jpa/domain/PurchaseOrder.java

package com.example.jpa.domain;

import jakarta.persistence.*;
import java.util.Objects;

@Entity(name = "PurchaseOrder")
@Table(name = "purchase_orders")
public class PurchaseOrder extends BaseEntity {

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(
        name = "customer_id",
        nullable = false,
        foreignKey = @ForeignKey(name = "fk_order_customer")
    )
    private Customer customer;

    @Column(name = "order_number", nullable = false, unique = true, length = 64)
    private String orderNumber;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 16)
    private OrderStatus status = OrderStatus.NEW;

    @Convert(converter = MoneyAttributeConverter.class)
    @Column(name = "total_amount", nullable = false, length = 64)
    private Money totalAmount;

    public PurchaseOrder() {}

    public PurchaseOrder(String orderNumber, OrderStatus status, Money totalAmount) {
        this.orderNumber = orderNumber;
        this.status = status;
        this.totalAmount = totalAmount;
    }

    public Customer getCustomer() { return customer; }
    public String getOrderNumber() { return orderNumber; }
    public OrderStatus getStatus() { return status; }
    public Money getTotalAmount() { return totalAmount; }

    public void setCustomer(Customer customer) { this.customer = customer; }
    public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
    public void setStatus(OrderStatus status) { this.status = status; }
    public void setTotalAmount(Money totalAmount) { this.totalAmount = totalAmount; }

    // 以业务唯一键(orderNumber)实现 equals/hashCode
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PurchaseOrder)) return false;
        PurchaseOrder that = (PurchaseOrder) o;
        return Objects.equals(orderNumber, that.orderNumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(orderNumber);
    }

    @Override
    public String toString() {
        return "PurchaseOrder{orderNumber='" + orderNumber + "', status=" + status +
               ", total=" + totalAmount + "}";
    }
}

文件:src/main/java/com/example/jpa/JpaApplication.java

package com.example.jpa;

import com.example.jpa.domain.*;
import jakarta.persistence.*;

public class JpaApplication {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("demoPU");
        EntityManager em = emf.createEntityManager();
        try {
            // 初始化数据
            em.getTransaction().begin();
            Customer c = new Customer(
                "Alice",
                "alice@example.com",
                new Address("1 Main St", "Metropolis", "10001", "US")
            );
            PurchaseOrder po = new PurchaseOrder(
                "PO-1001",
                OrderStatus.NEW,
                Money.of("USD", "199.99")
            );
            c.addOrder(po);
            em.persist(c); // 由于 cascade = ALL,订单将级联持久化
            em.getTransaction().commit();

            // 使用实体名进行JPQL查询,并结合实体图抓取orders
            EntityGraph<?> graph = em.getEntityGraph("Customer.withOrders");
            TypedQuery<Customer> q = em.createNamedQuery("Customer.findByEmail", Customer.class)
                .setParameter("email", "alice@example.com")
                .setHint("jakarta.persistence.loadgraph", graph);

            Customer found = q.getSingleResult();
            System.out.println("Found: " + found);
            System.out.println("Orders: " + found.getOrders());

        } finally {
            em.close();
            emf.close();
        }
    }
}

文件:src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="3.1"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_1.xsd">
    <persistence-unit name="demoPU" transaction-type="RESOURCE_LOCAL">
        <!-- 列出实体类,便于在Java SE环境下被发现 -->
        <class>com.example.jpa.domain.Customer</class>
        <class>com.example.jpa.domain.PurchaseOrder</class>

        <properties>
            <!-- JDBC配置(H2内存数据库) -->
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:demo;DB_CLOSE_DELAY=-1;MODE=LEGACY"/>
            <property name="jakarta.persistence.jdbc.user" value="sa"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>

            <!-- 自动建表 -->
            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>

            <!-- Hibernate常用调试参数 -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

运行说明:

  • 确保引入依赖(示例:Hibernate ORM 6.x、H2 数据库、Jakarta Persistence API)
  • 在 IDE 或命令行直接运行 JpaApplication.main()

示例Maven依赖参考(供集成时使用):

<!-- 关键依赖(版本根据项目实际选择) -->
<dependency>
  <groupId>org.hibernate.orm</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>6.5.0.Final</version>
</dependency>
<dependency>
  <groupId>jakarta.persistence</groupId>
  <artifactId>jakarta.persistence-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <version>2.2.224</version>
  <scope>runtime</scope>
</dependency>

技术要点说明

  • 实体名与表名的区别
    • @Entity(name="...") 定义JPQL/EntityGraph/元模型使用的“实体名”
    • @Table(name="...") 定义物理表名;两者互不影响
  • 主键与构造器
    • 实体必须有主键(@Id),且需要一个public/protected无参构造器
  • 类/字段修饰
    • 避免将实体类或其延迟加载的方法声明为final,以支持代理与延迟加载
    • 持久化字段不应为final(除非只读快照语义且由provider支持)
  • 关系维护
    • 双向关联需在两端维护一致性(示例中的 addOrder/removeOrder)
    • 合理设置 cascade 与 orphanRemoval,结合数据库外键约束的命名
  • 抓取策略与性能
    • 关联默认LAZY,按需使用 @NamedEntityGraph + loadgraph/hint 控制抓取,避免N+1
  • 乐观锁
    • @Version 字段用于并发控制;更新时自动校验版本,冲突将抛出 OptimisticLockException
  • 值对象映射
    • 通过 @Embedded 映射值对象(Address),通过 @Converter 映射复杂值对象(Money)
    • 转换器存储格式需与查询/审计需求匹配,生产中推荐结构化列设计
  • equals/hashCode
    • 优先使用业务唯一键(如email、orderNumber),避免基于未持久化id引发集合行为不一致
  • 命名查询与校验
    • 使用 @NamedQuery 在启动时进行语法校验,降低运行期风险
  • 兼容性
    • 本示例基于 Jakarta Persistence 3.x(jakarta.),如项目仍使用 javax.,请替换对应包名与依赖版本
  • 放置位置
    • persistence.xml 必须放在类路径的 META-INF 目录下(资源路径)以便JPA发现

以上示例展示了 @Entity 在进阶JPA实体映射中的综合用法,包括实体命名、JPQL、实体图、生命周期回调、乐观锁、值对象与转换器、以及双向关联等。

注解功能概述

@PreAuthorize 用于方法调用前的权限校验,基于 Spring Security 的 SpEL 表达式在进入目标方法前进行访问控制判断。其设计目标是将授权规则与业务方法解耦,支持基于角色(Role)、权限(Authority)、用户上下文(Authentication/Principal)以及业务参数的细粒度控制。

典型能力包括:

  • 按角色/权限控制:hasRole('ADMIN')、hasAuthority('DOCUMENT_DELETE')
  • 按业务参数控制:#id、#owner 等方法参数
  • 按当前登录用户信息控制:authentication.name、principal
  • 组合表达式:and、or、!、函数调用、Bean 方法调用(如 @beanName.method(...))

核心参数说明

参数名 类型 默认值 描述
value String 空字符串 必填的 SpEL 表达式,必须返回 boolean。可使用 hasRole、hasAuthority、authentication、principal、方法参数(#param)、Spring 容器中的 Bean(@beanName)等。

使用场景分析

  • 服务层方法授权:在 Service 方法上进行统一、可测试的权限控制,避免将授权逻辑散落在控制器或存储层。
  • 角色/权限控制(RBAC):按业务角色或操作权限限制访问,如管理员可删除、普通用户仅可读。
  • 基于属性的访问控制(ABAC):结合方法参数或域对象属性进行判定,如“文档所有者或管理员可更新”。
  • 多租户/数据隔离:通过表达式引用租户ID、资源归属等实现数据级授权。
  • 规则复用:通过 SpEL 调用 Spring Bean 的方法,实现复杂授权规则的集中复用。

完整代码示例

说明:

  • Spring Boot 3.x / Spring Security 6.x
  • 启用方法级安全 @EnableMethodSecurity(prePostEnabled = true)
  • 使用 InMemoryUserDetailsManager 建两个用户:alice(ROLE_USER)、bob(ROLE_ADMIN + DOCUMENT_DELETE)
  • 在 Service 上使用 @PreAuthorize 展示角色控制、权限控制与基于业务的所有者控制(通过 Bean 方法避免信任客户端传参)
// 文件:src/main/java/com/example/securitydemo/SecurityDemoApplication.java
package com.example.securitydemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecurityDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecurityDemoApplication.class, args);
    }
}

// 文件:src/main/java/com/example/securitydemo/config/SecurityConfig.java
package com.example.securitydemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.InMemoryUserDetailsManager;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable()) // 示例项目关闭 CSRF,生产请按需配置
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    InMemoryUserDetailsManager userDetailsService(PasswordEncoder encoder) {
        UserDetails alice = User.withUsername("alice")
            .password(encoder.encode("password"))
            .authorities("ROLE_USER")
            .build();

        UserDetails bob = User.withUsername("bob")
            .password(encoder.encode("password"))
            .authorities("ROLE_ADMIN", "DOCUMENT_DELETE")
            .build();

        return new InMemoryUserDetailsManager(alice, bob);
    }
}

// 文件:src/main/java/com/example/securitydemo/web/DocumentController.java
package com.example.securitydemo.web;

import com.example.securitydemo.domain.Document;
import com.example.securitydemo.service.DocumentService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

@RestController
@RequestMapping("/docs")
public class DocumentController {
    private final DocumentService service;

    public DocumentController(DocumentService service) {
        this.service = service;
    }

    @GetMapping("/{id}")
    public Document read(@PathVariable Long id) {
        Document doc = service.read(id);
        if (doc == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Document not found");
        }
        return doc;
    }

    @PutMapping("/{id}")
    public Document update(@PathVariable Long id, @RequestParam String content) {
        Document updated = service.update(id, content);
        if (updated == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Document not found");
        }
        return updated;
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        service.delete(id);
    }
}

// 文件:src/main/java/com/example/securitydemo/domain/Document.java
package com.example.securitydemo.domain;

public class Document {
    private Long id;
    private String content;
    private String owner; // 文档所有者用户名

    public Document() {}

    public Document(Long id, String content, String owner) {
        this.id = id;
        this.content = content;
        this.owner = owner;
    }

    public Long getId() { return id; }
    public String getContent() { return content; }
    public String getOwner() { return owner; }

    public void setId(Long id) { this.id = id; }
    public void setContent(String content) { this.content = content; }
    public void setOwner(String owner) { this.owner = owner; }
}

// 文件:src/main/java/com/example/securitydemo/service/DocumentService.java
package com.example.securitydemo.service;

import com.example.securitydemo.domain.Document;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class DocumentService {

    private final Map<Long, Document> store = new ConcurrentHashMap<>();

    public DocumentService() {
        // 初始化两份示例数据
        store.put(1L, new Document(1L, "Hello from Alice", "alice"));
        store.put(2L, new Document(2L, "Secret of Admin", "bob"));
    }

    // 仅要求认证通过的用户具备 USER 或 ADMIN 角色即可读取
    @PreAuthorize("hasAnyRole('USER','ADMIN')")
    public Document read(Long id) {
        return store.get(id);
    }

    // 只有文档所有者或管理员可以更新
    // 通过 Bean 表达式调用 docPermission.canUpdate(...),避免信任客户端传入的 owner 等敏感参数
    @PreAuthorize("@docPermission.canUpdate(#id, authentication)")
    public Document update(Long id, String content) {
        Document doc = store.get(id);
        if (doc == null) {
            return null;
        }
        doc.setContent(content);
        return doc;
    }

    // 仅具有 DOCUMENT_DELETE 权限的用户可删除
    @PreAuthorize("hasAuthority('DOCUMENT_DELETE')")
    public void delete(Long id) {
        store.remove(id);
    }

    // 供权限检查 Bean 使用的只读方法(不加安全注解,避免循环拦截)
    public Document findById(Long id) {
        return store.get(id);
    }
}

// 文件:src/main/java/com/example/securitydemo/security/DocumentPermission.java
package com.example.securitydemo.security;

import com.example.securitydemo.domain.Document;
import com.example.securitydemo.service.DocumentService;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

@Component("docPermission")
public class DocumentPermission {

    private final DocumentService documentService;

    public DocumentPermission(DocumentService documentService) {
        this.documentService = documentService;
    }

    // 业务级权限判断:文档存在 且(当前用户为所有者 或 拥有管理员角色)
    public boolean canUpdate(Long id, Authentication authentication) {
        Document doc = documentService.findById(id);
        if (doc == null) {
            return false;
        }
        boolean isOwner = doc.getOwner() != null && doc.getOwner().equals(authentication.getName());
        boolean isAdmin = authentication.getAuthorities().stream()
            .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
        return isOwner || isAdmin;
    }
}

说明要点:

  • 访问读取接口:任意已认证用户
  • 更新接口:文档所有者或管理员
  • 删除接口:拥有 DOCUMENT_DELETE 权限的用户

示例用户:

  • alice/password:ROLE_USER
  • bob/password:ROLE_ADMIN + DOCUMENT_DELETE

技术要点说明

  • 启用方法级安全:使用 @EnableMethodSecurity(prePostEnabled = true)。Spring Security 6 中该属性默认已启用,显式声明可读性更好。
  • 表达式上下文:
    • 角色判断:hasRole('ADMIN') 等价于检查权限 ROLE_ADMIN
    • 权限判断:hasAuthority('DOCUMENT_DELETE') 检查原始权限名
    • 当前用户:authentication、authentication.name、principal
    • 方法参数:#id、#paramName;如未开启参数名保留,可使用 @P/@Param 指定名称,但更推荐通过 Bean 方法进行业务校验,避免信任客户端传参
  • Bean 方法调用:@beanName.method(...) 可在表达式中复用复杂授权逻辑(示例中的 docPermission.canUpdate)
  • 应用位置与代理限制:
    • @PreAuthorize 可用于类或方法;类级别对所有公开方法生效
    • AOP 代理仅拦截通过 Spring 容器外部调用的 public 方法;同类自调用不会触发拦截
  • 异常与返回码:未通过授权抛出 AccessDeniedException,默认映射为 403 Forbidden
  • 角色前缀:hasRole('X') 会检查权限 'ROLE_X';使用 hasAuthority 需传入完整权限名
  • CSRF:示例为便捷演示禁用 CSRF;生产环境应按请求类型与客户端能力进行配置
  • 测试建议:使用 @WithMockUser 或 SecurityMockServerConfigurers 进行方法级权限单元测试,覆盖正反用例

以上示例展示了 @PreAuthorize 在角色控制、权限控制与基于业务规则校验中的标准用法,并通过 Bean 表达式实现安全且可复用的复杂授权逻辑。

示例详情

解决的问题

为Java开发者与技术团队提供一键式“注解用法”生成服务:输入注解名称、期望场景与代码复杂度,立刻得到专业、可直接运行的示例与实践指南。通过标准化产出(功能说明、参数要点、典型场景、完整代码与注意事项),帮助团队快速统一认知、降低误用风险、缩短检索与试错时间,提升代码评审效率与新人上手速度,并沉淀可复用的内部知识资产,最终带来更快的交付节奏与更稳的质量结果。

适用用户

Java后端开发工程师

在接手新模块时,输入注解名称与场景,快速生成可运行示例并嵌入项目;对Spring事务、校验、缓存等注解,立即获得参数解释与推荐配置,减少排查时间。

架构师/技术负责人

基于最佳实践生成统一用法范式,输出团队可复用的注解示例库与注意事项;在评审阶段对不规范使用给出替代方案,推动风格统一与质量基线。

测试开发工程师

为JUnit或集成测试场景一键生成注解示例与说明,快速搭建测试骨架;结合场景参数生成前后置步骤与断言模板,提高用例产出效率。

特征总结

输入注解名与场景,一键生成完整示例、用途说明与可运行代码,立即上手
覆盖Spring、JPA、测试框架等常见注解,掌握真实业务用法与落地技巧
自动解析注解参数意义与默认值,给出安全配置建议与常用组合方案
提供类、方法、导入齐全代码,复制即用,规避坑点,减少返工与调试时间
按复杂度自由切换,从入门到进阶逐级呈现,匹配个人水平与学习节奏
内置最佳实践与反例警示,统一团队风格,提升可维护性与上线稳定性
输出结构清晰含要点说明与注意事项,便于评审、沉淀文档与团队培训
支持按项目场景定制模板,批量生成注解示例库,缩短交付与培训周期
辅助代码审查快速定位不当用法,提供替代方案与改进建议,降低风险

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥20.00元
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 625 tokens
- 3 个可调节参数
{ 注解名称 } { 使用场景 } { 代码复杂度 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
使用提示词兑换券,低至 ¥ 9.9
了解兑换券 →
限时半价

不要错过!

半价获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59