Skip to content

Commit 6b4c5e8

Browse files
authored
Merge pull request #484 from kazuki43zoo/gh-476_support-scoped-proxy
Support scoped proxy on mapper scan feature
2 parents 3f7274e + 374a62e commit 6b4c5e8

15 files changed

+401
-35
lines changed

src/main/java/org/mybatis/spring/annotation/MapperScan.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@
2525

2626
import org.mybatis.spring.mapper.MapperFactoryBean;
2727
import org.mybatis.spring.mapper.MapperScannerConfigurer;
28+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
2829
import org.springframework.beans.factory.support.BeanNameGenerator;
2930
import org.springframework.context.annotation.Import;
3031

3132
/**
3233
* Use this annotation to register MyBatis mapper interfaces when using Java Config. It performs when same work as
3334
* {@link MapperScannerConfigurer} via {@link MapperScannerRegistrar}.
34-
*
35+
*
3536
* <p>
3637
* Either {@link #basePackageClasses} or {@link #basePackages} (or its alias {@link #value}) may be specified to define
3738
* specific packages to scan. Since 2.0.4, If specific packages are not defined, scanning will occur from the package of
@@ -40,7 +41,7 @@
4041
* <p>
4142
* Configuration example:
4243
* </p>
43-
*
44+
*
4445
* <pre class="code">
4546
* &#064;Configuration
4647
* &#064;MapperScan("org.mybatis.spring.sample.mapper")
@@ -165,10 +166,21 @@
165166
* <p>
166167
* Default is {@code false}.
167168
* </p>
168-
*
169+
*
169170
* @return set {@code true} to enable lazy initialization
170171
* @since 2.0.2
171172
*/
172173
String lazyInitialization() default "";
173174

175+
/**
176+
* Specifies the default scope of scanned mappers.
177+
*
178+
* <p>
179+
* Default is {@code ""} (equiv to singleton).
180+
* </p>
181+
*
182+
* @return the default scope
183+
*/
184+
String defaultScope() default AbstractBeanDefinition.SCOPE_DEFAULT;
185+
174186
}

src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.mybatis.spring.mapper.MapperFactoryBean;
2626
import org.mybatis.spring.mapper.MapperScannerConfigurer;
2727
import org.springframework.beans.BeanUtils;
28+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
2829
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2930
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3031
import org.springframework.beans.factory.support.BeanNameGenerator;
@@ -53,7 +54,7 @@ public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, Re
5354

5455
/**
5556
* {@inheritDoc}
56-
*
57+
*
5758
* @deprecated Since 2.0.2, this method not used never.
5859
*/
5960
@Override
@@ -130,6 +131,11 @@ void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes a
130131
builder.addPropertyValue("lazyInitialization", lazyInitialization);
131132
}
132133

134+
String defaultScope = annoAttrs.getString("defaultScope");
135+
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
136+
builder.addPropertyValue("defaultScope", defaultScope);
137+
}
138+
133139
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
134140

135141
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
@@ -146,7 +152,7 @@ private static String getDefaultBasePackage(AnnotationMetadata importingClassMet
146152

147153
/**
148154
* A {@link MapperScannerRegistrar} for {@link MapperScans}.
149-
*
155+
*
150156
* @since 2.0.0
151157
*/
152158
static class RepeatingRegistrar extends MapperScannerRegistrar {

src/main/java/org/mybatis/spring/config/MapperScannerBeanDefinitionParser.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2010-2019 the original author or authors.
2+
* Copyright 2010-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@
1717

1818
import java.lang.annotation.Annotation;
1919

20-
import org.mybatis.spring.mapper.MapperFactoryBean;
2120
import org.mybatis.spring.mapper.ClassPathMapperScanner;
21+
import org.mybatis.spring.mapper.MapperFactoryBean;
2222
import org.mybatis.spring.mapper.MapperScannerConfigurer;
2323
import org.springframework.beans.BeanUtils;
2424
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -33,7 +33,7 @@
3333

3434
/**
3535
* A {#code BeanDefinitionParser} that handles the element scan of the MyBatis. namespace
36-
*
36+
*
3737
* @author Lishu Luo
3838
* @author Eduardo Macarron
3939
*
@@ -53,10 +53,11 @@ public class MapperScannerBeanDefinitionParser extends AbstractBeanDefinitionPar
5353
private static final String ATTRIBUTE_FACTORY_REF = "factory-ref";
5454
private static final String ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS = "mapper-factory-bean-class";
5555
private static final String ATTRIBUTE_LAZY_INITIALIZATION = "lazy-initialization";
56+
private static final String ATTRIBUTE_DEFAULT_SCOPE = "default-scope";
5657

5758
/**
5859
* {@inheritDoc}
59-
*
60+
*
6061
* @since 2.0.2
6162
*/
6263
@Override
@@ -100,14 +101,15 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa
100101
builder.addPropertyValue("sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF));
101102
builder.addPropertyValue("sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF));
102103
builder.addPropertyValue("lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION));
104+
builder.addPropertyValue("defaultScope", element.getAttribute(ATTRIBUTE_DEFAULT_SCOPE));
103105
builder.addPropertyValue("basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE));
104106

105107
return builder.getBeanDefinition();
106108
}
107109

108110
/**
109111
* {@inheritDoc}
110-
*
112+
*
111113
* @since 2.0.2
112114
*/
113115
@Override

src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java

+56-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2010-2019 the original author or authors.
2+
* Copyright 2010-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,26 +15,30 @@
1515
*/
1616
package org.mybatis.spring.mapper;
1717

18+
import java.lang.annotation.Annotation;
19+
import java.util.Arrays;
20+
import java.util.Optional;
21+
import java.util.Set;
22+
1823
import org.apache.ibatis.session.SqlSessionFactory;
1924
import org.mybatis.logging.Logger;
2025
import org.mybatis.logging.LoggerFactory;
2126
import org.mybatis.spring.SqlSessionTemplate;
27+
import org.springframework.aop.scope.ScopedProxyFactoryBean;
28+
import org.springframework.aop.scope.ScopedProxyUtils;
2229
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
2330
import org.springframework.beans.factory.config.BeanDefinition;
2431
import org.springframework.beans.factory.config.BeanDefinitionHolder;
32+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
2533
import org.springframework.beans.factory.config.RuntimeBeanReference;
2634
import org.springframework.beans.factory.support.AbstractBeanDefinition;
2735
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
28-
import org.springframework.beans.factory.support.GenericBeanDefinition;
36+
import org.springframework.beans.factory.support.RootBeanDefinition;
2937
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
3038
import org.springframework.core.type.filter.AnnotationTypeFilter;
3139
import org.springframework.core.type.filter.AssignableTypeFilter;
3240
import org.springframework.util.StringUtils;
3341

34-
import java.lang.annotation.Annotation;
35-
import java.util.Arrays;
36-
import java.util.Set;
37-
3842
/**
3943
* A {@link ClassPathBeanDefinitionScanner} that registers Mappers by {@code basePackage}, {@code annotationClass}, or
4044
* {@code markerInterface}. If an {@code annotationClass} and/or {@code markerInterface} is specified, only the
@@ -45,7 +49,7 @@
4549
*
4650
* @author Hunter Presnall
4751
* @author Eduardo Macarron
48-
*
52+
*
4953
* @see MapperFactoryBean
5054
* @since 1.2.0
5155
*/
@@ -71,6 +75,8 @@ public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
7175

7276
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
7377

78+
private String defaultScope;
79+
7480
public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
7581
super(registry, false);
7682
}
@@ -136,6 +142,20 @@ public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperF
136142
this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
137143
}
138144

145+
/**
146+
* Set the default scope of scanned mappers.
147+
* <p>
148+
* Default is {@code null} (equiv to singleton).
149+
* </p>
150+
*
151+
* @param defaultScope
152+
* the scope
153+
* @since 2.0.6
154+
*/
155+
public void setDefaultScope(String defaultScope) {
156+
this.defaultScope = defaultScope;
157+
}
158+
139159
/**
140160
* Configures parent scanner to search for the right interfaces. It can search for all interfaces or just for those
141161
* that extends a markerInterface or/and those annotated with the annotationClass
@@ -191,9 +211,18 @@ public Set<BeanDefinitionHolder> doScan(String... basePackages) {
191211
}
192212

193213
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
194-
GenericBeanDefinition definition;
214+
AbstractBeanDefinition definition;
215+
BeanDefinitionRegistry registry = getRegistry();
195216
for (BeanDefinitionHolder holder : beanDefinitions) {
196-
definition = (GenericBeanDefinition) holder.getBeanDefinition();
217+
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
218+
boolean scopedProxy = false;
219+
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
220+
definition = (AbstractBeanDefinition) Optional
221+
.ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
222+
.map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
223+
"The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
224+
scopedProxy = true;
225+
}
197226
String beanClassName = definition.getBeanClassName();
198227
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
199228
+ "' mapperInterface");
@@ -236,7 +265,25 @@ private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
236265
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
237266
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
238267
}
268+
239269
definition.setLazyInit(lazyInitialization);
270+
271+
if (scopedProxy) {
272+
continue;
273+
}
274+
275+
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
276+
definition.setScope(defaultScope);
277+
}
278+
279+
if (!definition.isSingleton()) {
280+
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
281+
if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
282+
registry.removeBeanDefinition(proxyHolder.getBeanName());
283+
}
284+
registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
285+
}
286+
240287
}
241288
}
242289

src/main/java/org/mybatis/spring/mapper/MapperScannerConfigurer.java

+21
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ public class MapperScannerConfigurer
119119

120120
private BeanNameGenerator nameGenerator;
121121

122+
private String defaultScope;
123+
122124
/**
123125
* This property lets you set the base package for your mapper interface files.
124126
* <p>
@@ -311,6 +313,20 @@ public void setNameGenerator(BeanNameGenerator nameGenerator) {
311313
this.nameGenerator = nameGenerator;
312314
}
313315

316+
/**
317+
* Sets the default scope of scanned mappers.
318+
* <p>
319+
* Default is {@code null} (equiv to singleton).
320+
* </p>
321+
*
322+
* @param defaultScope
323+
* the default scope
324+
* @since 2.0.6
325+
*/
326+
public void setDefaultScope(String defaultScope) {
327+
this.defaultScope = defaultScope;
328+
}
329+
314330
/**
315331
* {@inheritDoc}
316332
*/
@@ -352,6 +368,9 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
352368
if (StringUtils.hasText(lazyInitialization)) {
353369
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
354370
}
371+
if (StringUtils.hasText(defaultScope)) {
372+
scanner.setDefaultScope(defaultScope);
373+
}
355374
scanner.registerFilters();
356375
scanner.scan(
357376
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
@@ -387,6 +406,7 @@ private void processPropertyPlaceHolders() {
387406
this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
388407
this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
389408
this.lazyInitialization = updatePropertyValue("lazyInitialization", values);
409+
this.defaultScope = updatePropertyValue("defaultScope", values);
390410
}
391411
this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);
392412
this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName)
@@ -395,6 +415,7 @@ private void processPropertyPlaceHolders() {
395415
.map(getEnvironment()::resolvePlaceholders).orElse(null);
396416
this.lazyInitialization = Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders)
397417
.orElse(null);
418+
this.defaultScope = Optional.ofNullable(this.defaultScope).map(getEnvironment()::resolvePlaceholders).orElse(null);
398419
}
399420

400421
private Environment getEnvironment() {

src/main/resources/org/mybatis/spring/config/mybatis-spring.xsd

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2010-2019 the original author or authors.
4+
Copyright 2010-2020 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -139,6 +139,15 @@
139139
</xsd:documentation>
140140
</xsd:annotation>
141141
</xsd:attribute>
142+
<xsd:attribute name="default-scope" type="xsd:string">
143+
<xsd:annotation>
144+
<xsd:documentation>
145+
<![CDATA[
146+
Specifies the default scope of scanned mappers. (Since 2.0.6)
147+
]]>
148+
</xsd:documentation>
149+
</xsd:annotation>
150+
</xsd:attribute>
142151
</xsd:complexType>
143152
</xsd:element>
144153
</xsd:schema>

0 commit comments

Comments
 (0)