第五节 Spring表达式语言(SpEL)

86 阅读1分钟

1.5 Spring表达式语言(SpEL)

1.5.1 资源注入表达式实战(深度工程应用)

SpEL基础语法全景

表达式类型:
  ├─ 字面量:#{'Hello World'}
  ├─ 属性引用:#{systemProperties['user.timezone']}
  ├─ 方法调用:#{T(java.lang.Math).random()}
  ├─ 运算符:
  │    ├─ 算术:+,-,*,/,%,^
  │    ├─ 关系:eq,ne,lt,gt,le,ge
  │    └─ 逻辑:and,or,not
  └─ 集合操作:#{users.?[age > 18]}

动态配置注入案例

@Configuration
public class DynamicConfig {
    // 注入操作系统时区
    @Value("#{systemProperties['user.timezone']}")
    private String systemTimezone;

    // 注入随机端口(8000-9000)
    @Value("#{T(java.util.concurrent.ThreadLocalRandom).current().nextInt(8000,9000)}")
    private int serverPort;

    // 注入环境变量
    @Value("#{environment['DATABASE_URL'] ?: 'jdbc:mysql://localhost:3306/default'}")
    private String databaseUrl;

    // 注入集合元素
    @Value("#{'${allowed.ips}'.split(',')}")
    private List<String> allowedIps;

    // 注入Bean属性
    @Value("#{dataSource.url}")
    private String datasourceUrl;
}

多环境配置表达式方案

# application-dev.properties
app.notification.enabled=true
app.cache.size=1000

# application-prod.properties
app.notification.enabled=false
app.cache.size=5000
@Service
public class SystemService {
    // 根据环境动态启用功能
    @Value("#{${app.notification.enabled} ? 'ENABLED' : 'DISABLED'}")
    private String notificationStatus;

    // 动态计算缓存超时时间
    @Value("#{${app.cache.size} * 60 * 1000}")
    private long cacheTimeout;
}

1.5.2 条件化配置的表达式技巧(生产级方案)

组合条件表达式实战

@Configuration
@Conditional( 
    value = OnRequiredServicesCondition.class
)
public class ServiceConfiguration {
    @Bean
    @ConditionalOnExpression(
        "#{environment.getProperty('app.mode') == 'cluster' && " +
        "T(java.net.InetAddress).getLocalHost().hostName.startsWith('node-')}"
    )
    public ClusterService clusterService() {
        return new ClusterServiceImpl();
    }
}

public class OnRequiredServicesCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, 
                          AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        return env.containsProperty("DB_MASTER_URL") &&
               env.containsProperty("CACHE_SERVERS");
    }
}

表达式驱动特性开关

@RestController
public class FeatureController {
    // 根据配置动态启用API版本
    @GetMapping("/v2/data")
    @ConditionalOnExpression("#{environment['app.feature.v2-api'] == 'enabled'}")
    public ResponseEntity<?> getDataV2() {
        // 新版实现逻辑
    }

    // 根据日期范围启用功能
    @Scheduled(fixedRate = 60000)
    @ConditionalOnExpression("#{T(java.time.LocalDate).now().isAfter(T(java.time.LocalDate).parse('2024-01-01'))}")
    public void executeYearlyTask() {
        // 2024年后启用的任务
    }
}

1.5.3 安全权限表达式进阶用法(金融系统案例)

方法级安全控制

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 启用方法级安全注解
}

@Service
public class AccountService {
    // 账户必须属于当前用户
    @PreAuthorize("#account.owner == authentication.name")
    public void updateAccount(Account account) {
        // 更新逻辑
    }

    // 交易金额限制
    @PreAuthorize("#amount <= 100000 or hasRole('VIP')")
    public void transfer(BigDecimal amount) {
        // 转账逻辑
    }

    // 审计日志访问控制
    @PostFilter("filterObject.operator == authentication.name or hasAuthority('AUDIT_READ_ALL')")
    public List<AuditLog> getLogs() {
        // 查询日志逻辑
    }
}

自定义权限表达式

// 1. 定义根安全对象
public class CustomSecurityExpressionRoot 
    extends SecurityExpressionRoot {
    
    public CustomSecurityExpressionRoot(Authentication a) {
        super(a);
    }

    public boolean isInDepartment(String deptCode) {
        User user = (User) this.authentication.getPrincipal();
        return user.getDepartments().contains(deptCode);
    }
}

// 2. 注册自定义表达式处理器
public class CustomMethodSecurityExpressionHandler 
    extends DefaultMethodSecurityExpressionHandler {
    
    @Override
    protected SecurityExpressionRoot createSecurityExpressionRoot(
        Authentication authentication, MethodInvocation invocation) {
        return new CustomSecurityExpressionRoot(authentication);
    }
}

// 3. 配置安全策略
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig 
    extends GlobalMethodSecurityConfiguration {
    
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler();
    }
}

// 4. 业务层使用
@Service
public class FinanceService {
    @PreAuthorize("isInDepartment('FINANCE')")
    public void approvePayment() {
        // 财务审批逻辑
    }
}

1.5.4 SpEL高级特性实战(数据转换与校验)

类型安全转换表达式

public class DataValidator {
    // 强制类型转换
    @Value("#{T(java.time.LocalDate).parse('${app.startDate}')}")
    private LocalDate startDate;

    // 集合元素转换
    @Value("#{'${app.ports}'.split(',').?[T(java.lang.Integer).parseInt(#this)]}")
    private List<Integer> activePorts;

    // 映射转换
    @Value("#{${app.ratios}.entrySet().stream().collect(T(java.util.Map).Entry.comparingByValue())}")
    private Map<String, Double> sortedRatios;
}

动态校验规则引擎

public class OrderValidationRules {
    // 从配置文件加载规则
    @Value("#{'${validation.order.amount}'.split(':')}")
    private List<String> amountRules;

    public boolean validateAmount(BigDecimal amount) {
        String operator = amountRules.get(0);
        BigDecimal limit = new BigDecimal(amountRules.get(1));
        
        switch (operator) {
            case "gt": return amount.compareTo(limit) > 0;
            case "lt": return amount.compareTo(limit) < 0;
            default: throw new IllegalArgumentException("无效运算符");
        }
    }
}

// 配置示例
validation.order.amount=gt:1000