Add support for multiple GuiceModuleMetadatas
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.guice;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface BindingTypeMatcher {
|
||||
|
||||
boolean matches(Class<?> type);
|
||||
|
||||
}
|
||||
@@ -23,14 +23,24 @@ import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Annotation that decorates the whole application context and provides metadata to Guice
|
||||
* if the context is used as a Module. Can be added to any <code>@Configuration</code>
|
||||
* class (and if added to many then the filters are combined with logical OR). By default
|
||||
* all beans in the context will be bound to Guice with all of their implemented
|
||||
* interfaces. If you need to filter out which beans are added you can filter by class.
|
||||
* TODO: filter by name.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @see SpringModule
|
||||
* @see GuiceModuleMetadata
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(GuiceModuleMetadataRegistrar.class)
|
||||
public @interface GuiceModules {
|
||||
public @interface GuiceModule {
|
||||
|
||||
/**
|
||||
* Specifies which types are eligible for inclusion in Guice module
|
||||
@@ -30,12 +30,14 @@ import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
* Encapsulates some metadata about a Guice module that is to be created from the parent
|
||||
* context of a <code>@Bean</code> of this type.
|
||||
* context of a <code>@Bean</code> of this type. Can be used directly as a
|
||||
* <code>@Bean</code>, but it is easier to just add <code>@</code>{@link GuiceModule} to
|
||||
* your <code>@Configuration</code>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class GuiceModuleMetadata {
|
||||
public class GuiceModuleMetadata implements BindingTypeMatcher {
|
||||
|
||||
private TypeFilter[] includeFilters;
|
||||
|
||||
@@ -60,6 +62,7 @@ public class GuiceModuleMetadata {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> type) {
|
||||
|
||||
if (infrastructureTypes.contains(type)) {
|
||||
@@ -104,7 +107,9 @@ public class GuiceModuleMetadata {
|
||||
private boolean visible(Class<?> type) {
|
||||
Class<?> cls = type;
|
||||
while (cls != null && cls != Object.class) {
|
||||
if (!Modifier.isInterface(cls.getModifiers()) && !Modifier.isPublic(cls.getModifiers()) && !Modifier.isProtected(cls.getModifiers())) {
|
||||
if (!Modifier.isInterface(cls.getModifiers())
|
||||
&& !Modifier.isPublic(cls.getModifiers())
|
||||
&& !Modifier.isProtected(cls.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
cls = cls.getDeclaringClass();
|
||||
|
||||
@@ -26,8 +26,10 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
@@ -65,8 +67,10 @@ public class GuiceModuleMetadataRegistrar implements ImportBeanDefinitionRegistr
|
||||
parseFilters(annotation, "includeFilters"));
|
||||
builder.addPropertyValue("excludeFilters",
|
||||
parseFilters(annotation, "excludeFilters"));
|
||||
registry.registerBeanDefinition(GuiceModules.class.getName(),
|
||||
builder.getBeanDefinition());
|
||||
AbstractBeanDefinition definition = builder.getBeanDefinition();
|
||||
String name = new DefaultBeanNameGenerator().generateBeanName(definition,
|
||||
registry);
|
||||
registry.registerBeanDefinition(name, definition);
|
||||
}
|
||||
|
||||
public static class GuiceModuleMetadataFactory implements
|
||||
@@ -110,7 +114,7 @@ public class GuiceModuleMetadataRegistrar implements ImportBeanDefinitionRegistr
|
||||
|
||||
Set<TypeFilter> result = new HashSet<TypeFilter>();
|
||||
AnnotationAttributes attributes = new AnnotationAttributes(
|
||||
annotation.getAnnotationAttributes(GuiceModules.class.getName()));
|
||||
annotation.getAnnotationAttributes(GuiceModule.class.getName()));
|
||||
AnnotationAttributes[] filters = attributes.getAnnotationArray(attributeName);
|
||||
|
||||
for (AnnotationAttributes filter : filters) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
package org.springframework.guice;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -36,7 +37,7 @@ public class SpringModule implements Module {
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private GuiceModuleMetadata metadata = new GuiceModuleMetadata();
|
||||
private BindingTypeMatcher matcher = new GuiceModuleMetadata();
|
||||
|
||||
private Map<Class<?>, Provider<?>> bound = new HashMap<Class<?>, Provider<?>>();
|
||||
|
||||
@@ -44,10 +45,8 @@ public class SpringModule implements Module {
|
||||
this.beanFactory = (DefaultListableBeanFactory) context
|
||||
.getAutowireCapableBeanFactory();
|
||||
if (beanFactory.getBeanNamesForType(GuiceModuleMetadata.class).length > 0) {
|
||||
this.metadata = beanFactory.getBean(GuiceModuleMetadata.class);
|
||||
} else if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory,
|
||||
GuiceModuleMetadata.class).length > 0) {
|
||||
this.metadata = beanFactory.getBean(GuiceModuleMetadata.class);
|
||||
this.matcher = new CompositeTypeMatcher(beanFactory.getBeansOfType(
|
||||
GuiceModuleMetadata.class).values());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +62,6 @@ public class SpringModule implements Module {
|
||||
final String beanName = name;
|
||||
Provider<Object> provider = new BeanFactoryProvider(beanFactory,
|
||||
beanName, type);
|
||||
if (bound.get(type) != null) {
|
||||
continue; // TODO: named beans
|
||||
}
|
||||
if (!cls.isInterface() && !ClassUtils.isCglibProxyClass(cls)) {
|
||||
bindConditionally(binder, cls, provider);
|
||||
}
|
||||
@@ -78,13 +74,17 @@ public class SpringModule implements Module {
|
||||
}
|
||||
}
|
||||
|
||||
private void bindConditionally(Binder binder, Class<Object> cls,
|
||||
private void bindConditionally(Binder binder, Class<Object> type,
|
||||
Provider<Object> provider) {
|
||||
if (!metadata.matches(cls)) {
|
||||
if (bound.get(type) != null) {
|
||||
// Only bind one provider for each type
|
||||
return; // TODO: named beans
|
||||
}
|
||||
if (!matcher.matches(type)) {
|
||||
return;
|
||||
}
|
||||
binder.bind(cls).toProvider(provider);
|
||||
bound.put(cls, provider);
|
||||
binder.bind(type).toProvider(provider);
|
||||
bound.put(type, provider);
|
||||
}
|
||||
|
||||
private static class BeanFactoryProvider implements Provider<Object> {
|
||||
@@ -125,4 +125,22 @@ public class SpringModule implements Module {
|
||||
}
|
||||
}
|
||||
|
||||
private static class CompositeTypeMatcher implements BindingTypeMatcher {
|
||||
private Collection<? extends BindingTypeMatcher> matchers;
|
||||
|
||||
public CompositeTypeMatcher(Collection<? extends BindingTypeMatcher> matchers) {
|
||||
this.matchers = matchers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> type) {
|
||||
for (BindingTypeMatcher matcher : matchers) {
|
||||
if (matcher.matches(type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
package org.springframework.guice;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
@@ -34,7 +34,7 @@ import com.google.inject.Injector;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class GuiceModulesTests {
|
||||
public class GuiceModuleAnnotationTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
@@ -42,8 +42,7 @@ public class GuiceModulesTests {
|
||||
@Test
|
||||
public void includes() throws Exception {
|
||||
Injector injector = createInjector(TestConfig.class, MetadataIncludesConfig.class);
|
||||
expected.expect(ConfigurationException.class);
|
||||
assertNull(injector.getBinding(Service.class));
|
||||
assertNotNull(injector.getBinding(Service.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,6 +52,12 @@ public class GuiceModulesTests {
|
||||
assertNull(injector.getInstance(Service.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoIncludes() throws Exception {
|
||||
Injector injector = createInjector(TestConfig.class, MetadataIncludesConfig.class, MetadataMoreIncludesConfig.class);
|
||||
assertNotNull(injector.getBinding(Service.class));
|
||||
}
|
||||
|
||||
private Injector createInjector(Class<?>... config) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(config);
|
||||
@@ -69,23 +74,27 @@ public class GuiceModulesTests {
|
||||
|
||||
public static class Foo {
|
||||
|
||||
@Inject
|
||||
@Autowired
|
||||
public Foo(Service service) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@GuiceModules(excludeFilters=@Filter(type=FilterType.REGEX, pattern=".*"))
|
||||
@GuiceModule(excludeFilters=@Filter(type=FilterType.REGEX, pattern=".*"))
|
||||
protected static class MetadataExcludesConfig {
|
||||
}
|
||||
|
||||
// TODO: allow include Service.class as well
|
||||
@Configuration
|
||||
@GuiceModules(includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=MyService.class))
|
||||
@GuiceModule(includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=Service.class))
|
||||
protected static class MetadataIncludesConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@GuiceModule(includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class))
|
||||
protected static class MetadataMoreIncludesConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class TestConfig {
|
||||
@Bean
|
||||
Reference in New Issue
Block a user