mxd vor 4 Jahren
Ursprung
Commit
77adb93c17
17 geänderte Dateien mit 1781 neuen und 0 gelöschten Zeilen
  1. 204 0
      magic-api-spring-boot-starter/pom.xml
  2. 46 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/CacheConfig.java
  3. 17 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/ClusterConfig.java
  4. 20 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/DebugConfig.java
  5. 574 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicAPIAutoConfiguration.java
  6. 312 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicAPIProperties.java
  7. 91 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicJsonAutoConfiguration.java
  8. 32 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicMongoAutoConfiguration.java
  9. 47 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicRedisAutoConfiguration.java
  10. 99 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicSwaggerConfiguration.java
  11. 59 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/PageConfig.java
  12. 85 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/ResourceConfig.java
  13. 47 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/ResponseCodeConfig.java
  14. 34 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/SecurityConfig.java
  15. 86 0
      magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/SwaggerConfig.java
  16. 27 0
      magic-api-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  17. 1 0
      magic-api-spring-boot-starter/src/main/resources/META-INF/spring.factories

+ 204 - 0
magic-api-spring-boot-starter/pom.xml

@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.ssssssss</groupId>
+    <artifactId>magic-api-spring-boot-starter</artifactId>
+    <version>1.1.3</version>
+    <packaging>jar</packaging>
+    <name>magic-api-spring-boot-starter</name>
+    <description>magic-api-spring-boot-starter</description>
+    <url>https://ssssssss.org/</url>
+    <inceptionYear>2020</inceptionYear>
+    <licenses>
+        <license>
+            <name>The MIT License (MIT)</name>
+            <url>https://github.com/ssssssss-team/magic-api-spring-boot-starter/blob/master/LICENSE</url>
+        </license>
+    </licenses>
+    <developers>
+        <developer>
+            <name>jmxd</name>
+            <email>838425805@qq.com</email>
+        </developer>
+    </developers>
+    <scm>
+        <url>https://gitee.com/ssssssss-team/magic-api-spring-boot-starter</url>
+        <connection>scm:git:https://gitee.com/ssssssss-team/magic-api-spring-boot-starter.git</connection>
+        <developerConnection>scm:git:https://gitee.com/ssssssss-team/magic-api-spring-boot-starter.git
+        </developerConnection>
+    </scm>
+    <properties>
+        <spring-boot.version>2.4.5</spring-boot.version>
+        <swagger.version>2.9.2</swagger.version>
+        <fastjson.version>1.2.75</fastjson.version>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.ssssssss</groupId>
+            <artifactId>magic-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.ssssssss</groupId>
+            <artifactId>magic-editor</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${swagger.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-mongodb</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <!--Compiler-->
+                    <plugin>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <configuration>
+                            <source>1.8</source>
+                            <target>1.8</target>
+                        </configuration>
+                    </plugin>
+                    <!-- Source -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-source-plugin</artifactId>
+                        <version>2.2.1</version>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>jar-no-fork</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-jar-plugin</artifactId>
+                        <configuration>
+                            <archive>
+                                <manifest>
+                                    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                                </manifest>
+                            </archive>
+                        </configuration>
+                    </plugin>
+                    <!-- Java Doc -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <version>3.0.0</version>
+                        <configuration>
+                            <additionalOptions>
+                                <additionalOption>-Xdoclint:none</additionalOption>
+                            </additionalOptions>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <!-- GPG -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>1.5</version>
+                        <executions>
+                            <execution>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+            <distributionManagement>
+                <snapshotRepository>
+                    <id>oss</id>
+                    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+                </snapshotRepository>
+                <repository>
+                    <id>oss</id>
+                    <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
+                </repository>
+            </distributionManagement>
+        </profile>
+    </profiles>
+</project>

+ 46 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/CacheConfig.java

@@ -0,0 +1,46 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+/**
+ * 缓存配置
+ */
+public class CacheConfig {
+
+	/**
+	 * 是否启用缓存
+	 */
+	private boolean enable = false;
+
+	/**
+	 * 默认缓存容量
+	 */
+	private int capacity = 10000;
+
+	/**
+	 * 默认过期时间,单位为毫秒,-1为不过期
+	 */
+	private long ttl = -1;
+
+	public int getCapacity() {
+		return capacity;
+	}
+
+	public void setCapacity(int capacity) {
+		this.capacity = capacity;
+	}
+
+	public long getTtl() {
+		return ttl;
+	}
+
+	public void setTtl(long ttl) {
+		this.ttl = ttl;
+	}
+
+	public boolean isEnable() {
+		return enable;
+	}
+
+	public void setEnable(boolean enable) {
+		this.enable = enable;
+	}
+}

+ 17 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/ClusterConfig.java

@@ -0,0 +1,17 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+public class ClusterConfig {
+
+	/**
+	 * 实例ID,集群环境下,要保证每台机器不同。默认启动后随机生成uuid
+	 */
+	private String instanceId;
+
+	public String getInstanceId() {
+		return instanceId;
+	}
+
+	public void setInstanceId(String instanceId) {
+		this.instanceId = instanceId;
+	}
+}

+ 20 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/DebugConfig.java

@@ -0,0 +1,20 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+/**
+ * Debug配置
+ */
+public class DebugConfig {
+
+	/**
+	 * 断点超时时间
+	 */
+	private int timeout = 60;
+
+	public int getTimeout() {
+		return timeout;
+	}
+
+	public void setTimeout(int timeout) {
+		this.timeout = timeout;
+	}
+}

+ 574 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicAPIAutoConfiguration.java

@@ -0,0 +1,574 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.core.Ordered;
+import org.springframework.core.env.Environment;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.ssssssss.magicapi.adapter.ColumnMapperAdapter;
+import org.ssssssss.magicapi.adapter.DialectAdapter;
+import org.ssssssss.magicapi.adapter.Resource;
+import org.ssssssss.magicapi.adapter.ResourceAdapter;
+import org.ssssssss.magicapi.adapter.resource.DatabaseResource;
+import org.ssssssss.magicapi.cache.DefaultSqlCache;
+import org.ssssssss.magicapi.cache.SqlCache;
+import org.ssssssss.magicapi.config.*;
+import org.ssssssss.magicapi.controller.*;
+import org.ssssssss.magicapi.dialect.Dialect;
+import org.ssssssss.magicapi.exception.MagicAPIException;
+import org.ssssssss.magicapi.interceptor.AuthorizationInterceptor;
+import org.ssssssss.magicapi.interceptor.DefaultAuthorizationInterceptor;
+import org.ssssssss.magicapi.interceptor.RequestInterceptor;
+import org.ssssssss.magicapi.interceptor.SQLInterceptor;
+import org.ssssssss.magicapi.logging.LoggerManager;
+import org.ssssssss.magicapi.model.Constants;
+import org.ssssssss.magicapi.modules.*;
+import org.ssssssss.magicapi.provider.*;
+import org.ssssssss.magicapi.provider.impl.*;
+import org.ssssssss.magicapi.utils.ClassScanner;
+import org.ssssssss.magicapi.utils.Mapping;
+import org.ssssssss.magicapi.utils.PathUtils;
+import org.ssssssss.script.MagicResourceLoader;
+import org.ssssssss.script.MagicScript;
+import org.ssssssss.script.MagicScriptEngine;
+import org.ssssssss.script.functions.ExtensionMethod;
+import org.ssssssss.script.parsing.ast.statement.AsyncCall;
+import org.ssssssss.script.reflection.AbstractReflection;
+import org.ssssssss.script.reflection.JavaReflection;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
+
+@Configuration
+@ConditionalOnClass({RequestMappingHandlerMapping.class})
+@EnableConfigurationProperties(MagicAPIProperties.class)
+@Import({MagicRedisAutoConfiguration.class, MagicMongoAutoConfiguration.class, MagicSwaggerConfiguration.class, MagicJsonAutoConfiguration.class})
+public class MagicAPIAutoConfiguration implements WebMvcConfigurer {
+
+	private static final Logger logger = LoggerFactory.getLogger(MagicAPIAutoConfiguration.class);
+
+	/**
+	 * 请求拦截器
+	 */
+	private final List<RequestInterceptor> requestInterceptors;
+
+	/**
+	 * SQL拦截器
+	 */
+	private final List<SQLInterceptor> sqlInterceptors;
+
+	/**
+	 * 自定义的类型扩展
+	 */
+	private final List<ExtensionMethod> extensionMethods;
+
+	/**
+	 * 内置的消息转换
+	 */
+	private final List<HttpMessageConverter<?>> httpMessageConverters;
+
+	/**
+	 * 自定义的方言
+	 */
+	private final List<Dialect> dialects;
+
+	/**
+	 * 自定义的列名转换
+	 */
+	private final List<ColumnMapperProvider> columnMapperProviders;
+
+
+	private final AuthorizationInterceptor authorizationInterceptor;
+
+	/**
+	 * 自定义的函数
+	 */
+	private final List<MagicFunction> magicFunctions;
+
+	private final Environment environment;
+
+	private final MagicCorsFilter magicCorsFilter = new MagicCorsFilter();
+
+	private final MagicAPIProperties properties;
+
+	private final ApplicationContext applicationContext;
+
+	@Autowired
+	@Lazy
+	private RequestMappingHandlerMapping requestMappingHandlerMapping;
+
+	private final RestTemplate restTemplate;
+
+	private String ALL_CLASS_TXT;
+
+	public MagicAPIAutoConfiguration(MagicAPIProperties properties,
+									 ObjectProvider<List<Dialect>> dialectsProvider,
+									 ObjectProvider<List<RequestInterceptor>> requestInterceptorsProvider,
+									 ObjectProvider<List<SQLInterceptor>> sqlInterceptorsProvider,
+									 ObjectProvider<List<ExtensionMethod>> extensionMethodsProvider,
+									 ObjectProvider<List<HttpMessageConverter<?>>> httpMessageConvertersProvider,
+									 ObjectProvider<List<ColumnMapperProvider>> columnMapperProvidersProvider,
+									 ObjectProvider<List<MagicFunction>> magicFunctionsProvider,
+									 ObjectProvider<RestTemplate> restTemplateProvider,
+									 ObjectProvider<AuthorizationInterceptor> authorizationInterceptorProvider,
+									 Environment environment,
+									 ApplicationContext applicationContext
+									 ) {
+		this.properties = properties;
+		this.dialects = dialectsProvider.getIfAvailable(Collections::emptyList);
+		this.requestInterceptors = requestInterceptorsProvider.getIfAvailable(Collections::emptyList);
+		this.sqlInterceptors = sqlInterceptorsProvider.getIfAvailable(Collections::emptyList);
+		this.extensionMethods = extensionMethodsProvider.getIfAvailable(Collections::emptyList);
+		this.httpMessageConverters = httpMessageConvertersProvider.getIfAvailable(Collections::emptyList);
+		this.columnMapperProviders = columnMapperProvidersProvider.getIfAvailable(Collections::emptyList);
+		this.magicFunctions = magicFunctionsProvider.getIfAvailable(Collections::emptyList);
+		this.restTemplate = restTemplateProvider.getIfAvailable(this::createRestTemplate);
+		this.authorizationInterceptor = authorizationInterceptorProvider.getIfAvailable(this::createAuthorizationInterceptor);
+		this.environment = environment;
+		this.applicationContext = applicationContext;
+	}
+
+	private String redirectIndex(HttpServletRequest request) {
+		if (request.getRequestURI().endsWith("/")) {
+			return "redirect:./index.html";
+		}
+		return "redirect:" + properties.getWeb() + "/index.html";
+	}
+
+	@ResponseBody
+	private MagicAPIProperties readConfig() {
+		return properties;
+	}
+
+	@ResponseBody
+	private String readClass() {
+		if (ALL_CLASS_TXT == null) {
+			try {
+				ALL_CLASS_TXT = StringUtils.join(ClassScanner.scan(), "\r\n");
+			} catch (Throwable t) {
+				logger.warn("扫描Class失败", t);
+				ALL_CLASS_TXT = "";
+			}
+		}
+		return ALL_CLASS_TXT;
+	}
+
+	/**
+	 * 注入动态数据源
+	 */
+	@Bean
+	@ConditionalOnMissingBean(MagicDynamicDataSource.class)
+	public MagicDynamicDataSource magicDynamicDataSource(@Autowired(required = false) DataSource dataSource) {
+		MagicDynamicDataSource dynamicDataSource = new MagicDynamicDataSource();
+		if (dataSource != null) {
+			dynamicDataSource.put(dataSource);
+		} else {
+			logger.warn("当前数据源未配置");
+		}
+		return dynamicDataSource;
+	}
+
+	@Bean
+	@ConditionalOnMissingBean(Resource.class)
+	@ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "database")
+	public Resource magicDatabaseResource(MagicDynamicDataSource magicDynamicDataSource) {
+		ResourceConfig resourceConfig = properties.getResource();
+		MagicDynamicDataSource.DataSourceNode dataSourceNode = magicDynamicDataSource.getDataSource(resourceConfig.getDatasource());
+		if (dataSourceNode == null) {
+			throw new IllegalArgumentException(String.format("找不到数据源:%s", resourceConfig.getDatasource()));
+		}
+		return new DatabaseResource(new JdbcTemplate(dataSourceNode.getDataSource()), resourceConfig.getTableName(), resourceConfig.getPrefix(), resourceConfig.isReadonly());
+	}
+
+	@Bean
+	@ConditionalOnMissingBean(Resource.class)
+	@ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "file", matchIfMissing = true)
+	public Resource magicResource() throws IOException {
+		ResourceConfig resourceConfig = properties.getResource();
+		return ResourceAdapter.getResource(resourceConfig.getLocation(), resourceConfig.isReadonly());
+	}
+
+
+	@Override
+	public void addResourceHandlers(ResourceHandlerRegistry registry) {
+		String web = properties.getWeb();
+		if (web != null) {
+			// 当开启了UI界面时,收集日志
+			LoggerManager.createMagicAppender();
+			// 配置静态资源路径
+			registry.addResourceHandler(web + "/**").addResourceLocations("classpath:/magic-editor/");
+			try {
+				Mapping.create(requestMappingHandlerMapping)
+						// 默认首页设置
+						.register(RequestMappingInfo.paths(web).build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("redirectIndex", HttpServletRequest.class))
+						// 读取配置
+						.register(RequestMappingInfo.paths(web + "/config.json").build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readConfig"))
+						// 读取配置
+						.register(RequestMappingInfo.paths(web + "/classes.txt").produces("text/plain").build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readClass"));
+			} catch (NoSuchMethodException ignored) {
+			}
+		}
+	}
+
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		registry.addInterceptor(new MagicWebRequestInterceptor(properties.isSupportCrossDomain() ? magicCorsFilter : null, authorizationInterceptor))
+				.addPathPatterns("/**");
+	}
+
+
+	@Bean
+	@ConditionalOnProperty(prefix = "magic-api", value = "support-cross-domain", havingValue = "true", matchIfMissing = true)
+	public FilterRegistrationBean<MagicCorsFilter> magicCorsFilterRegistrationBean() {
+		FilterRegistrationBean<MagicCorsFilter> registration = new FilterRegistrationBean<>(magicCorsFilter);
+		registration.addUrlPatterns("/*");
+		registration.setName("Magic Cors Filter");
+		registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
+		return registration;
+	}
+
+	@Bean
+	@ConditionalOnMissingBean(PageProvider.class)
+	public PageProvider pageProvider() {
+		PageConfig pageConfig = properties.getPageConfig();
+		logger.info("未找到分页实现,采用默认分页实现,分页配置:(页码={},页大小={},默认首页={},默认页大小={})", pageConfig.getPage(), pageConfig.getSize(), pageConfig.getDefaultPage(), pageConfig.getDefaultSize());
+		return new DefaultPageProvider(pageConfig.getPage(), pageConfig.getSize(), pageConfig.getDefaultPage(), pageConfig.getDefaultSize());
+	}
+
+	/**
+	 * 注入结果构建方法
+	 */
+	@Bean
+	@ConditionalOnMissingBean(ResultProvider.class)
+	public ResultProvider resultProvider() {
+		return new DefaultResultProvider(properties.getResponse());
+	}
+
+	/**
+	 * 注入SQL缓存实现
+	 */
+	@Bean
+	@ConditionalOnMissingBean(SqlCache.class)
+	public SqlCache sqlCache() {
+		CacheConfig cacheConfig = properties.getCacheConfig();
+		logger.info("未找到SQL缓存实现,采用默认缓存实现(LRU+TTL),缓存配置:(容量={},TTL={})", cacheConfig.getCapacity(), cacheConfig.getTtl());
+		return new DefaultSqlCache(cacheConfig.getCapacity(), cacheConfig.getTtl());
+	}
+
+	/**
+	 * 注入接口映射
+	 */
+	@Bean
+	public MappingHandlerMapping mappingHandlerMapping() throws NoSuchMethodException {
+		String prefix = StringUtils.isNotBlank(properties.getPrefix()) ? PathUtils.replaceSlash("/" + properties.getPrefix() + "/") : null;
+		return new MappingHandlerMapping(prefix, properties.isAllowOverride());
+	}
+
+
+	@Bean
+	@ConditionalOnMissingBean(FunctionServiceProvider.class)
+	public FunctionServiceProvider functionServiceProvider(GroupServiceProvider groupServiceProvider, Resource magicResource) {
+		return new DefaultFunctionServiceProvider(groupServiceProvider, magicResource);
+	}
+
+	/**
+	 * 注入分组存储service
+	 */
+	@Bean
+	@ConditionalOnMissingBean(GroupServiceProvider.class)
+	public GroupServiceProvider groupServiceProvider(Resource magicResource) {
+		return new DefaultGroupServiceProvider(magicResource);
+	}
+
+	/**
+	 * 注入接口存储service
+	 */
+	@Bean
+	@ConditionalOnMissingBean(ApiServiceProvider.class)
+	public ApiServiceProvider apiServiceProvider(GroupServiceProvider groupServiceProvider, Resource magicResource) {
+		return new DefaultApiServiceProvider(groupServiceProvider, magicResource);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean(MagicNotifyService.class)
+	public MagicNotifyService magicNotifyService() {
+		return magicNotify -> {
+		};
+	}
+
+	@Bean
+	public MagicFunctionManager magicFunctionManager(GroupServiceProvider groupServiceProvider, FunctionServiceProvider functionServiceProvider) {
+		return new MagicFunctionManager(groupServiceProvider, functionServiceProvider);
+	}
+
+
+	/**
+	 * 注入API调用Service
+	 */
+	@Bean
+	public MagicAPIService magicAPIService(MappingHandlerMapping mappingHandlerMapping,
+										   ApiServiceProvider apiServiceProvider,
+										   FunctionServiceProvider functionServiceProvider,
+										   GroupServiceProvider groupServiceProvider,
+										   ResultProvider resultProvider,
+										   MagicFunctionManager magicFunctionManager,
+										   MagicNotifyService magicNotifyService) {
+		return new DefaultMagicAPIService(mappingHandlerMapping, apiServiceProvider, functionServiceProvider, groupServiceProvider, resultProvider, magicFunctionManager, magicNotifyService, properties.getClusterConfig().getInstanceId(), properties.isThrowException());
+	}
+
+	private void setupSpringSecurity() {
+		Class<?> clazz = null;
+		try {
+			clazz = Class.forName("org.springframework.security.core.context.SecurityContextHolder");
+		} catch (ClassNotFoundException ignored) {
+		}
+		if (clazz != null) {
+			try {
+				Method method = clazz.getDeclaredMethod("setStrategyName", String.class);
+				method.setAccessible(true);
+				method.invoke(clazz, "MODE_INHERITABLETHREADLOCAL");
+				logger.info("自动适配 Spring Security 成功");
+			} catch (Exception ignored) {
+				logger.info("自动适配 Spring Security 失败");
+			}
+		}
+	}
+
+	/**
+	 * 注入数据库查询模块
+	 */
+	@Bean
+	@ConditionalOnBean({DataSource.class})
+	public SQLModule magicSqlModule(MagicDynamicDataSource dynamicDataSource,
+									ResultProvider resultProvider,
+									PageProvider pageProvider,
+									SqlCache sqlCache) {
+		SQLModule sqlModule = new SQLModule(dynamicDataSource);
+		sqlModule.setResultProvider(resultProvider);
+		sqlModule.setPageProvider(pageProvider);
+		sqlModule.setSqlInterceptors(sqlInterceptors);
+		ColumnMapperAdapter columnMapperAdapter = new ColumnMapperAdapter();
+		this.columnMapperProviders.stream().filter(mapperProvider -> !"default".equals(mapperProvider.name())).forEach(columnMapperAdapter::add);
+		columnMapperAdapter.setDefault(properties.getSqlColumnCase());
+		sqlModule.setColumnMapperProvider(columnMapperAdapter);
+		sqlModule.setColumnMapRowMapper(columnMapperAdapter.getDefaultColumnMapRowMapper());
+		sqlModule.setRowMapColumnMapper(columnMapperAdapter.getDefaultRowMapColumnMapper());
+		sqlModule.setSqlCache(sqlCache);
+		DialectAdapter dialectAdapter = new DialectAdapter();
+		dialects.forEach(dialectAdapter::add);
+		sqlModule.setDialectAdapter(dialectAdapter);
+		return sqlModule;
+	}
+
+	/**
+	 * 注册模块、类型扩展
+	 */
+	private void setupMagicModules(ResultProvider resultProvider,
+								   List<MagicModule> magicModules,
+								   List<ExtensionMethod> extensionMethods,
+								   List<LanguageProvider> languageProviders) {
+		// 设置脚本import时 class加载策略
+		MagicResourceLoader.setClassLoader((className) -> {
+			try {
+				return applicationContext.getBean(className);
+			} catch (Exception e) {
+				Class<?> clazz = null;
+				try {
+					clazz = Class.forName(className);
+					return applicationContext.getBean(clazz);
+				} catch (Exception ex) {
+					return clazz;
+				}
+			}
+		});
+		MagicResourceLoader.addScriptLanguageLoader(language -> languageProviders.stream()
+				.filter(it -> it.support(language))
+				.findFirst().<BiFunction<Map<String, Object>, String, Object>>map(languageProvider -> (context, script) -> {
+					try {
+						return languageProvider.execute(language, script, context);
+					} catch (Exception e) {
+						throw new MagicAPIException(e.getMessage(), e);
+					}
+				}).orElse(null)
+		);
+		logger.info("注册模块:{} -> {}", "log", Logger.class);
+		MagicResourceLoader.addModule("log", LoggerFactory.getLogger(MagicScript.class));
+		List<String> importModules = properties.getAutoImportModuleList();
+		logger.info("注册模块:{} -> {}", "env", EnvModule.class);
+		MagicResourceLoader.addModule("env", new EnvModule(environment));
+		logger.info("注册模块:{} -> {}", "request", RequestModule.class);
+		MagicResourceLoader.addModule("request", new RequestModule());
+		logger.info("注册模块:{} -> {}", "response", ResponseModule.class);
+		MagicResourceLoader.addModule("response", new ResponseModule(resultProvider));
+		logger.info("注册模块:{} -> {}", "assert", AssertModule.class);
+		MagicResourceLoader.addModule("assert", new AssertModule());
+		magicModules.forEach(module -> {
+			logger.info("注册模块:{} -> {}", module.getModuleName(), module.getClass());
+			MagicResourceLoader.addModule(module.getModuleName(), module);
+		});
+		if (MagicResourceLoader.loadModule("http") == null) {
+			logger.info("注册模块:{} -> {}", "http", HttpModule.class);
+			MagicResourceLoader.addModule("http", new HttpModule(this.restTemplate));
+		}
+		MagicResourceLoader.getModuleNames().stream().filter(importModules::contains).forEach(moduleName -> {
+			logger.info("自动导入模块:{}", moduleName);
+			MagicScriptEngine.addDefaultImport(moduleName, MagicResourceLoader.loadModule(moduleName));
+		});
+		properties.getAutoImportPackageList().forEach(importPackage -> {
+			logger.info("自动导包:{}", importPackage);
+			MagicResourceLoader.addPackage(importPackage);
+		});
+		extensionMethods.forEach(extension -> extension.supports().forEach(support -> {
+			logger.info("注册扩展:{} -> {}", support, extension.getClass());
+			AbstractReflection.getInstance().registerMethodExtension(support, extension);
+		}));
+	}
+
+	@Bean
+	public JSR223LanguageProvider jsr223LanguageProvider() {
+		return new JSR223LanguageProvider();
+	}
+
+	@Bean
+	public MagicConfiguration magicConfiguration(List<MagicModule> magicModules,
+												 List<LanguageProvider> languageProviders,
+												 @Autowired(required = false) MagicDynamicDataSource magicDynamicDataSource,
+												 Resource magicResource,
+												 ResultProvider resultProvider,
+												 MagicAPIService magicAPIService,
+												 ApiServiceProvider apiServiceProvider,
+												 GroupServiceProvider groupServiceProvider,
+												 MappingHandlerMapping mappingHandlerMapping,
+												 FunctionServiceProvider functionServiceProvider,
+												 MagicFunctionManager magicFunctionManager) {
+		logger.info("magic-api工作目录:{}", magicResource);
+		setupSpringSecurity();
+		AsyncCall.setThreadPoolExecutorSize(properties.getThreadPoolExecutorSize());
+		// 设置响应结果的code值
+		ResponseCodeConfig responseCodeConfig = properties.getResponseCodeConfig();
+		Constants.RESPONSE_CODE_SUCCESS = responseCodeConfig.getSuccess();
+		Constants.RESPONSE_CODE_INVALID = responseCodeConfig.getInvalid();
+		Constants.RESPONSE_CODE_EXCEPTION = responseCodeConfig.getException();
+		// 设置模块和扩展方法
+		setupMagicModules(resultProvider, magicModules, extensionMethods, languageProviders);
+		MagicConfiguration configuration = new MagicConfiguration();
+		configuration.setMagicAPIService(magicAPIService);
+		configuration.setApiServiceProvider(apiServiceProvider);
+		configuration.setGroupServiceProvider(groupServiceProvider);
+		configuration.setMappingHandlerMapping(mappingHandlerMapping);
+		configuration.setFunctionServiceProvider(functionServiceProvider);
+		SecurityConfig securityConfig = properties.getSecurityConfig();
+		configuration.setUsername(securityConfig.getUsername());
+		configuration.setPassword(securityConfig.getPassword());
+		configuration.setDebugTimeout(properties.getDebugConfig().getTimeout());
+		configuration.setHttpMessageConverters(Optional.ofNullable(httpMessageConverters).orElse(Collections.emptyList()));
+		configuration.setResultProvider(resultProvider);
+		configuration.setThrowException(properties.isThrowException());
+		configuration.setMagicDynamicDataSource(magicDynamicDataSource);
+		configuration.setEditorConfig(properties.getEditorConfig());
+		configuration.setWorkspace(magicResource);
+		configuration.setAuthorizationInterceptor(authorizationInterceptor);
+		// 注册函数
+		this.magicFunctions.forEach(JavaReflection::registerFunction);
+		// 向页面传递配置信息时不传递用户名密码,增强安全性
+		securityConfig.setUsername(null);
+		securityConfig.setPassword(null);
+
+		// 构建UI请求处理器
+		String base = properties.getWeb();
+		mappingHandlerMapping.setRequestMappingHandlerMapping(requestMappingHandlerMapping);
+		MagicDataSourceController dataSourceController = new MagicDataSourceController(configuration);
+		if (base != null) {
+			configuration.setEnableWeb(true);
+			List<MagicController> controllers = new ArrayList<>(Arrays.asList(
+					new MagicAPIController(configuration),
+					dataSourceController,
+					new MagicWorkbenchController(configuration),
+					new MagicGroupController(configuration),
+					new MagicFunctionController(configuration)
+			));
+			controllers.forEach(item -> mappingHandlerMapping.registerController(item, base));
+		}
+		dataSourceController.registerDataSource();
+		// 设置拦截器信息
+		this.requestInterceptors.forEach(interceptor -> {
+			logger.info("注册请求拦截器:{}", interceptor.getClass());
+			configuration.addRequestInterceptor(interceptor);
+		});
+
+		if (this.properties.isBanner()) {
+			configuration.printBanner();
+		}
+		configuration.setMagicFunctionManager(magicFunctionManager);
+		// 注册函数加载器
+		magicFunctionManager.registerFunctionLoader();
+		// 注册所有函数a
+		magicFunctionManager.registerAllFunction();
+		// 自动刷新
+		magicFunctionManager.enableRefresh(properties.getRefreshInterval());
+		mappingHandlerMapping.setHandler(new RequestHandler(configuration));
+		mappingHandlerMapping.setMagicApiService(apiServiceProvider);
+		mappingHandlerMapping.setGroupServiceProvider(groupServiceProvider);
+		// 注册所有映射
+		mappingHandlerMapping.registerAllMapping();
+		int refreshInterval = properties.getRefreshInterval();
+		// 自动刷新
+		mappingHandlerMapping.enableRefresh(refreshInterval);
+		if (refreshInterval > 0) {
+			Executors.newScheduledThreadPool(1).scheduleAtFixedRate(dataSourceController::registerDataSource, refreshInterval, refreshInterval, TimeUnit.SECONDS);
+		}
+		return configuration;
+	}
+
+	public AuthorizationInterceptor createAuthorizationInterceptor() {
+		SecurityConfig securityConfig = properties.getSecurityConfig();
+		return new DefaultAuthorizationInterceptor(securityConfig.getUsername(), securityConfig.getPassword());
+	}
+
+	private RestTemplate createRestTemplate(){
+		RestTemplate restTemplate = new RestTemplate();
+		restTemplate.getMessageConverters().add(new StringHttpMessageConverter(StandardCharsets.UTF_8) {
+			{
+				setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
+			}
+
+			@Override
+			public boolean supports(Class<?> clazz) {
+				return true;
+			}
+		});
+		return restTemplate;
+	}
+
+}

+ 312 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicAPIProperties.java

@@ -0,0 +1,312 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+import org.ssssssss.magicapi.controller.RequestHandler;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@ConfigurationProperties(prefix = "magic-api")
+public class MagicAPIProperties {
+
+	/**
+	 * 版本号
+	 */
+	private final String version = RequestHandler.class.getPackage().getImplementationVersion();
+	/**
+	 * web页面入口
+	 */
+	private String web;
+	/**
+	 * 接口路径前缀
+	 */
+	private String prefix;
+	/**
+	 * 打印banner
+	 */
+	private boolean banner = true;
+	/**
+	 * 是否抛出异常
+	 */
+	private boolean throwException = false;
+	/**
+	 * 自动导入的模块,多个用","分隔
+	 *
+	 * @since 0.3.2
+	 */
+	private String autoImportModule = "db";
+	/**
+	 * 可自动导入的包(目前只支持以.*结尾的通配符),多个用","分隔
+	 *
+	 * @since 0.4.0
+	 */
+	private String autoImportPackage;
+	/**
+	 * 自动刷新间隔,单位为秒,默认不开启
+	 *
+	 * @since 0.3.4
+	 */
+	@Deprecated
+	private int refreshInterval = 0;
+	/**
+	 * 是否允许覆盖应用接口,默认为false
+	 *
+	 * @since 0.4.0
+	 */
+	private boolean allowOverride = false;
+	/**
+	 * SQL列名转换
+	 *
+	 * @since 0.5.0
+	 */
+	private String sqlColumnCase = "default";
+	/**
+	 * 线程核心数,需要>0,<=0时采用默认配置,即CPU核心数 * 2
+	 *
+	 * @since 0.4.5
+	 */
+	private int threadPoolExecutorSize = 0;
+	/**
+	 * 编辑器配置文件路径(js)
+	 *
+	 * @since 0.6.1
+	 */
+	private String editorConfig;
+	/**
+	 * 是否启用跨域支持
+	 *
+	 * @since 1.0.0
+	 */
+	private boolean supportCrossDomain = true;
+
+	/**
+	 * JSON响应结构表达式
+	 *
+	 * @since 1.0.0
+	 */
+	private String response;
+
+
+	@NestedConfigurationProperty
+	private SecurityConfig securityConfig = new SecurityConfig();
+
+	@NestedConfigurationProperty
+	private PageConfig pageConfig = new PageConfig();
+
+	@NestedConfigurationProperty
+	private CacheConfig cacheConfig = new CacheConfig();
+
+	@NestedConfigurationProperty
+	private DebugConfig debugConfig = new DebugConfig();
+
+	@NestedConfigurationProperty
+	private SwaggerConfig swaggerConfig = new SwaggerConfig();
+
+	@NestedConfigurationProperty
+	private ResourceConfig resource = new ResourceConfig();
+
+	@NestedConfigurationProperty
+	private ResponseCodeConfig responseCodeConfig = new ResponseCodeConfig();
+
+	@NestedConfigurationProperty
+	private ClusterConfig clusterConfig = new ClusterConfig();
+
+	public String getEditorConfig() {
+		return editorConfig;
+	}
+
+	public void setEditorConfig(String editorConfig) {
+		this.editorConfig = editorConfig;
+	}
+
+	public String getWeb() {
+		if (StringUtils.isBlank(web)) {
+			return null;
+		}
+		if (web.endsWith("/**")) {
+			return web.substring(0, web.length() - 3);
+		}
+		if (web.endsWith("/*")) {
+			return web.substring(0, web.length() - 2);
+		}
+		if (web.endsWith("/")) {
+			return web.substring(0, web.length() - 1);
+		}
+		return web;
+	}
+
+	public void setWeb(String web) {
+		this.web = web;
+	}
+
+	public String getSqlColumnCase() {
+		return sqlColumnCase;
+	}
+
+	public void setSqlColumnCase(String sqlColumnCase) {
+		this.sqlColumnCase = sqlColumnCase;
+	}
+
+	public boolean isBanner() {
+		return banner;
+	}
+
+	public void setBanner(boolean banner) {
+		this.banner = banner;
+	}
+
+	public PageConfig getPageConfig() {
+		return pageConfig;
+	}
+
+	public void setPageConfig(PageConfig pageConfig) {
+		this.pageConfig = pageConfig;
+	}
+
+	public boolean isThrowException() {
+		return throwException;
+	}
+
+	public void setThrowException(boolean throwException) {
+		this.throwException = throwException;
+	}
+
+	public CacheConfig getCacheConfig() {
+		return cacheConfig;
+	}
+
+	public void setCacheConfig(CacheConfig cacheConfig) {
+		this.cacheConfig = cacheConfig;
+	}
+
+	public DebugConfig getDebugConfig() {
+		return debugConfig;
+	}
+
+	public void setDebugConfig(DebugConfig debugConfig) {
+		this.debugConfig = debugConfig;
+	}
+
+	public SecurityConfig getSecurityConfig() {
+		return securityConfig;
+	}
+
+	public void setSecurityConfig(SecurityConfig securityConfig) {
+		this.securityConfig = securityConfig;
+	}
+
+	public String getPrefix() {
+		return prefix;
+	}
+
+	public void setPrefix(String prefix) {
+		this.prefix = prefix;
+	}
+
+	public SwaggerConfig getSwaggerConfig() {
+		return swaggerConfig;
+	}
+
+	public void setSwaggerConfig(SwaggerConfig swaggerConfig) {
+		this.swaggerConfig = swaggerConfig;
+	}
+
+	public String getAutoImportModule() {
+		return autoImportModule;
+	}
+
+	public void setAutoImportModule(String autoImport) {
+		this.autoImportModule = autoImport;
+	}
+
+	public List<String> getAutoImportModuleList() {
+		return Arrays.asList(autoImportModule.replaceAll("\\s", "").split(","));
+	}
+
+	public int getRefreshInterval() {
+		return refreshInterval;
+	}
+
+	public void setRefreshInterval(int refreshInterval) {
+		this.refreshInterval = refreshInterval;
+	}
+
+	public boolean isAllowOverride() {
+		return allowOverride;
+	}
+
+	public void setAllowOverride(boolean allowOverride) {
+		this.allowOverride = allowOverride;
+	}
+
+	public String getAutoImportPackage() {
+		return autoImportPackage;
+	}
+
+	public void setAutoImportPackage(String autoImportPackage) {
+		this.autoImportPackage = autoImportPackage;
+	}
+
+	public List<String> getAutoImportPackageList() {
+		if (autoImportPackage == null) {
+			return Collections.emptyList();
+		}
+		return Arrays.asList(autoImportPackage.replaceAll("\\s", "").split(","));
+	}
+
+	public int getThreadPoolExecutorSize() {
+		return threadPoolExecutorSize;
+	}
+
+	public void setThreadPoolExecutorSize(int threadPoolExecutorSize) {
+		this.threadPoolExecutorSize = threadPoolExecutorSize;
+	}
+
+	public String getVersion() {
+		return version;
+	}
+
+
+	public ResourceConfig getResource() {
+		return resource;
+	}
+
+	public void setResource(ResourceConfig resource) {
+		this.resource = resource;
+	}
+
+	public boolean isSupportCrossDomain() {
+		return supportCrossDomain;
+	}
+
+	public void setSupportCrossDomain(boolean supportCrossDomain) {
+		this.supportCrossDomain = supportCrossDomain;
+	}
+
+	public String getResponse() {
+		return response;
+	}
+
+	public void setResponse(String response) {
+		this.response = response;
+	}
+
+	public ResponseCodeConfig getResponseCodeConfig() {
+		return responseCodeConfig;
+	}
+
+	public void setResponseCodeConfig(ResponseCodeConfig responseCodeConfig) {
+		this.responseCodeConfig = responseCodeConfig;
+	}
+
+	public ClusterConfig getClusterConfig() {
+		return clusterConfig;
+	}
+
+	public void setClusterConfig(ClusterConfig clusterConfig) {
+		this.clusterConfig = clusterConfig;
+	}
+}

+ 91 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicJsonAutoConfiguration.java

@@ -0,0 +1,91 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Configuration;
+import org.ssssssss.magicapi.utils.IoUtils;
+import org.ssssssss.script.exception.MagicScriptException;
+import org.ssssssss.script.parsing.ast.statement.ClassConverter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.function.Function;
+
+@Configuration
+@AutoConfigureBefore(MagicAPIAutoConfiguration.class)
+public class MagicJsonAutoConfiguration {
+
+	private static void register(Function<String, Object> processString, Function<byte[], Object> processBytes, Function<Object, String> stringify) {
+		register(processString, processBytes, IoUtils::bytes, stringify);
+	}
+
+	private static void register(Function<String, Object> processString, Function<byte[], Object> processBytes, Function<InputStream, Object> processInputStream, Function<Object, String> stringify) {
+		ClassConverter.register("json", (value, params) -> {
+			if (value == null) {
+				return params != null && params.length > 0 ? params[0] : null;
+			} else if (value instanceof CharSequence) {
+				return processString.apply(value.toString());
+			} else if (value instanceof byte[]) {
+				return processBytes.apply((byte[]) value);
+			} else if (value instanceof InputStream) {
+				return processInputStream.apply((InputStream) value);
+			}
+			throw new MagicScriptException(String.format("不支持的类型:%s", value.getClass()));
+		});
+		ClassConverter.register("stringify", (value, params) -> {
+			if (value == null) {
+				return params != null && params.length > 0 ? params[0] : null;
+			}
+			return stringify.apply(value);
+		});
+	}
+
+	@ConditionalOnBean({ObjectMapper.class})
+	@Configuration
+	static class JacksonAutoConfiguration {
+
+
+		JacksonAutoConfiguration(ObjectMapper objectMapper) {
+			register(str -> {
+				try {
+					return objectMapper.readValue(str, Object.class);
+				} catch (IOException e) {
+					throw new RuntimeException(e);
+				}
+			}, bytes -> {
+				try {
+					return objectMapper.readValue(bytes, Object.class);
+				} catch (IOException e) {
+					throw new RuntimeException(e);
+				}
+			}, is -> {
+				try {
+					return objectMapper.readValue(is, Object.class);
+				} catch (IOException e) {
+					throw new RuntimeException(e);
+				}
+			}, object -> {
+				try {
+					return objectMapper.writeValueAsString(object);
+				} catch (JsonProcessingException e) {
+					throw new RuntimeException(e);
+				}
+			});
+		}
+	}
+
+	@ConditionalOnMissingBean({ObjectMapper.class})
+	@ConditionalOnClass(JSON.class)
+	@Configuration
+	static class FastJsonAutoConfiguration {
+
+		FastJsonAutoConfiguration() {
+			register(JSON::parse, JSON::parse, JSON::toJSONString);
+		}
+	}
+}

+ 32 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicMongoAutoConfiguration.java

@@ -0,0 +1,32 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.ssssssss.magicapi.modules.MongoCollectionExtension;
+import org.ssssssss.magicapi.modules.MongoFindIterableExtension;
+import org.ssssssss.magicapi.modules.MongoModule;
+import org.ssssssss.script.reflection.AbstractReflection;
+
+/**
+ * mongo配置
+ */
+@Configuration
+@ConditionalOnBean(MongoTemplate.class)
+@AutoConfigureBefore(MagicAPIAutoConfiguration.class)
+public class MagicMongoAutoConfiguration {
+
+	/**
+	 * 注入mongo模块
+	 */
+	@Bean
+	public MongoModule mongoFunctions(MongoTemplate mongoTemplate) {
+		AbstractReflection.getInstance().registerMethodExtension(MongoCollection.class, new MongoCollectionExtension());
+		AbstractReflection.getInstance().registerMethodExtension(FindIterable.class, new MongoFindIterableExtension());
+		return new MongoModule(mongoTemplate);
+	}
+}

+ 47 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicRedisAutoConfiguration.java

@@ -0,0 +1,47 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.ssssssss.magicapi.adapter.Resource;
+import org.ssssssss.magicapi.adapter.resource.RedisResource;
+import org.ssssssss.magicapi.modules.RedisModule;
+
+/**
+ * redis配置
+ */
+@ConditionalOnBean(RedisConnectionFactory.class)
+@Configuration
+@AutoConfigureBefore(MagicAPIAutoConfiguration.class)
+public class MagicRedisAutoConfiguration {
+
+	private final MagicAPIProperties properties;
+
+	public MagicRedisAutoConfiguration(MagicAPIProperties properties) {
+		this.properties = properties;
+	}
+
+	/**
+	 * 注入redis模块
+	 */
+	@Bean
+	public RedisModule redisFunctions(RedisConnectionFactory connectionFactory) {
+		return new RedisModule(connectionFactory);
+	}
+
+	/**
+	 * 使用Redis存储
+	 */
+	@Bean
+	@ConditionalOnMissingBean
+	@ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "redis")
+	public Resource magicRedisResource(RedisConnectionFactory connectionFactory) {
+		ResourceConfig resource = properties.getResource();
+		return new RedisResource(new StringRedisTemplate(connectionFactory), resource.getPrefix(), resource.isReadonly());
+	}
+}

+ 99 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/MagicSwaggerConfiguration.java

@@ -0,0 +1,99 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Primary;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.ssssssss.magicapi.config.MappingHandlerMapping;
+import org.ssssssss.magicapi.provider.GroupServiceProvider;
+import org.ssssssss.magicapi.swagger.SwaggerEntity;
+import org.ssssssss.magicapi.swagger.SwaggerProvider;
+import org.ssssssss.magicapi.utils.Mapping;
+import springfox.documentation.swagger.web.SwaggerResource;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+
+import javax.servlet.ServletContext;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Swagger配置类
+ */
+@Configuration
+@AutoConfigureAfter({MagicAPIAutoConfiguration.class})
+@EnableConfigurationProperties(MagicAPIProperties.class)
+@ConditionalOnClass(name = "springfox.documentation.swagger.web.SwaggerResourcesProvider")
+public class MagicSwaggerConfiguration {
+
+	@Autowired
+	@Lazy
+	private RequestMappingHandlerMapping requestMappingHandlerMapping;
+
+	private final MagicAPIProperties properties;
+
+	private ApplicationContext applicationContext;
+
+	public MagicSwaggerConfiguration(MagicAPIProperties properties,ApplicationContext applicationContext) {
+		this.properties = properties;
+		this.applicationContext = applicationContext;
+	}
+
+
+	@Bean
+	@Primary
+	public SwaggerResourcesProvider magicSwaggerResourcesProvider(MappingHandlerMapping handlerMapping, GroupServiceProvider groupServiceProvider, ServletContext servletContext) throws NoSuchMethodException {
+		SwaggerConfig config = properties.getSwaggerConfig();
+		RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(config.getLocation()).build();
+
+		// 构建文档信息
+		SwaggerProvider swaggerProvider = new SwaggerProvider();
+		swaggerProvider.setGroupServiceProvider(groupServiceProvider);
+		swaggerProvider.setMappingHandlerMapping(handlerMapping);
+		SwaggerEntity.License license = new SwaggerEntity.License("MIT", "https://gitee.com/ssssssss-team/magic-api/blob/master/LICENSE");
+		swaggerProvider.setInfo(new SwaggerEntity.Info(config.getDescription(), config.getVersion(), config.getTitle(), license, config.getConcat()));
+		swaggerProvider.setBasePath(servletContext.getContextPath());
+
+
+		// 注册swagger.json
+		Mapping.create(requestMappingHandlerMapping).register(requestMappingInfo, swaggerProvider, SwaggerProvider.class.getDeclaredMethod("swaggerJson"));
+
+		return () -> {
+			List<SwaggerResource> resources = new ArrayList<>();
+			Map<String, SwaggerResourcesProvider> beans = applicationContext.getBeansOfType(SwaggerResourcesProvider.class);
+			// 获取已定义的文档信息
+			if (beans != null) {
+				for (Map.Entry<String, SwaggerResourcesProvider> entry : beans.entrySet()) {
+					if (!"magicSwaggerResourcesProvider".equalsIgnoreCase(entry.getKey())) {
+						resources.addAll(entry.getValue().get());
+					}
+				}
+			}
+			// 追加Magic Swagger信息
+			resources.add(swaggerResource(config.getName(), config.getLocation()));
+			return resources;
+		};
+	}
+
+	/**
+	 * 构建 SwaggerResource
+	 *
+	 * @param name     名字
+	 * @param location 位置
+	 * @return
+	 */
+	private SwaggerResource swaggerResource(String name, String location) {
+		SwaggerResource resource = new SwaggerResource();
+		resource.setName(name);
+		resource.setLocation(location);
+		resource.setSwaggerVersion("2.0");
+		return resource;
+	}
+}

+ 59 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/PageConfig.java

@@ -0,0 +1,59 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+/**
+ * 分页配置
+ */
+public class PageConfig {
+
+	/**
+	 * 默认page参数名
+	 */
+	private String page = "page";
+
+	/**
+	 * 默认size参数名
+	 */
+	private String size = "size";
+
+	/**
+	 * 默认首页
+	 */
+	private long defaultPage = 1;
+
+	/**
+	 * 默认页大小
+	 */
+	private long defaultSize = 10;
+
+	public String getPage() {
+		return page;
+	}
+
+	public void setPage(String page) {
+		this.page = page;
+	}
+
+	public String getSize() {
+		return size;
+	}
+
+	public void setSize(String size) {
+		this.size = size;
+	}
+
+	public long getDefaultPage() {
+		return defaultPage;
+	}
+
+	public void setDefaultPage(long defaultPage) {
+		this.defaultPage = defaultPage;
+	}
+
+	public long getDefaultSize() {
+		return defaultSize;
+	}
+
+	public void setDefaultSize(long defaultSize) {
+		this.defaultSize = defaultSize;
+	}
+}

+ 85 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/ResourceConfig.java

@@ -0,0 +1,85 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+/**
+ * 接口存储配置
+ */
+public class ResourceConfig {
+
+	/**
+	 * 存储类型,默认是文件
+	 */
+	private String type = "file";
+
+	/**
+	 * 文件存储位置
+	 */
+	private String location = "/data/magic-api/";
+
+	/**
+	 * 是否是只读模式
+	 */
+	private boolean readonly = false;
+
+	/**
+	 * 前缀
+	 */
+	private String prefix = "magic-api";
+
+	/**
+	 * 使用数据库存储时的表名
+	 */
+	private String tableName = "magic_api_file";
+
+	/**
+	 * 使用数据库存储时使用的数据源
+	 */
+	private String datasource;
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getLocation() {
+		return location;
+	}
+
+	public void setLocation(String location) {
+		this.location = location;
+	}
+
+	public boolean isReadonly() {
+		return readonly;
+	}
+
+	public void setReadonly(boolean readonly) {
+		this.readonly = readonly;
+	}
+
+	public String getPrefix() {
+		return prefix;
+	}
+
+	public void setPrefix(String prefix) {
+		this.prefix = prefix;
+	}
+
+	public String getTableName() {
+		return tableName;
+	}
+
+	public void setTableName(String tableName) {
+		this.tableName = tableName;
+	}
+
+	public String getDatasource() {
+		return datasource;
+	}
+
+	public void setDatasource(String datasource) {
+		this.datasource = datasource;
+	}
+}

+ 47 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/ResponseCodeConfig.java

@@ -0,0 +1,47 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+/**
+ * json结果code配置
+ * @since 1.1.2
+ */
+public class ResponseCodeConfig {
+
+	/**
+	 * 执行成功的code值
+	 */
+	private int success = 1;
+
+	/**
+	 * 参数验证未通过的code值
+	 */
+	private int invalid = 0;
+
+	/**
+	 * 执行出现异常的code值
+	 */
+	private int exception = -1;
+
+	public int getSuccess() {
+		return success;
+	}
+
+	public void setSuccess(int success) {
+		this.success = success;
+	}
+
+	public int getInvalid() {
+		return invalid;
+	}
+
+	public void setInvalid(int invalid) {
+		this.invalid = invalid;
+	}
+
+	public int getException() {
+		return exception;
+	}
+
+	public void setException(int exception) {
+		this.exception = exception;
+	}
+}

+ 34 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/SecurityConfig.java

@@ -0,0 +1,34 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+/**
+ * 安全配置
+ * @since 0.4.0
+ */
+public class SecurityConfig {
+
+	/**
+	 * 登录用的用户名
+	 */
+	private String username;
+
+	/**
+	 * 登录用的密码
+	 */
+	private String password;
+
+	public String getUsername() {
+		return username;
+	}
+
+	public void setUsername(String username) {
+		this.username = username;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+}

+ 86 - 0
magic-api-spring-boot-starter/src/main/java/org/ssssssss/magicapi/spring/boot/starter/SwaggerConfig.java

@@ -0,0 +1,86 @@
+package org.ssssssss.magicapi.spring.boot.starter;
+
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+import org.ssssssss.magicapi.swagger.SwaggerEntity;
+
+/**
+ * Swagger 配置
+ */
+public class SwaggerConfig {
+
+	/**
+	 * 资源名称
+	 */
+	private String name = "MagicAPI接口";
+
+	/**
+	 * 资源位置
+	 */
+	private String location = "/v2/api-docs/magic-api/swagger2.json";
+
+	/**
+	 * 文档标题
+	 */
+	private String title = "MagicAPI Swagger Docs";
+
+	/**
+	 * 文档描述
+	 */
+	private String description = "MagicAPI 接口信息";
+
+	@NestedConfigurationProperty
+	private SwaggerEntity.Concat concat = new SwaggerEntity.Concat();
+
+	/**
+	 * 文档版本
+	 */
+	private String version = "1.0";
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getLocation() {
+		return location;
+	}
+
+	public void setLocation(String location) {
+		this.location = location;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getVersion() {
+		return version;
+	}
+
+	public void setVersion(String version) {
+		this.version = version;
+	}
+
+	public SwaggerEntity.Concat getConcat() {
+		return concat;
+	}
+
+	public void setConcat(SwaggerEntity.Concat concat) {
+		this.concat = concat;
+	}
+}

+ 27 - 0
magic-api-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@@ -0,0 +1,27 @@
+{
+  "groups": [
+    {
+      "sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
+      "name": "magic-api",
+      "type": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties"
+    },
+    {
+      "sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
+      "name": "page-config",
+      "sourceMethod": "getPageConfig()",
+      "type": "org.ssssssss.magicapi.spring.boot.starter.PageConfig"
+    },
+    {
+      "sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
+      "name": "cache-config",
+      "sourceMethod": "getCacheConfig()",
+      "type": "org.ssssssss.magicapi.spring.boot.starter.CacheConfig"
+    },
+    {
+      "sourceType": "org.ssssssss.magicapi.spring.boot.starter.MagicAPIProperties",
+      "name": "debug-config",
+      "sourceMethod": "getDebugConfig()",
+      "type": "org.ssssssss.magicapi.spring.boot.starter.DebugConfig"
+    }
+  ]
+}

+ 1 - 0
magic-api-spring-boot-starter/src/main/resources/META-INF/spring.factories

@@ -0,0 +1 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.spring.boot.starter.MagicAPIAutoConfiguration