From 20c5cb6f8d82ebabc2cf7456969dcfc8e361e6a0 Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Tue, 23 Oct 2018 01:22:29 -0400 Subject: [PATCH] Add SpringRepository and SpringScope annotations for generating JdbcMapper implementations with spring annotations --- .../jdbc/codegen/JdbcMapperProcessor.java | 47 +++++++++++++++++++ .../jdbc/codegen/spring/SpringRepository.java | 21 +++++++++ .../jdbc/codegen/spring/SpringScope.java | 32 +++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringRepository.java create mode 100644 jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringScope.java diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java index d1b7352..e8bb027 100644 --- a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java @@ -1,6 +1,8 @@ package com.moparisthebest.jdbc.codegen; import com.moparisthebest.jdbc.*; +import com.moparisthebest.jdbc.codegen.spring.SpringRepository; +import com.moparisthebest.jdbc.codegen.spring.SpringScope; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; @@ -10,6 +12,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import java.io.*; +import java.lang.annotation.Annotation; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; @@ -49,6 +52,7 @@ public class JdbcMapperProcessor extends AbstractProcessor { } static Types types; + static Elements elements; static Messager messager; public static Types getTypes() { @@ -88,6 +92,7 @@ public class JdbcMapperProcessor extends AbstractProcessor { java8 = RELEASE_8 != null && processingEnv.getSourceVersion().ordinal() >= RELEASE_8.ordinal(); types = processingEnv.getTypeUtils(); + elements = processingEnv.getElementUtils(); messager = processingEnv.getMessager(); final Elements elements = processingEnv.getElementUtils(); sqlExceptionType = elements.getTypeElement(SQLException.class.getCanonicalName()).asType(); @@ -227,6 +232,26 @@ public class JdbcMapperProcessor extends AbstractProcessor { w.write("import java.sql.*;\n\n"); w.write("import static com.moparisthebest.jdbc.util.ResultSetUtil.*;\n"); w.write("import static com.moparisthebest.jdbc.TryClose.tryClose;\n\n"); + + final SpringRepository springRepository = getLowestClassOrPackageAnnotation(genClass, SpringRepository.class); + if(springRepository != null) { + w.write("@org.springframework.stereotype.Repository"); + if(!springRepository.value().isEmpty()) + w.append("(\"").append(escapeJavaString(springRepository.value())).append("\")"); + w.write("\n"); + } + + final SpringScope springScope = getLowestClassOrPackageAnnotation(genClass, SpringScope.class); + if(springScope != null) { + w.write("@org.springframework.context.annotation.Scope("); + if(!springScope.scopeName().isEmpty()) + w.append("\n\tscopeName = \"").append(escapeJavaString(springScope.scopeName())).append("\","); + String proxyMode = springScope.proxyMode(); + if(proxyMode.isEmpty()) + proxyMode = isInterface ? "INTERFACES" : "TARGET_CLASS"; + w.append("\n\tproxyMode = org.springframework.context.annotation.ScopedProxyMode.").append(proxyMode).append(")\n"); + } + w.write("public class "); w.write(className); if (isInterface) { @@ -1084,6 +1109,28 @@ public class JdbcMapperProcessor extends AbstractProcessor { } } + private static final Pattern lastPackage = Pattern.compile("\\.?[^.]+$"); + private static A getLowestClassOrPackageAnnotation(final Element typeElement, final Class annotationType) { + A ret = typeElement.getAnnotation(annotationType); + if(ret != null) + return ret; + PackageElement packageElement = elements.getPackageOf(typeElement); + while(packageElement != null) { + ret = packageElement.getAnnotation(annotationType); + if(ret != null) + return ret; + if(packageElement.isUnnamed()) + break; + packageElement = elements.getPackageElement(lastPackage.matcher(packageElement.getQualifiedName()).replaceFirst("")); + } + return null; + } + + private static String escapeJavaString(final String s) { + return s.replace("\"", "\\\"") + .replace("\n", "\\n"); + } + public static String toString(final Throwable e) { StringWriter sw = null; PrintWriter pw = null; diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringRepository.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringRepository.java new file mode 100644 index 0000000..8bc9e6c --- /dev/null +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringRepository.java @@ -0,0 +1,21 @@ +package com.moparisthebest.jdbc.codegen.spring; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This puts a @org.springframework.stereotype.Repository annotation on the generated class + *

+ * If put on a package, applies to all classes generated under that package, if put on a + * class or interface only applies to that class/interface, most specific class or package wins. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.PACKAGE}) +public @interface SpringRepository { + /** + * Used for @org.springframework.stereotype.Repository.value + */ + String value() default ""; +} diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringScope.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringScope.java new file mode 100644 index 0000000..933e56a --- /dev/null +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/spring/SpringScope.java @@ -0,0 +1,32 @@ +package com.moparisthebest.jdbc.codegen.spring; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This puts a @org.springframework.context.annotation.Scope annotation on the generated class + *

+ * If put on a package, applies to all classes generated under that package, if put on a + * class or interface only applies to that class/interface, most specific class or package wins. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.PACKAGE}) +public @interface SpringScope { + /** + * Used for @org.springframework.context.annotation.Scope.scopeName + *

+ * Defaults to "request", which is, as of Spring 5, the value of: + * org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST + */ + String scopeName() default "request"; + + /** + * Used for @org.springframework.context.annotation.Scope.proxyMode + *

+ * If empty string default is set, defaults to setting ScopedProxyMode.INTERFACES + * when implementing an interface, or ScopedProxyMode.TARGET_CLASS otherwise + */ + String proxyMode() default ""; +}