forked from mybatis/spring-boot-starter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Support annotation based on MyBatis for Spring Boot 3 Native
- Loading branch information
1 parent
319241a
commit e399cbb
Showing
23 changed files
with
1,164 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# | ||
# Copyright 2015-2024 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. | ||
# | ||
|
||
name: Spring Boot Native Support Samples | ||
|
||
on: [ push, pull_request ] | ||
|
||
jobs: | ||
test-ubuntu: | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
os: [ ubuntu-latest, macOS-latest ] | ||
java: [ 17.0.9, 21.0.2, 22.0.2 ] | ||
fail-fast: false | ||
max-parallel: 5 | ||
name: Test GraalVM JDK ${{ matrix.java }}, ${{ matrix.os }} | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Ubuntu Set up JDK | ||
if: ${{ matrix.os == 'ubuntu-latest' }} | ||
run: | | ||
JAVA_HOME=$RUNNER_WORKSPACE/.graalvm | ||
echo $JAVA_HOME | ||
mkdir -p $JAVA_HOME | ||
echo "JAVA_HOME=$JAVA_HOME" >> $GITHUB_ENV | ||
curl -L -o graalvm.tar.gz https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${{ matrix.java }}/graalvm-community-jdk-${{ matrix.java }}_linux-x64_bin.tar.gz | ||
tar -zxvf graalvm.tar.gz -C $JAVA_HOME --strip-components=1 | ||
ls -lh $JAVA_HOME | ||
mvn -v | ||
- name: MacOS Set up JDK | ||
if: ${{ matrix.os == 'macOS-latest' }} | ||
run: | | ||
JAVA_HOME=$RUNNER_WORKSPACE/.graalvm | ||
echo $JAVA_HOME | ||
mkdir -p $JAVA_HOME | ||
curl -L -o graalvm.tar.gz https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${{ matrix.java }}/graalvm-community-jdk-${{ matrix.java }}_macos-x64_bin.tar.gz | ||
tar -zxvf graalvm.tar.gz -C $JAVA_HOME --strip-components=1 | ||
JAVA_HOME=$RUNNER_WORKSPACE/.graalvm/Contents/Home | ||
echo $JAVA_HOME | ||
echo "JAVA_HOME=$JAVA_HOME" >> $GITHUB_ENV | ||
ls -lh $JAVA_HOME | ||
mvn -v | ||
- name: Ubuntu Prerequisites | ||
if: ${{ matrix.os == 'ubuntu-latest' }} | ||
run: sudo apt-get install build-essential zlib1g-dev | ||
|
||
#- name: MacOS Prerequisites | ||
# if: ${{ matrix.os == 'macOS-latest' }} | ||
# run: xcode-select --install | ||
|
||
- name: Test with Spring Boot Native Latest | ||
run: ./mvnw -V compile -Pnative native:compile -am -pl mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation | ||
|
||
- name: Run Native Latest | ||
run: mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/target/mybatis-spring-boot-sample-graalvm-annotation |
138 changes: 138 additions & 0 deletions
138
...a/org/mybatis/spring/boot/autoconfigure/MyBatisBeanFactoryInitializationAotProcessor.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,138 @@ | ||
/* | ||
* Copyright 2015-2024 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 | ||
* | ||
* https://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.mybatis.spring.boot.autoconfigure; | ||
|
||
import java.lang.reflect.Method; | ||
import java.lang.reflect.ParameterizedType; | ||
import java.lang.reflect.Type; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import org.apache.ibatis.reflection.TypeParameterResolver; | ||
import org.mybatis.spring.mapper.MapperFactoryBean; | ||
import org.mybatis.spring.mapper.MapperScannerConfigurer; | ||
import org.springframework.aot.hint.MemberCategory; | ||
import org.springframework.aot.hint.RuntimeHints; | ||
import org.springframework.beans.PropertyValue; | ||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; | ||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; | ||
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; | ||
import org.springframework.beans.factory.config.BeanDefinition; | ||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | ||
import org.springframework.beans.factory.support.RegisteredBean; | ||
import org.springframework.util.ReflectionUtils; | ||
|
||
/** | ||
* @since 3.0.4 | ||
*/ | ||
class MyBatisBeanFactoryInitializationAotProcessor | ||
implements BeanFactoryInitializationAotProcessor, BeanRegistrationExcludeFilter { | ||
|
||
private static final Set<Class<?>> EXCLUDE_CLASSES = new HashSet<>(); | ||
|
||
static { | ||
EXCLUDE_CLASSES.add(MapperScannerConfigurer.class); | ||
} | ||
|
||
@Override | ||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { | ||
String[] beanNames = beanFactory.getBeanNamesForType(MapperFactoryBean.class); | ||
if (beanNames.length == 0) { | ||
return null; | ||
} | ||
return (context, code) -> { | ||
RuntimeHints hints = context.getRuntimeHints(); | ||
for (String beanName : beanNames) { | ||
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName.substring(1)); | ||
PropertyValue mapperInterface = beanDefinition.getPropertyValues().getPropertyValue("mapperInterface"); | ||
if (mapperInterface != null && mapperInterface.getValue() != null) { | ||
Class<?> mapperInterfaceType = (Class<?>) mapperInterface.getValue(); | ||
if (mapperInterfaceType != null) { | ||
registerReflectionTypeIfNecessary(mapperInterfaceType, hints); | ||
hints.proxies().registerJdkProxy(mapperInterfaceType); | ||
registerMapperRelationships(mapperInterfaceType, hints); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
@Override | ||
public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { | ||
return EXCLUDE_CLASSES.contains(registeredBean.getBeanClass()); | ||
} | ||
|
||
private void registerMapperRelationships(Class<?> mapperInterfaceType, RuntimeHints hints) { | ||
Method[] methods = ReflectionUtils.getAllDeclaredMethods(mapperInterfaceType); | ||
for (Method method : methods) { | ||
if (method.getDeclaringClass() != Object.class) { | ||
ReflectionUtils.makeAccessible(method); | ||
Class<?> returnType = MyBatisMapperTypeUtils.resolveReturnClass(mapperInterfaceType, method); | ||
registerReflectionTypeIfNecessary(returnType, hints); | ||
MyBatisMapperTypeUtils.resolveParameterClasses(mapperInterfaceType, method) | ||
.forEach(x -> registerReflectionTypeIfNecessary(x, hints)); | ||
} | ||
} | ||
} | ||
|
||
static class MyBatisMapperTypeUtils { | ||
private MyBatisMapperTypeUtils() { | ||
// NOP | ||
} | ||
|
||
static Class<?> resolveReturnClass(Class<?> mapperInterface, Method method) { | ||
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); | ||
return typeToClass(resolvedReturnType, method.getReturnType()); | ||
} | ||
|
||
static Set<Class<?>> resolveParameterClasses(Class<?> mapperInterface, Method method) { | ||
return Stream.of(TypeParameterResolver.resolveParamTypes(method, mapperInterface)) | ||
.map(x -> typeToClass(x, x instanceof Class ? (Class<?>) x : Object.class)).collect(Collectors.toSet()); | ||
} | ||
|
||
private static Class<?> typeToClass(Type src, Class<?> fallback) { | ||
Class<?> result = null; | ||
if (src instanceof Class<?>) { | ||
if (((Class<?>) src).isArray()) { | ||
result = ((Class<?>) src).getComponentType(); | ||
} else { | ||
result = (Class<?>) src; | ||
} | ||
} else if (src instanceof ParameterizedType parameterizedType) { | ||
int index = (parameterizedType.getRawType() instanceof Class | ||
&& Map.class.isAssignableFrom((Class<?>) parameterizedType.getRawType()) | ||
&& parameterizedType.getActualTypeArguments().length > 1) ? 1 : 0; | ||
Type actualType = parameterizedType.getActualTypeArguments()[index]; | ||
result = typeToClass(actualType, fallback); | ||
} | ||
if (result == null) { | ||
result = fallback; | ||
} | ||
return result; | ||
} | ||
|
||
} | ||
|
||
private void registerReflectionTypeIfNecessary(Class<?> type, RuntimeHints hints) { | ||
if (!type.isPrimitive() && !type.getName().startsWith("java")) { | ||
hints.reflection().registerType(type, MemberCategory.values()); | ||
} | ||
} | ||
|
||
} |
87 changes: 87 additions & 0 deletions
87
...ain/java/org/mybatis/spring/boot/autoconfigure/MyBatisMapperFactoryBeanPostProcessor.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,87 @@ | ||
/* | ||
* Copyright 2015-2024 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 | ||
* | ||
* https://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.mybatis.spring.boot.autoconfigure; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.mybatis.spring.mapper.MapperFactoryBean; | ||
import org.springframework.beans.factory.BeanFactory; | ||
import org.springframework.beans.factory.BeanFactoryAware; | ||
import org.springframework.beans.factory.config.ConfigurableBeanFactory; | ||
import org.springframework.beans.factory.config.ConstructorArgumentValues; | ||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; | ||
import org.springframework.beans.factory.support.RootBeanDefinition; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.ResolvableType; | ||
import org.springframework.util.ClassUtils; | ||
|
||
/** | ||
* The {@code BeanDefinitionPostProcessor} for customizing a {@code MapperFactoryBean}. | ||
* | ||
* @author Stéphane Nicoll | ||
* @author Kazuki Shimizu | ||
* @author xuxiaowei | ||
* | ||
* @since 3.0.4 | ||
*/ | ||
@Configuration | ||
class MyBatisMapperFactoryBeanPostProcessor implements MergedBeanDefinitionPostProcessor, BeanFactoryAware { | ||
|
||
private static final Log LOG = LogFactory.getLog(MyBatisMapperFactoryBeanPostProcessor.class); | ||
|
||
private static final String MAPPER_FACTORY_BEAN = MapperFactoryBean.class.getName(); | ||
|
||
private ConfigurableBeanFactory beanFactory; | ||
|
||
@Override | ||
public void setBeanFactory(BeanFactory beanFactory) { | ||
this.beanFactory = (ConfigurableBeanFactory) beanFactory; | ||
} | ||
|
||
@Override | ||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { | ||
if (ClassUtils.isPresent(MAPPER_FACTORY_BEAN, this.beanFactory.getBeanClassLoader())) { | ||
resolveMapperFactoryBeanTypeIfNecessary(beanDefinition); | ||
} | ||
} | ||
|
||
private void resolveMapperFactoryBeanTypeIfNecessary(RootBeanDefinition beanDefinition) { | ||
if (!beanDefinition.hasBeanClass() || !MapperFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())) { | ||
return; | ||
} | ||
if (beanDefinition.getResolvableType().hasUnresolvableGenerics()) { | ||
Class<?> mapperInterface = getMapperInterface(beanDefinition); | ||
if (mapperInterface != null) { | ||
// Exposes a generic type information to context for prevent early initializing | ||
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); | ||
constructorArgumentValues.addGenericArgumentValue(mapperInterface); | ||
beanDefinition.setConstructorArgumentValues(constructorArgumentValues); | ||
beanDefinition | ||
.setTargetType(ResolvableType.forClassWithGenerics(beanDefinition.getBeanClass(), mapperInterface)); | ||
} | ||
} | ||
} | ||
|
||
private Class<?> getMapperInterface(RootBeanDefinition beanDefinition) { | ||
try { | ||
return (Class<?>) beanDefinition.getPropertyValues().get("mapperInterface"); | ||
} catch (Exception e) { | ||
LOG.debug("Fail getting mapper interface type.", e); | ||
return null; | ||
} | ||
} | ||
|
||
} |
14 changes: 14 additions & 0 deletions
14
...e-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/mybatis-reflection.json
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,14 @@ | ||
[ | ||
{ "name": "org.apache.ibatis.logging.slf4j.Slf4jImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.logging.log4j2.Log4j2Impl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.logging.log4j.Log4jImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.logging.nologging.NoLoggingImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.plugin.Interceptor", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.javassist.util.proxy.ProxyFactory", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "org.apache.ibatis.scripting.xmltags.XMLLanguageDriver", "methods": [{ "name": "<init>", "parameterTypes": [] }] }, | ||
{ "name": "org.apache.ibatis.scripting.defaults.RawLanguageDriver", "methods": [{ "name": "<init>", "parameterTypes": [] }] }, | ||
{ "name": "org.mybatis.spring.SqlSessionFactoryBean", "allDeclaredConstructors": true, "allDeclaredMethods": true }, | ||
{ "name": "java.util.ArrayList", "methods": [{ "name": "<init>", "parameterTypes": [] }] } | ||
] |
17 changes: 17 additions & 0 deletions
17
...e-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/native-image.properties
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,17 @@ | ||
# | ||
# Copyright 2015-2024 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 | ||
# | ||
# https://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. | ||
# | ||
|
||
Args = -H:ReflectionConfigurationResources=${.}/mybatis-reflection.json |
2 changes: 2 additions & 0 deletions
2
mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories
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,2 @@ | ||
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ | ||
org.mybatis.spring.boot.autoconfigure.MyBatisBeanFactoryInitializationAotProcessor |
1 change: 1 addition & 0 deletions
1
...esources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
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 |
---|---|---|
@@ -1,2 +1,3 @@ | ||
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration | ||
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration | ||
org.mybatis.spring.boot.autoconfigure.MyBatisMapperFactoryBeanPostProcessor |
13 changes: 13 additions & 0 deletions
13
mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/LICENSE_HEADER
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,13 @@ | ||
Copyright ${license.git.copyrightYears} 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 | ||
|
||
https://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. |
22 changes: 22 additions & 0 deletions
22
mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/format.xml
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,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
Copyright 2015-2024 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 | ||
https://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. | ||
--> | ||
<!DOCTYPE Format> | ||
<Format> | ||
<!-- Dummy format file --> | ||
</Format> |
Oops, something went wrong.