
在基于Jersey的应用中,HK2 (Hades Kernel 2) 作为默认的依赖注入框架,通过其内置的扫描机制,能够自动发现并注册被特定注解(如@Service和@Contract)标记的组件。这种自动化能力极大地简化了服务的注册和管理。
例如,一个典型的服务接口和实现可能如下所示:
// Service interface
@Contract
public interface UserService {
void add(User user);
}
// Service implementation
@Service
public class UserServiceImpl implements UserService {
@Override
public void add(User user) {
// ... implementation details ...
}
}当Jersey应用启动时,如果配置了HK2的自动扫描特性(例如通过AutoScanFeature),HK2会通过hk2-metadata-generator生成的元数据文件(位于META-INF/hk2-locator/default)来识别这些被@Service和@Contract注解的类,并将它们注册到ServiceLocator中,从而允许通过@Inject进行依赖注入。
然而,这种默认行为的局限性在于,它仅限于HK2预设或通过特定机制生成的元数据所识别的注解。当我们需要为应用程序的其他层(例如数据访问层DAO)定义自定义注解(如@Repository)并希望HK2能够自动注入这些组件时,默认的扫描机制将无法满足需求。直接在DAO类上使用@Singleton等jakarta.inject标准注解也可能不会被HK2的默认扫描器识别为可注入的组件,除非明确配置。
为了扩展HK2的依赖注入能力,使其能够识别并管理自定义注解标记的组件,我们可以利用org.glassfish.hk2.utilities.binding.AbstractBinder进行编程方式的绑定。AbstractBinder提供了一种灵活的方式,允许开发者手动注册服务、配置它们的生命周期(如单例、每次请求等)以及命名绑定。
核心思路是:
由于HK2默认的扫描机制不识别我们的自定义注解,我们需要一个工具来帮助我们发现这些类。Reflections是一个强大的Java反射库,可以用于在运行时扫描类、方法、字段和注解。
在pom.xml中添加Reflections依赖:
<dependencies>
<!-- ... existing dependencies ... -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version> <!-- 使用最新稳定版本 -->
</dependency>
<!-- 如果使用Java 9+,可能需要额外的依赖 -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
</dependencies>为了更好地组织和管理,我们可以定义一个自定义的注解,例如@Repository,用于标记DAO层接口。此外,为了将接口与其实现类关联起来,我们还可以定义一个辅助注解,例如@BeanAddress,它存储了实现类的全限定名。
// src/main/java/com/example/annotations/Repository.java
package com.example.annotations;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Repository {
}
// src/main/java/com/example/annotations/BeanAddress.java
package com.example.annotations;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface BeanAddress {
String implPackageName(); // 用于存储实现类的全限定名
}现在,我们可以使用这些注解来标记我们的DAO接口及其实现:
// src/main/java/com/example/dao/UserDao.java
package com.example.dao;
import com.example.annotations.BeanAddress;
import com.example.annotations.Repository;
import com.example.model.User;
@Repository
@BeanAddress(implPackageName = "com.example.dao.impl.UserDaoImpl")
public interface UserDao {
void save(User user);
}
// src/main/java/com/example/dao/impl/UserDaoImpl.java
package com.example.dao.impl;
import com.example.dao.UserDao;
import com.example.model.User;
import jakarta.inject.Singleton; // 标记为单例,但这不是HK2自动扫描的关键
@Singleton // 这里的@Singleton仅表示生命周期,而非被HK2扫描的触发器
public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
System.out.println("Saving user: " + user.getName());
// ... actual database logic ...
}
}接下来,创建一个继承自AbstractBinder的类,用于扫描并绑定带有@Repository注解的接口:
// src/main/java/com/example/config/CustomRepositoryBinder.java
package com.example.config;
import com.example.annotations.BeanAddress;
import com.example.annotations.Repository;
import jakarta.inject.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 HK2 custom binder configuration for package: " + packageName);
// 使用Reflections扫描指定包下所有带有@Repository注解的接口
Reflections reflections = new Reflections(packageName);
Set<Class<?>> repositories = reflections.getTypesAnnotatedWith(Repository.class, true);
repositories.forEach(repoInterface -> {
// 确保接口有BeanAddress注解来指定实现类
if (repoInterface.isAnnotationPresent(BeanAddress.class)) {
BeanAddress beanAddress = repoInterface.getAnnotation(BeanAddress.class);
String implClassName = beanAddress.implPackageName();
try {
Class<?> implClass = Class.forName(implClassName);
// 绑定实现类到接口,并指定为单例作用域
// .to(repoInterface) 表示将实现类作为 repoInterface 的实现提供
// .in(Singleton.class) 表示此实例为单例
// .named(...) 可以用于命名绑定,如果需要特定名称的实例
bind(implClass).to(repoInterface).in(Singleton.class);
LOGGER.info(String.format("Bound repository: %s to implementation: %s as Singleton",
repoInterface.getName(), implClass.getName()));
} catch (ClassNotFoundException e) {
LOGGER.log(Level.SEVERE, "Implementation class not found for repository: "
+ repoInterface.getName() + " with impl name: " + implClassName, e);
throw new RuntimeException("Failed to bind repository due to missing implementation class.", e);
}
} else {
LOGGER.warning("Repository interface " + repoInterface.getName() + " is missing @BeanAddress annotation.");
}
});
LOGGER.info("Finished HK2 custom binder configuration.");
}
}最后一步是将这个自定义的CustomRepositoryBinder注册到Jersey应用程序的配置中。这通常在ResourceConfig的子类中完成,或者在Jersey的Feature中进行。
// src/main/java/com/example/MyApplication.java
package com.example;
import com.example.config.CustomRepositoryBinder;
import jakarta.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
@ApplicationPath("/")
public class MyApplication extends ResourceConfig {
public MyApplication() {
// 注册Jersey资源类所在的包
packages("com.example.resource");
// 注册HK2自动扫描Feature (如果需要,用于@Service和@Contract)
register(new AutoScanFeature()); // 假设AutoScanFeature是您已有的自动扫描配置
// 注册自定义的Repository Binder
// 传入需要扫描的包名,通常是DAO接口所在的包
register(new CustomRepositoryBinder("com.example.dao"));
// 注册其他组件,例如Jackson JSON provider
register(org.glassfish.jersey.media.json.jackson.JacksonFeature.class);
}
}通过这种方式,当应用程序启动时,CustomRepositoryBinder会被执行,它会扫描com.example.dao包下所有带有@Repository注解的接口,并根据@BeanAddress注解指定的实现类,将它们作为单例服务注册到HK2的ServiceLocator中。此后,您就可以在其他组件(如MyResource)中通过@Inject注入UserDao接口的实例了。
// src/main/java/com/example/resource/MyResource.java
package com.example.resource;
import com.example.dao.UserDao;
import com.example.model.User;
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 MyResource {
@Inject
private UserDao userDao; // 现在可以注入UserDao了
@GET
@Path("/add")
@Produces(MediaType.TEXT_PLAIN)
public String addUser() {
User newUser = new User("John Doe");
userDao.save(newUser);
return "User added successfully!";
}
}通过org.glassfish.hk2.utilities.binding.AbstractBinder结合Reflections库,我们可以有效地扩展HK2的依赖注入能力,使其能够识别并管理除了默认@Service和@Contract之外的自定义注解标记的组件。这种手动绑定策略为开发者提供了极大的灵活性,能够根据应用程序的特定架构和分层需求,精确控制组件的注册、生命周期和可注入性,从而构建更加健壮、可维护的Jersey应用。
以上就是扩展HK2依赖注入:自定义注解与手动绑定策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号