
在基于jersey的应用程序中,hk2 (hades kernel 2) 作为默认的依赖注入框架扮演着核心角色。它通过扫描特定的注解来自动发现并管理组件的生命周期。默认情况下,hk2主要识别以下两种注解:
当HK2进行组件扫描时(例如通过AutoScanFeature中的ClasspathDescriptorFileFinder),它会查找带有这些注解的类和接口,并自动建立它们之间的绑定关系,使得通过@Inject注解可以获取到相应的实例。例如,如果有一个UserService接口被@Contract标记,其实现类UserServiceImpl被@Service标记,HK2便能自动将UserServiceImpl绑定到UserService接口,并在需要时提供UserServiceImpl的实例。
尽管@Service和@Contract提供了便捷的默认注入机制,但在某些场景下,我们可能希望使用自定义的注解(例如@Repository用于DAO层)来标记组件,或者希望对组件的生命周期(如单例@Singleton)进行更细粒度的控制,而不是仅仅依赖HK2的默认发现逻辑。
直接在自定义注解(如@Repository)上使用@Inject通常不会奏效,因为HK2的默认扫描器并不知道如何识别和处理这些自定义注解。此外,@Singleton是一个生命周期注解,它本身并不能让HK2发现并绑定一个类,它需要与一个绑定机制结合使用才能生效。
例如,对于DAO层,我们通常希望它们是单例的,并且可能使用@Repository这样的语义化注解来标识它们。如果仅仅依赖@Service和@Contract,会导致所有可注入的组件都使用相同的注解,降低了代码的语义清晰度。
为了解决上述问题,HK2提供了org.glassfish.hk2.utilities.binding.AbstractBinder。它允许我们通过编程的方式,手动定义接口与实现类之间的绑定关系,并指定其生命周期。这种方式提供了极大的灵活性,使得我们可以完全控制哪些类被注入、如何被注入以及它们的生命周期。
核心思想是:不再完全依赖HK2的默认扫描机制来发现所有组件,而是通过结合反射(例如使用Reflections库)来扫描我们自定义的注解,然后根据扫描结果,在AbstractBinder中显式地创建绑定。
为了更好地组织和管理自定义组件,我们可以设计自己的注解。例如:
示例自定义注解:
// Repository.java
package com.example.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Repository {
// 标记DAO层接口
}
// BeanAddress.java
package com.example.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface BeanAddress {
String implClassName(); // 存储实现类的全限定名
}示例DAO接口和实现:
// UserDao.java
package com.example.dao;
import com.example.annotations.BeanAddress;
import com.example.annotations.Repository;
@Repository
@BeanAddress(implClassName = "com.example.dao.impl.UserDaoImpl")
public interface UserDao {
void save(Object entity);
// ... 其他DAO方法
}
// UserDaoImpl.java
package com.example.dao.impl;
// 注意:这里不需要HK2的@Service或@Contract注解
public class UserDaoImpl implements UserDao {
@Override
public void save(Object entity) {
System.out.println("Saving entity: " + entity.getClass().getSimpleName());
// 实际的数据库操作逻辑
}
}AbstractBinder的核心是重写configure()方法,在该方法中定义所有的绑定规则。结合Reflections库,我们可以动态地扫描带有@Repository注解的接口,然后根据@BeanAddress注解的信息来绑定其实现。
首先,确保你的pom.xml中包含了Reflections库的依赖:
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version> <!-- 使用最新稳定版本 -->
</dependency>然后,创建自定义的AbstractBinder:
package com.example.di;
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 custom HK2 binding for package: " + packageName);
Reflections reflections = new Reflections(packageName);
// 1. 扫描所有带有 @Repository 注解的接口/类
Set<Class<?>> repositoryInterfaces = reflections.getTypesAnnotatedWith(Repository.class, true);
repositoryInterfaces.forEach(repoInterface -> {
if (repoInterface.isInterface()) { // 确保是接口
// 2. 获取 @BeanAddress 注解,从中提取实现类的全限定名
BeanAddress beanAddress = repoInterface.getAnnotation(BeanAddress.class);
if (beanAddress == null) {
LOGGER.warning("Interface " + repoInterface.getName() + " has @Repository but no @BeanAddress. Skipping.");
return;
}
try {
// 3. 根据实现类名加载实现类
Class<?> implementationClass = Class.forName(beanAddress.implClassName());
// 4. 绑定接口到实现类,并指定为单例
bind(implementationClass).to(repoInterface).in(Singleton.class);
LOGGER.info(String.format("Bound %s to %s as Singleton.", implementationClass.getName(), repoInterface.getName()));
} catch (ClassNotFoundException e) {
LOGGER.log(Level.SEVERE, "Could not find implementation class for " + repoInterface.getName() + ": " + beanAddress.implClassName(), e);
throw new RuntimeException("Failed to bind repository: " + repoInterface.getName(), e);
}
} else {
LOGGER.warning("Class " + repoInterface.getName() + " has @Repository but is not an interface. Skipping.");
}
});
LOGGER.info("Custom HK2 binding completed.");
}
}在上述代码中:
要使自定义的AbstractBinder生效,你需要将其注册到Jersey应用程序的配置中。这通常在ResourceConfig的子类或应用程序启动时完成。
package com.example.app;
import com.example.di.CustomRepositoryBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
public class MyApplication extends ResourceConfig {
public MyApplication() {
// 注册JAX-RS资源包
packages("com.example.resources"); // 你的JAX-RS资源类所在的包
// 禁用HK2的默认自动扫描(如果你的AutoScanFeature已经处理了,这里可能不需要)
// property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
// property(ServerProperties.MOXY_JSON_FEATURE_DISABLE, true);
// 注册自定义的Binder
register(new CustomRepositoryBinder("com.example")); // 替换为你的根包名,以便Reflections扫描
// 注册HK2的元数据生成器,确保HK2能正确处理注解和类路径
// 如果你的pom.xml中已经包含了jersey-hk2和hk2-metadata-generator,通常不需要额外代码
// 但如果遇到警告或问题,可以考虑显式配置
// register(org.glassfish.hk2.utilities.binding.ServiceLocatorBinder.class); // 示例,不一定需要
}
}在Jersey Grizzly服务器的启动代码中,你需要使用这个ResourceConfig子类:
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.grizzly.http.server.HttpServer;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
private static final URI BASE_URI = URI.create("http://localhost:8080/api/");
public static HttpServer startServer() {
// 创建应用程序配置实例
final MyApplication config = new MyApplication();
return GrizzlyHttpServerFactory.createHttpServer(BASE_URI, config);
}
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
System.in.read();
server.shutdownNow();
}
}现在,你的UserDao接口就可以通过@Inject注入到其他组件中,并且它的实例将是单例的:
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;
import com.example.dao.UserDao; // 引入你的DAO接口
@Path("/users")
public class UserResource {
@Inject
private UserDao userDao; // HK2现在可以注入UserDao的单例实例了
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getUsers() {
userDao.save(new Object()); // 调用DAO方法
return "User data accessed!";
}
}通过利用HK2的AbstractBinder机制,结合自定义注解和反射扫描,我们可以极大地扩展HK2依赖注入的能力。这种方法不仅允许我们使用更具语义化的注解来标记不同层次的组件(如DAO层的@Repository),还能对这些组件的生命周期进行精确控制(如设置为单例),从而提高了应用程序的模块化程度、可维护性和灵活性。在面对HK2默认机制无法满足的复杂注入场景时,AbstractBinder提供了一个强大而灵活的解决方案。
以上就是优化HK2依赖注入:如何通过自定义绑定扩展组件扫描与生命周期管理的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号