Jersey 2 + HK2 - 类的自动绑定

8

继续讨论主题Jersey 2 + HK2 - @ApplicationScoped not working

我已经知道如何绑定类,以便正确地@Inject它们。

你有任何想法如何自动化这个过程吗?在我的应用程序中将每个单独的服务放入bind语句似乎是非常糟糕的做法。

3个回答

6

使用谷歌的Guice已经好几年了,我已经习惯了即时绑定器的可用性,它允许注入任意类型而无需事先配置。

我也认为显式绑定每个服务的想法不太好。 我也不喜欢需要使用特殊的构建步骤以及填充程序的附加初始化代码。

因此,我设计了以下JustInTimeResolver实现:

/**
 * Mimic GUICE's ability to satisfy injection points automatically,
 * without needing to explicitly bind every class, and without needing
 * to add an extra build step.
 */
@Service
public class JustInTimeServiceResolver implements JustInTimeInjectionResolver {

    @Inject
    private ServiceLocator serviceLocator;

    @Override
    public boolean justInTimeResolution( Injectee injectee ) {
    final Type requiredType = injectee.getRequiredType();

        if ( injectee.getRequiredQualifiers().isEmpty() && requiredType instanceof Class ) {
            final Class<?> requiredClass = (Class<?>) requiredType;

            // IMPORTANT: check the package name, so we don't accidentally preempt other framework JIT resolvers
            if ( requiredClass.getName().startsWith( "com.fastmodel" )) {
                final List<ActiveDescriptor<?>> descriptors = ServiceLocatorUtilities.addClasses( serviceLocator, requiredClass );

                if ( !descriptors.isEmpty() ) {
                    return true;
                }
            }
        }
        return false;
    }
} 

在我的项目中,我只需在我的Jersey应用程序配置绑定器中添加以下内容:
bind( JustInTimeServiceResolver.class ).to( JustInTimeInjectionResolver.class );

我希望能够像在Guice中一样,自动创建绑定。


@jwells131313 - 我非常好奇为什么像这样的设施没有包含在HK2中。 - Ben Schreiber
1
这个功能已经存在了,你刚刚使用了它!但我猜你的意思是想用一个通用的方法来处理任何出现的类(请注意,你的解决方案只能处理以com.fastmodel开头的类)。问题是,正如你所看到的,添加这个功能非常简单。也许将其改进为一个可以尝试添加任何需要注入的类的版本并不是一个坏主意,但必须小心使用,因为可能有一些类不希望以这种方式添加。不过,对于像“额外功能”这样的功能来说,这并不是一个坏主意。 - jwells131313

1

我建议您首先查看这里:自动服务填充

基本过程是在您的类上使用@Service注释,并在构建时使用JSR-269(APT)处理器(元数据生成器)。这样做将向您的jar文件添加一些元数据(通常在META-INF / hk2-locator / default下)。

然后,您可以确保这些服务被自动捕获,而无需执行所有那些繁琐的绑定,方法是使用动态配置服务中提供的填充器,该服务在每个ServiceLocator中都可用。

伪代码如下:

public void populate(ServiceLocator locator) throws Exception {
    DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
    Populator populator = dcs.getPopulator();
    populator.populate(new ClasspathDescriptorFileFinder(getClass().getClassLoader()));
}

在上述代码中,使用ClasspathDescriptorFileFinder搜索类路径以查找元数据。在像OSGi这样的环境中可以使用其他策略。
我认为这是一种比自己进行所有绑定更好的添加服务的方法。

0
我有一个建议可以解决我的问题,我尝试了提出的解决方案,在这里没有起作用。在我的解决方案中,需要使用@MyInjectable注释对每个类进行注释。
1-创建一个注释
@Retention(RUNTIME)
@Target(ElementType.TYPE)
public @interface MyInjectable {
}

2-创建一个AbstractBinder实现

public class MyApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bindFactory(EMFFactory.class).to(EntityManagerFactory.class).in(Singleton.class);
        bindFactory(EMFactory.class).to(EntityManager.class).in(RequestScoped.class);
        bind(Environment.class).to(Environment.class);
        scanAndBind("com.yourpackage.here");
    }

    private void scanAndBind(String packageName) {
        try {
            Class[] classes = getClasses(packageName);
            for (Class<?> klazz:
                 classes) {
                MyInjectable annotation = klazz.getAnnotation(MyInjectable.class);
                if (annotation!= null) {
                    bind(klazz).to(klazz);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static Class[] getClasses(String packageName)
            throws ClassNotFoundException, IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes.toArray(new Class[classes.size()]);
    }

    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                assert !file.getName().contains(".");
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(".class")) {
                classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
            }
        }
        return classes;
    }

}

3-创建一个ResourceConfig

public class MyApplication extends ResourceConfig {
    @Inject
    public MyApplication(ServiceLocator locator) {
        ServiceLocatorUtilities.enableImmediateScope(locator);
        ....
        register(new MyApplicationBinder());
    }
}

4-在web.xml中正确配置

<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>br.com.solutiontrue.ws</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>your.package.name.MyApplication</param-value>
    </init-param>
    <init-param>
        <param-name>jersey.config.server.resource.validation.disable</param-name>
        <param-value>true</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
</servlet>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接