在Spring

时间:2016-07-15 13:43:17

标签: spring spring-ioc

我想实现一个注释,它在应用程序启动后立即向工厂注册类(而不是类的实例)。我使用的是Spring Framework 4.2.7。

考虑一个带有仪表板和多个小部件的系统。仪表板有一个配置文件,其中包含要为当前用户显示的小组件列表。显示时,它会读取配置并创建小部件。小部件将从配置中接收其他参数。

以下是一些说明这一点的代码:

public class TestDashboard implements Dashboard {
    public void dashboardPreDisplay() {
        List<String> widgets = getWidgetList(/* current user in session */);
        for (String widgetId : widgets) {
            // create instance of DashboardWidget with given ID
            DashboardWidget x = widgetFactory.createWidget(widgetId);
        }
    }

    public List<String> getWidgetList(String user) {
        // load list of IDs of DashboardWidgets to be displayed for the user
    }

    @Autowired
    private WidgetFactory widgetFactory;
}

@Service
public class WidgetFactory {
    public DashboardWidget createWidget(String widgetId) {
        // look up Class<> of DashboardWidget with given id in widgetClasses
        // construct and initialize DashboardWidget
    }

    private HashMap<String, Class<?>> widgetClasses;
}

在实现我的小部件时,我不想处理使用工厂类注册小部件。理想情况下,我会像这样注释小部件:

@DashboardWidget(id = "uniqueId")
public class DashboardWidgetA implements DashboardWidget {
    // ...
}

当应用程序启动时,它应扫描类@DashboardWidget注释的类路径,并在工厂中注册类,以便通过为createWidget-method提供小部件的id来构造小部件。

此刻我有点困惑。我认为Spring拥有实现此行为的所有工具。但我想不出怎么做的方法。

你对我有什么建议吗?

1 个答案:

答案 0 :(得分:1)

没有什么可以阻止您创建自定义注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DashboardWidget {}

然后你可以注释你的Widget的类,并使它们成为spring bean。如果你想将它们作为单例(scope = singleton)或每个用户的单独实例(scope = prototype),你必须记住。

你必须实施:

public class WidgetInitializationListener implements ApplicationListener<ContextRefreshedEvent> {

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {

    ApplicationContext context = event.getApplicationContext();
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        String originalClassName = getOriginalClassName(beanDefinitionName, event);
        if (originalClassName != null) {
            Class<?> clazz = Class.forName(originalClassName);
            if (hasWidgetAnnotation(clazz)) {
                registerSomewhereYourWidget(context, beanDefinitionName, originalClassName);
            }
        }
    }
}

private String getOriginalClassName(String name, ContextRefreshedEvent event) {
    try {
        ConfigurableListableBeanFactory factory =
                (ConfigurableListableBeanFactory)event.getApplicationContext().getAutowireCapableBeanFactory();
        BeanDefinition beanDefinition = factory.getBeanDefinition(name);
        return beanDefinition.getBeanClassName();
    } catch (NoSuchBeanDefinitionException e) {
        LOG.debug("Can't get bean definition for : " + name);
        return null;
    }
}

所以这里大多数情况与春天无关,只是你只是通过你的bean来找到注释的那些。