From 0a4eb81722c33253ce4e7cf2ab2bfd4a04122bd5 Mon Sep 17 00:00:00 2001 From: Christian Nguyen Van Than Date: Tue, 29 Jul 2014 18:16:42 +0200 Subject: [PATCH 1/2] add possibility to use custom factory bean on MapperScan annotation --- .../mybatis/spring/annotation/MapperScan.java | 10 ++- .../annotation/MapperScannerRegistrar.java | 5 ++ .../spring/mapper/ClassPathMapperScanner.java | 15 +++-- .../spring/mapper/MapperFactoryBean.java | 27 ++++++-- .../spring/annotation/MapperScanTest.java | 27 +++++++- .../spring/type/DummyMapperFactoryBean.java | 62 +++++++++++++++++++ 6 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 src/test/java/org/mybatis/spring/type/DummyMapperFactoryBean.java diff --git a/src/main/java/org/mybatis/spring/annotation/MapperScan.java b/src/main/java/org/mybatis/spring/annotation/MapperScan.java index a0dfe7577d..d26d824bb1 100644 --- a/src/main/java/org/mybatis/spring/annotation/MapperScan.java +++ b/src/main/java/org/mybatis/spring/annotation/MapperScan.java @@ -61,7 +61,7 @@ * * @author Michael Lanyon * @author Eduardo Macarron - * + * * @since 1.2.0 * @see MapperScannerRegistrar * @see MapperFactoryBean @@ -101,7 +101,7 @@ * within the Spring container. */ Class nameGenerator() default BeanNameGenerator.class; - + /** * This property specifies the annotation that the scanner will search for. *

@@ -136,4 +136,10 @@ */ String sqlSessionFactoryRef() default ""; + /** + * Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean. + * + */ + Class factoryBean() default MapperFactoryBean.class; + } diff --git a/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java b/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java index b6814799f3..85375cf02c 100644 --- a/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java +++ b/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java @@ -79,6 +79,11 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass)); } + Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); + if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { + scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass)); + } + scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef")); scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); diff --git a/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java b/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java index f0ef66b753..ca73bf8c27 100644 --- a/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java +++ b/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java @@ -1,5 +1,5 @@ -/** - * Copyright 2010-2015 the original author or authors. +/* + * Copyright 2010-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. @@ -70,6 +70,8 @@ public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { private Class markerInterface; + private MapperFactoryBean mapperFactoryBean = new MapperFactoryBean(); + public ClassPathMapperScanner(BeanDefinitionRegistry registry) { super(registry, false); } @@ -102,6 +104,11 @@ public void setSqlSessionFactoryBeanName(String sqlSessionFactoryBeanName) { this.sqlSessionFactoryBeanName = sqlSessionFactoryBeanName; } + public void setMapperFactoryBean(MapperFactoryBean mapperFactoryBean) { + this.mapperFactoryBean = (mapperFactoryBean != null ? mapperFactoryBean : new MapperFactoryBean()); + } + + /** * 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 @@ -177,8 +184,8 @@ private void processBeanDefinitions(Set beanDefinitions) { // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean - definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); - definition.setBeanClass(MapperFactoryBean.class); + definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName()); + definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); diff --git a/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java b/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java index e28df68a5c..4155171d23 100644 --- a/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java +++ b/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java @@ -1,5 +1,5 @@ -/** - * Copyright 2010-2015 the original author or authors. +/* + * Copyright 2010-2012 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. @@ -66,7 +66,7 @@ public MapperFactoryBean(Class mapperInterface) { public MapperFactoryBean() { } - /** + /** * Sets the mapper interface of the MyBatis mapper * * @param mapperInterface class of the interface @@ -115,7 +115,6 @@ protected void checkDaoConfig() { /** * {@inheritDoc} */ - @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } @@ -123,7 +122,6 @@ public T getObject() throws Exception { /** * {@inheritDoc} */ - @Override public Class getObjectType() { return this.mapperInterface; } @@ -131,9 +129,26 @@ public Class getObjectType() { /** * {@inheritDoc} */ - @Override public boolean isSingleton() { return true; } + //------------- mutators -------------- + + /** + * Return the mapper interface of the MyBatis mapper + * @return class of the interface + */ + public Class getMapperInterface() { + return mapperInterface; + } + + /** + * Return the flag for addition into MyBatis config. + * @return true if the mapper will be added to MyBatis in the case it is not already + * registered. + */ + public boolean isAddToConfig() { + return addToConfig; + } } diff --git a/src/test/java/org/mybatis/spring/annotation/MapperScanTest.java b/src/test/java/org/mybatis/spring/annotation/MapperScanTest.java index c2be0ead3d..7b89972340 100644 --- a/src/test/java/org/mybatis/spring/annotation/MapperScanTest.java +++ b/src/test/java/org/mybatis/spring/annotation/MapperScanTest.java @@ -15,9 +15,6 @@ */ package org.mybatis.spring.annotation; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.fail; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -27,6 +24,7 @@ import org.mybatis.spring.mapper.MapperInterface; import org.mybatis.spring.mapper.MapperSubinterface; import org.mybatis.spring.mapper.child.MapperChildInterface; +import org.mybatis.spring.type.DummyMapperFactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; @@ -39,6 +37,8 @@ import com.mockrunner.mock.jdbc.MockDataSource; +import static org.junit.Assert.*; + /** * Test for the MapperScannerRegistrar. *

@@ -162,6 +162,22 @@ public void testMarkerInterfaceAndAnnotationScan() { assertBeanNotLoaded("mapperInterface"); } + @Test + public void testCustomMapperFactoryBean() { + applicationContext.register(AppConfigWithCustomMapperFactoryBean.class); + + startContext(); + + // all interfaces with methods should be loaded + applicationContext.getBean("mapperInterface"); + applicationContext.getBean("mapperSubinterface"); + applicationContext.getBean("mapperChildInterface"); + applicationContext.getBean("annotatedMapper"); + + assertTrue(DummyMapperFactoryBean.getMapperCount() > 0); + + } + @Test public void testScanWithNameConflict() { GenericBeanDefinition definition = new GenericBeanDefinition(); @@ -265,6 +281,11 @@ public static class AppConfigWithSqlSessionFactory { public static class AppConfigWithNameGenerator { } + @Configuration + @MapperScan(basePackages = "org.mybatis.spring.mapper", factoryBean = DummyMapperFactoryBean.class) + public static class AppConfigWithCustomMapperFactoryBean { + } + public static class BeanNameGenerator implements org.springframework.beans.factory.support.BeanNameGenerator { public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry definitionRegistry) { diff --git a/src/test/java/org/mybatis/spring/type/DummyMapperFactoryBean.java b/src/test/java/org/mybatis/spring/type/DummyMapperFactoryBean.java new file mode 100644 index 0000000000..fec9ec1c06 --- /dev/null +++ b/src/test/java/org/mybatis/spring/type/DummyMapperFactoryBean.java @@ -0,0 +1,62 @@ +package org.mybatis.spring.type; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.log4j.Logger; +import org.mybatis.spring.mapper.MapperFactoryBean; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.concurrent.atomic.AtomicInteger; + +public class DummyMapperFactoryBean extends MapperFactoryBean { + + private static final Logger LOGGER = Logger.getLogger(DummyMapperFactoryBean.class); + + private static final AtomicInteger mapperInstanceCount = new AtomicInteger(0); + + @Override + protected void checkDaoConfig() { + super.checkDaoConfig(); + // make something more + if (isAddToConfig()) { + LOGGER.debug("register mapper for interface : " + getMapperInterface()); + + } + } + + @Override + public T getObject() throws Exception { + MapperFactoryBean mapperFactoryBean = new MapperFactoryBean(); + mapperFactoryBean.setMapperInterface(getMapperInterface()); + mapperFactoryBean.setAddToConfig(isAddToConfig()); + mapperFactoryBean.setSqlSessionFactory(getCustomSessionFactoryForClass(getMapperInterface())); + T object = mapperFactoryBean.getObject(); + mapperInstanceCount.incrementAndGet(); + return object; + } + + + + private SqlSessionFactory getCustomSessionFactoryForClass(Class mapperClass) { + // can for example read a custom annotation to set a custom sqlSessionFactory + + // just a dummy implementation example + return (SqlSessionFactory) Proxy.newProxyInstance( + SqlSessionFactory.class.getClassLoader(), + new Class[]{SqlSessionFactory.class}, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("getConfiguration".equals(method.getName())) { + return getSqlSession().getConfiguration(); + } + // dummy + return null; + } + }); + } + + public static final int getMapperCount(){ + return mapperInstanceCount.get(); + } +} From 2609ecfe2e8af7e6bda12e6209ae07e102b74241 Mon Sep 17 00:00:00 2001 From: Christian Nguyen Van Than Date: Tue, 26 May 2015 14:37:19 +0200 Subject: [PATCH 2/2] fix copyright date and rearrange mutators --- .../spring/mapper/ClassPathMapperScanner.java | 2 +- .../spring/mapper/MapperFactoryBean.java | 54 ++++++++++--------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java b/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java index ca73bf8c27..089a4ab9ad 100644 --- a/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java +++ b/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 the original author or authors. + * Copyright 2010-2015 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. diff --git a/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java b/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java index 4155171d23..ef9304a142 100644 --- a/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java +++ b/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 the original author or authors. + * Copyright 2010-2015 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. @@ -48,7 +48,7 @@ * Note that this factory can only inject interfaces, not concrete classes. * * @author Eduardo Macarron - * + * * @see SqlSessionTemplate * @version $Id$ */ @@ -66,30 +66,6 @@ public MapperFactoryBean(Class mapperInterface) { public MapperFactoryBean() { } - /** - * Sets the mapper interface of the MyBatis mapper - * - * @param mapperInterface class of the interface - */ - public void setMapperInterface(Class mapperInterface) { - this.mapperInterface = mapperInterface; - } - - /** - * If addToConfig is false the mapper will not be added to MyBatis. This means - * it must have been included in mybatis-config.xml. - *

- * If it is true, the mapper will be added to MyBatis in the case it is not already - * registered. - *

- * By default addToCofig is true. - * - * @param addToConfig - */ - public void setAddToConfig(boolean addToConfig) { - this.addToConfig = addToConfig; - } - /** * {@inheritDoc} */ @@ -135,16 +111,42 @@ public boolean isSingleton() { //------------- mutators -------------- + /** + * Sets the mapper interface of the MyBatis mapper + * + * @param mapperInterface class of the interface + */ + public void setMapperInterface(Class mapperInterface) { + this.mapperInterface = mapperInterface; + } + /** * Return the mapper interface of the MyBatis mapper + * * @return class of the interface */ public Class getMapperInterface() { return mapperInterface; } + /** + * If addToConfig is false the mapper will not be added to MyBatis. This means + * it must have been included in mybatis-config.xml. + *

+ * If it is true, the mapper will be added to MyBatis in the case it is not already + * registered. + *

+ * By default addToCofig is true. + * + * @param addToConfig + */ + public void setAddToConfig(boolean addToConfig) { + this.addToConfig = addToConfig; + } + /** * Return the flag for addition into MyBatis config. + * * @return true if the mapper will be added to MyBatis in the case it is not already * registered. */