WildFly JavaEE部署中查找自定义注解类的最优方案及资源保护
在WildFly环境里要实现部署时扫描自定义注解类并缓存,同时做好资源保护,我给你梳理下最优方案和具体步骤:
一、扫描自定义注解类的最优方式:CDI扩展(推荐)
CDI是Jakarta EE的标准特性,WildFly原生支持,这种方式跨服务器兼容,实现起来简洁,不需要依赖WildFly特定API,是首选方案。
步骤1:实现CDI扩展类
创建一个实现javax.enterprise.inject.spi.Extension的类,通过观察ProcessAnnotatedType事件来捕获所有带自定义注解的类:
import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; import java.util.Collections; import java.util.HashSet; import java.util.Set; public class CustomAnnotationScanner implements Extension { // 用线程安全的集合存储扫描到的类,避免并发问题 private final Set<Class<?>> annotatedClasses = Collections.synchronizedSet(new HashSet<>()); // 监听ProcessAnnotatedType事件,筛选带目标注解的类 <T> void onProcessAnnotatedType(@Observes ProcessAnnotatedType<T> event) { if (event.getAnnotatedType().isAnnotationPresent(MyCustomAnnotation.class)) { annotatedClasses.add(event.getAnnotatedType().getJavaClass()); } } // 对外提供只读的缓存集合,防止外部修改 public Set<Class<?>> getAnnotatedClasses() { return Collections.unmodifiableSet(annotatedClasses); } }
步骤2:注册CDI扩展
在项目的META-INF/services目录下创建文件javax.enterprise.inject.spi.Extension,内容为你的扩展类全限定名,比如:
com.example.annotation.CustomAnnotationScanner
这样WildFly在部署时会自动识别并加载这个扩展。
步骤3:封装缓存供业务访问
把扫描到的类缓存封装成一个CDI Bean,方便业务代码注入使用:
import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import java.util.Set; @ApplicationScoped public class AnnotatedClassCache { private final CustomAnnotationScanner scanner; // 构造注入CDI扩展类 @Inject public AnnotatedClassCache(CustomAnnotationScanner scanner) { this.scanner = scanner; } // 对外提供缓存的类集合 public Set<Class<?>> getCachedAnnotatedClasses() { return scanner.getAnnotatedClasses(); } }
二、备选方案:WildFly特定部署处理器
如果需要更底层的部署阶段控制(比如在类加载前处理),可以使用WildFly的DeploymentUnitProcessor,但这种方式依赖WildFly API,跨服务器兼容性差:
步骤1:实现部署处理器
import org.jboss.as.server.deployment.Attachments; import org.jboss.as.server.deployment.DeploymentPhaseContext; import org.jboss.as.server.deployment.DeploymentUnit; import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.jandex.IndexView; import org.jboss.jandex.AnnotationInstance; import java.util.Collection; public class WildFlyAnnotationScannerProcessor implements DeploymentUnitProcessor { @Override public void deploy(DeploymentPhaseContext phaseContext) { DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); // 获取WildFly的类索引(由Jandex生成,高效扫描类注解) IndexView index = deploymentUnit.getAttachment(Attachments.INDEX); if (index != null) { // 查找所有带自定义注解的类 Collection<AnnotationInstance> annotations = index.getAnnotations(MyCustomAnnotation.class.getName()); for (AnnotationInstance annotation : annotations) { String className = annotation.target().asClass().name().toString(); try { Class<?> clazz = deploymentUnit.getClassLoader().loadClass(className); // 将clazz存入线程安全的缓存集合 } catch (ClassNotFoundException e) { // 处理类加载异常 } } } } @Override public void undeploy(DeploymentUnit deploymentUnit) { // 部署卸载时清理缓存,避免内存泄漏 } }
步骤2:注册部署处理器
需要将处理器打包成WildFly模块,并在jboss-deployment-structure.xml中配置依赖,这个过程相对繁琐,适合有WildFly定制需求的场景。
三、资源保护方案
1. 缓存访问控制
通过CDI的安全注解限制缓存的访问权限,比如使用@RolesAllowed:
import javax.annotation.security.RolesAllowed; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import java.util.Set; @ApplicationScoped @RolesAllowed("ANNOTATION_ADMIN") // 只有拥有ANNOTATION_ADMIN角色的用户能访问 public class AnnotatedClassCache { // ... 原有代码 }
同时需要在WildFly中配置相应的安全域和角色映射,确保只有授权用户能调用缓存方法。
2. 线程安全保护
缓存集合必须使用线程安全的实现,比如Collections.synchronizedSet、CopyOnWriteArraySet或ConcurrentHashMap(如果需要键值对存储),避免多线程访问时出现并发修改异常。
3. 类加载安全
如果使用WildFly部署处理器,要确保处理器所在的模块拥有足够的类加载权限,在module.xml中添加必要的依赖:
<module xmlns="urn:jboss:module:1.3" name="com.example.annotation.processor"> <resources> <resource-root path="annotation-processor.jar"/> </resources> <dependencies> <module name="org.jboss.as.server"/> <module name="org.jboss.jandex"/> <module name="javax.enterprise.api"/> <module name="javax.annotation.api"/> </dependencies> </module>
4. 缓存只读保护
对外暴露缓存时,返回不可修改的集合(比如Collections.unmodifiableSet),防止外部代码意外修改缓存内容,保证数据一致性。
内容的提问来源于stack exchange,提问作者Net_Hans

