Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
add @enable java configuration option
todo: annotationClass and markerInterface support, more testing & example code
- Loading branch information
Showing
19 changed files
with
388 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
src/main/java/org/mybatis/spring/mapper/MapperBeanRegistrar.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.mybatis.spring.mapper; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.mybatis.spring.mapper.annotation.EnableMyBatisMapperScanner; | ||
|
||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; | ||
import org.springframework.core.annotation.AnnotationAttributes; | ||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; | ||
import org.springframework.core.type.AnnotationMetadata; | ||
import org.springframework.util.StringUtils; | ||
|
||
/** | ||
* A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration | ||
* of MyBatis mapper scanning. Using an @Enable annotation allows beans to | ||
* be registered via @Component configuration, whereas implementing | ||
* {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration. | ||
* | ||
* @author lanyonm | ||
* @see MapperFactoryBean | ||
* @since 1.1.2 | ||
* @version $Id$ | ||
*/ | ||
public class MapperBeanRegistrar implements ImportBeanDefinitionRegistrar { | ||
|
||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { | ||
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap( | ||
importingClassMetadata.getAnnotationAttributes(EnableMyBatisMapperScanner.class.getName())); | ||
|
||
List<String> basePackages = new ArrayList<String>(); | ||
for (String pkg : annoAttrs.getStringArray("value")) { | ||
if (StringUtils.hasText(pkg)) { | ||
basePackages.add(pkg); | ||
} | ||
} | ||
for (String pkg : annoAttrs.getStringArray("basePackages")) { | ||
if (StringUtils.hasText(pkg)) { | ||
basePackages.add(pkg); | ||
} | ||
} | ||
|
||
MapperScanner scanner = new MapperScanner(registry); | ||
scanner.doScan(StringUtils.toStringArray(basePackages)); | ||
} | ||
|
||
} |
201 changes: 201 additions & 0 deletions
201
src/main/java/org/mybatis/spring/mapper/MapperScanner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package org.mybatis.spring.mapper; | ||
|
||
import java.io.IOException; | ||
import java.lang.annotation.Annotation; | ||
import java.util.Arrays; | ||
import java.util.Set; | ||
|
||
import org.apache.ibatis.binding.MapperRegistry; | ||
import org.apache.ibatis.session.SqlSessionFactory; | ||
import org.mybatis.spring.SqlSessionTemplate; | ||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; | ||
import org.springframework.beans.factory.config.BeanDefinition; | ||
import org.springframework.beans.factory.config.BeanDefinitionHolder; | ||
import org.springframework.beans.factory.config.RuntimeBeanReference; | ||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; | ||
import org.springframework.beans.factory.support.GenericBeanDefinition; | ||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; | ||
import org.springframework.core.type.classreading.MetadataReader; | ||
import org.springframework.core.type.classreading.MetadataReaderFactory; | ||
import org.springframework.core.type.filter.AnnotationTypeFilter; | ||
import org.springframework.core.type.filter.AssignableTypeFilter; | ||
import org.springframework.core.type.filter.TypeFilter; | ||
import org.springframework.util.StringUtils; | ||
|
||
/** | ||
* A {@link ClassPathBeanDefinitionScanner} that registers Mappers by | ||
* {@code basePackage}, {@code annotationClass}, or {@code markerInterface}. | ||
* If an {@code annotationClass} and/or {@code markerInterface} is specified, | ||
* only the specified types will be searched (searching for all interfaces | ||
* will be disabled). | ||
* | ||
* <p>This functionality was previously a private class of | ||
* {@link MapperScannerConfigurer}, but was broken out in version 1.1.2.</p> | ||
* | ||
* @see MapperFactoryBean | ||
* @see MapperScannerConfigurer | ||
* @see MapperRegistry | ||
* @since 1.1.2 | ||
* @version $Id$ | ||
*/ | ||
class MapperScanner extends ClassPathBeanDefinitionScanner { | ||
|
||
private boolean addToConfig = true; | ||
private Class<? extends Annotation> annotationClass; | ||
private Class<?> markerInterface; | ||
private SqlSessionFactory sqlSessionFactory; | ||
private SqlSessionTemplate sqlSessionTemplate; | ||
private String sqlSessionTemplateBeanName; | ||
private String sqlSessionFactoryBeanName; | ||
|
||
public MapperScanner(BeanDefinitionRegistry registry) { | ||
this(registry, true); | ||
} | ||
|
||
public MapperScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { | ||
super(registry, useDefaultFilters); | ||
} | ||
|
||
public void setAddToConfig(boolean addToConfig) { | ||
this.addToConfig = addToConfig; | ||
} | ||
|
||
public void setAnnotationClass(Class<? extends Annotation> annotationClass) { | ||
this.annotationClass = annotationClass; | ||
} | ||
|
||
public void setMarkerInterface(Class<?> markerInterface) { | ||
this.markerInterface = markerInterface; | ||
} | ||
|
||
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { | ||
this.sqlSessionFactory = sqlSessionFactory; | ||
} | ||
|
||
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { | ||
this.sqlSessionTemplate = sqlSessionTemplate; | ||
} | ||
|
||
public void setSqlSessionTemplateBeanName(String sqlSessionTemplateBeanName) { | ||
this.sqlSessionTemplateBeanName = sqlSessionTemplateBeanName; | ||
} | ||
|
||
public void setSqlSessionFactoryBeanName(String sqlSessionFactoryBeanName) { | ||
this.sqlSessionFactoryBeanName = sqlSessionFactoryBeanName; | ||
} | ||
|
||
/** | ||
* Configures parent scanner to search for the right interfaces. It can search for all | ||
* interfaces or just for those that extends a markerInterface or/and those annotated with | ||
* the annotationClass | ||
*/ | ||
@Override | ||
protected void registerDefaultFilters() { | ||
boolean acceptAllInterfaces = true; | ||
|
||
// if specified, use the given annotation and / or marker interface | ||
if (this.annotationClass != null) { | ||
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); | ||
acceptAllInterfaces = false; | ||
} | ||
|
||
// override AssignableTypeFilter to ignore matches on the actual marker interface | ||
if (this.markerInterface != null) { | ||
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { | ||
@Override | ||
protected boolean matchClassName(String className) { | ||
return false; | ||
} | ||
}); | ||
acceptAllInterfaces = false; | ||
} | ||
if (acceptAllInterfaces) { | ||
// default include filter that accepts all classes | ||
addIncludeFilter(new TypeFilter() { | ||
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { | ||
return true; | ||
} | ||
}); | ||
} | ||
|
||
// exclude package-info.java | ||
addExcludeFilter(new TypeFilter() { | ||
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { | ||
String className = metadataReader.getClassMetadata().getClassName(); | ||
return className.endsWith("package-info"); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Calls the parent search that will search and register all the candidates. Then the | ||
* registered objects are post processed to set them as MapperFactoryBeans | ||
*/ | ||
@Override | ||
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { | ||
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); | ||
|
||
if (beanDefinitions.isEmpty()) { | ||
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) | ||
+ "' package. Please check your configuration."); | ||
} else { | ||
for (BeanDefinitionHolder holder : beanDefinitions) { | ||
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition(); | ||
|
||
if (logger.isDebugEnabled()) { | ||
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() | ||
+ "' and '" + definition.getBeanClassName() + "' mapperInterface"); | ||
} | ||
|
||
// the mapper interface is the original class of the bean | ||
// but, the actual class of the bean is MapperFactoryBean | ||
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName()); | ||
definition.setBeanClass(MapperFactoryBean.class); | ||
|
||
definition.getPropertyValues().add("addToConfig", this.addToConfig); | ||
|
||
boolean explicitFactoryUsed = false; | ||
if (StringUtils.hasLength(this.sqlSessionFactoryBeanName)) { | ||
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); | ||
explicitFactoryUsed = true; | ||
} else if (this.sqlSessionFactory != null) { | ||
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); | ||
explicitFactoryUsed = true; | ||
} | ||
|
||
if (StringUtils.hasLength(this.sqlSessionTemplateBeanName)) { | ||
if (explicitFactoryUsed) { | ||
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); | ||
} | ||
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); | ||
definition.getPropertyValues().add("sqlSessionFactory", null); | ||
} else if (this.sqlSessionTemplate != null) { | ||
if (explicitFactoryUsed) { | ||
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); | ||
} | ||
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); | ||
definition.getPropertyValues().add("sqlSessionFactory", null); | ||
} | ||
} | ||
} | ||
|
||
return beanDefinitions; | ||
} | ||
|
||
@Override | ||
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { | ||
return (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent()); | ||
} | ||
|
||
@Override | ||
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { | ||
if (super.checkCandidate(beanName, beanDefinition)) { | ||
return true; | ||
} else { | ||
logger.warn("Skipping MapperFactoryBean with name '" + beanName | ||
+ "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface" | ||
+ ". Bean already defined with the same name!"); | ||
return false; | ||
} | ||
} | ||
} |
Oops, something went wrong.