在基于Jersey的应用程序中,HK2 (Hotswap Kernel 2) 作为默认的依赖注入框架,通过扫描特定注解来自动发现和绑定服务。通常,@Service和@Contract是HK2默认识别的注解,用于标记服务实现类和接口。这种自动扫描能力通常依赖于hk2-metadata-generator库在编译时生成元数据文件(位于META-INF/hk2-locator/default),以及运行时AutoScanFeature类通过ClasspathDescriptorFileFinder加载这些元数据。
例如,在提供的配置中:
这使得HK2能够自动识别并注入 UserServiceImpl 作为 UserService 的实现。然而,这种机制的局限性在于,它只对预设的或通过特定配置识别的注解生效。当开发者希望使用自定义注解(如 @Repository 用于数据访问层)或标准的JSR-330 @Singleton 注解来标记组件时,默认的自动扫描可能无法识别,导致依赖注入失败。
为了解决默认扫描机制的局限性,并允许使用自定义注解(例如 @Repository)来标识和绑定组件,我们可以利用HK2提供的 AbstractBinder 类进行手动绑定。这种方法提供了更大的灵活性,允许我们根据业务需求定义自己的组件发现和绑定规则。
核心思想是:
除了Jersey和HK2的依赖外,我们需要引入 Reflections 库来帮助我们扫描自定义注解。
<dependencies> <!-- ... 其他 Jersey/HK2 依赖 ... --> <dependency> <groupId>org.glassfish.jersey.inject</groupId> <artifactId>jersey-hk2</artifactId> </dependency> <dependency> <groupId>org.glassfish.hk2</groupId> <artifactId>hk2-metadata-generator</artifactId> <version>3.0.2</version> </dependency> <!-- Reflections library for scanning annotations --> <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.10.2</version> <!-- Use a recent stable version --> </dependency> </dependencies>
为了实现更灵活的绑定,我们可以定义一个 Repository 注解来标记DAO层接口,并可选地定义一个 BeanAddress 注解来显式指定其实现类,尤其是在接口名无法直接推断实现类名或存在多个实现时。
@Repository 注解:
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface Repository { // 可以在这里添加其他属性,例如名称 String value() default ""; }
@BeanAddress 注解(可选,用于显式指定实现类):
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface BeanAddress { String implPackageName(); // 完整实现类名,包括包名 }
假设我们有一个 UserDao 接口和其实现 UserDaoImpl。
UserDao.java (接口):
import com.example.app.annotations.BeanAddress; import com.example.app.annotations.Repository; @Repository // 标记为自定义的Repository @BeanAddress(implPackageName = "com.example.app.dao.impl.UserDaoImpl") // 显式指定实现类 public interface UserDao { void save(Object user); // ... 其他DAO方法 }
UserDaoImpl.java (实现):
package com.example.app.dao.impl; import com.example.app.dao.UserDao; // 不需要额外的HK2注解,因为绑定器会处理它 public class UserDaoImpl implements UserDao { @Override public void save(Object user) { System.out.println("Saving user via UserDaoImpl: " + user); // ... 实际的持久化逻辑 } }
这是核心部分。我们将创建一个继承自 AbstractBinder 的类,在其中利用 Reflections 扫描带有 @Repository 注解的接口,并根据 BeanAddress 注解(如果存在)或约定来绑定其实现。
package com.example.app.config; import com.example.app.annotations.BeanAddress; import com.example.app.annotations.Repository; import jakarta.inject.Singleton; // JSR-330 标准的 Singleton 注解 import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.reflections.Reflections; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; public class CustomRepositoryBinder extends AbstractBinder { private static final Logger LOGGER = Logger.getLogger(CustomRepositoryBinder.class.getName()); private final String packageName; // 需要扫描的包名 public CustomRepositoryBinder(String packageName) { this.packageName = packageName; } @Override protected void configure() { LOGGER.info("Starting custom repository binding for package: " + packageName); // 使用Reflections扫描指定包下所有带有@Repository注解的类型 Reflections reflections = new Reflections(packageName); Set<Class<?>> repositoryInterfaces = reflections.getTypesAnnotatedWith(Repository.class, true); repositoryInterfaces.forEach(repoInterface -> { if (!repoInterface.isInterface()) { LOGGER.warning("Skipping non-interface class annotated with @Repository: " + repoInterface.getName()); return; } // 尝试从BeanAddress注解获取实现类名 BeanAddress beanAddress = repoInterface.getAnnotation(BeanAddress.class); Class<?> implementationClass = null; if (beanAddress != null) { try { implementationClass = Class.forName(beanAddress.implPackageName()); LOGGER.info("Found explicit implementation for " + repoInterface.getName() + ": " + implementationClass.getName()); } catch (ClassNotFoundException e) { LOGGER.log(Level.SEVERE, "Implementation class not found for " + repoInterface.getName() + ": " + beanAddress.implPackageName(), e); throw new RuntimeException("Failed to load implementation class for " + repoInterface.getName(), e); } } else { // 如果没有BeanAddress,可以尝试通过约定来查找实现类,例如: // 如果接口是 com.example.app.dao.UserDao // 尝试查找 com.example.app.dao.impl.UserDaoImpl String implClassName = repoInterface.getName() + "Impl"; // 简单约定 try { implementationClass = Class.forName(implClassName); LOGGER.info("Found conventional implementation for " + repoInterface.getName() + ": " + implementationClass.getName()); } catch (ClassNotFoundException e) { LOGGER.warning("No explicit BeanAddress and no conventional implementation found for " + repoInterface.getName() + ". Skipping binding."); return; // 跳过此接口 } } if (implementationClass != null) { // 执行绑定:将实现类绑定到接口,并指定为单例作用域 bind(implementationClass).to(repoInterface).in(Singleton.class); LOGGER.info("Successfully bound " + implementationClass.getName() + " to " + repoInterface.getName() + " as Singleton."); } }); } }
代码解释:
最后一步是将 CustomRepositoryBinder 注册到Jersey应用程序中。这通常在 ResourceConfig 子类或 Feature 实现中完成。
在 ResourceConfig 中注册:
package com.example.app.config; import org.glassfish.jersey.server.ResourceConfig; import com.example.app.config.CustomRepositoryBinder; // 导入你的绑定器 public class MyApplication extends ResourceConfig { public MyApplication() { // 注册你的JAX-RS资源包 packages("com.example.app.resource"); // 注册自定义绑定器,传入需要扫描的包名 register(new CustomRepositoryBinder("com.example.app")); // 替换为你的应用根包名 } }
或者,如果你有一个 Feature 类:
package com.example.app.config; import jakarta.ws.rs.core.Feature; import jakarta.ws.rs.core.FeatureContext; import com.example.app.config.CustomRepositoryBinder; // 导入你的绑定器 public class MyCustomFeature implements Feature { @Override public boolean configure(FeatureContext context) { // 注册自定义绑定器 context.register(new CustomRepositoryBinder("com.example.app")); // 替换为你的应用根包名 return true; } }
一旦绑定器注册成功,你就可以在你的JAX-RS资源或其他HK2管理的组件中注入 UserDao 了。
package com.example.app.resource; import com.example.app.dao.UserDao; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; @Path("/users") public class UserResource { @Inject private UserDao userDao; // 现在可以成功注入UserDao @GET @Path("/test-dao") @Produces(MediaType.TEXT_PLAIN) public String testDaoInjection() { userDao.save("John Doe"); return "UserDao injected and used successfully!"; } }
通过 AbstractBinder 结合 Reflections 库,我们成功地扩展了HK2的依赖注入能力,使其能够识别和绑定带有自定义注解(如 @Repository)的组件。这种方法提供了极大的灵活性,允许开发者根据项目架构和分层需求,自定义组件的发现和注册逻辑,从而实现更清晰的职责分离和更精细的依赖管理。这对于构建大型、模块化且易于维护的Jersey/HK2应用程序至关重要。
以上就是HK2 依赖注入:扩展与自定义绑定注解实现的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 //m.sbmmt.com/ All Rights Reserved | php.cn | 湘ICP备2023035733号