3

シングルトン オブジェクトの静的ラッパーである XXXLocalServiceUtil クラスの形式でサービスを公開する API を使用しています。静的な XXXLocalServiceUtil メソッドを使用する代わりに、XXXLocalService オブジェクト自体を挿入して、コードで直接使用したいと考えています。

@Named
public class MyMailingService {        
    @Inject UserLocalService userService;

    public String mailUser(String email) {
       User user = userService.getUser(email);
       emailUser(user);
    }
}

そして、私のapplicationContext.xmlように設定します:

<beans ...>
    <bean class="x.y.z.UserLocalServiceUtil" factory-method="getService"/>
    <bean class="x.y.z.CompanyLocalServiceUtil" factory-method="getService"/>
    ...
</beans>

これは完全に機能します。さて、私が話しているこの API には、これらの XXXLocalServiceUtil クラスが約 100 あり、それぞれに実際のサービスを返す独自の静的getServiceメソッドがあります。これらすべてのサービスをリストする代わりに、applicationContext.xmlSpring に、注入する XXXLocalService ごとに適切な XXXLocalServiceUtil クラスを見つけるという魔法を実行させたいと思います。したがって、私が必要としているのは、もちろん遅延読み込みベースで、私のために作業を行うある種の動的 Bean ファクトリです。

これを簡単に達成する方法を知っている人はいますか?

4

2 に答える 2

7

GenericApplicationContextを使用して、XML で宣言された残りの Spring Bean とともに、Bean を applicationContext に動的にロードできます。Reflections ライブラリを使用して実装された例を次に示します...

private static final Pattern SERVICE_UTIL_PATTERN = Pattern.compile(".*LocalServiceUtil.*");

public static void main(String[] args) {
    ConfigurationBuilder builder = new ConfigurationBuilder().addUrls(
            ClasspathHelper.forPackage("x.y.z"))
            .setScanners(new SubTypesScanner(false));
    Reflections reflections = new Reflections(builder);
    GenericApplicationContext applicationContext = new GenericApplicationContext();
    Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);

    for (Class<? extends Object> serviceUtilClass : classes) {
        String className = serviceUtilClass.getName();

        if (SERVICE_UTIL_PATTERN.matcher(className).matches()) {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClassName(className);
            beanDefinition.setFactoryMethodName("getService");
            beanDefinition.setLazyInit(true);

            String beanName = StringUtils.uncapitalize(serviceClass.getSimpleName().replace("Util", ""));
            applicationContext.registerBeanDefinition(beanName, beanDefinition);
        }
    }

    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(applicationContext);
    reader.loadBeanDefinitions("classpath:/applicationContext.xml");
    applicationContext.refresh();
}

更新: これを Web アプリケーションで使用するには、Spring のXmlWebApplicationContextを拡張し、次のようにメソッドをオーバーライドするだけinitBeanDefinitionReaderです...

private static final Pattern SERVICE_UTIL_PATTERN = Pattern.compile(".*LocalServiceUtil.*");

@Override
protected void initBeanDefinitionReader(
        XmlBeanDefinitionReader beanDefinitionReader) {
    ConfigurationBuilder builder = new ConfigurationBuilder().addUrls(
            ClasspathHelper.forPackage("x.y.z"))
            .setScanners(new SubTypesScanner(false));
    Reflections reflections = new Reflections(builder);
    Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
    BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();

    for (Class<? extends Object> serviceClass : classes) {
        String className = serviceClass.getName();

        if (SERVICE_UTIL_PATTERN.matcher(className).matches()) {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClassName(className);
            beanDefinition.setFactoryMethodName("getService");
            beanDefinition.setLazyInit(true);
            String beanName = StringUtils.uncapitalize(serviceClass
                    .getSimpleName().replace("Util", ""));
            registry.registerBeanDefinition(beanName, beanDefinition);
        }
    }
}

}

web.xmlに以下を追加context-paramします...

<context-param>
  <param-name>contextClass</param-name>
  <param-value>x.y.z.MyXmlWebApplicationContext</param-value>
</context-param>
于 2012-06-05T20:19:53.223 に答える
3

あなたが試すことができることの1つは、次のように、タイプごとに自動配線するように applicationContext.xml に変更@Injectして定義することです。@Autowired

<beans ... default-autowire="byType">
    ...
</beans>

@Inject@Autowiredは同等ですが、Spring の@Autowiredアノテーションには、required属性が強制注入されるという利点があります。

別の解決策:

ファクトリを使用する代わりに、プロトタイプスコープで Bean を使用できます。

インターフェイスをXXXLocalService実装しApplicationContextAware、プロトタイプ化された Bean を取得する必要があります。つまり、次のようになります。

@Named
public class MyMailingService implements ApplicationContextAware {        
    @Inject UserLocalService userService;

    private ApplicationContext appContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.appContext = applicationContext;
    }

    public String mailUser(String email) {
       User user = (User) appContext.getBean("user");
       emailUser(user);
    }
}

また、applicationContext.xml は次のようになります。

<beans ...>
    <bean id="user" class="x.y.z.User" scope="prototype"/>
    ...
</beans>

を呼び出すたびにgetBean()、そのタイプの新しいオブジェクトが取得され、その Bean にすべてが注入されるという利点があります。

于 2012-05-29T19:14:22.913 に答える