001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.core; 017 018import com.jfinal.aop.Aop; 019import com.jfinal.aop.AopManager; 020import com.jfinal.config.*; 021import com.jfinal.core.Controller; 022import com.jfinal.core.Path; 023import com.jfinal.core.converter.TypeConverter; 024import com.jfinal.kit.PathKit; 025import com.jfinal.kit.PropKit; 026import com.jfinal.plugin.activerecord.ActiveRecordPlugin; 027import com.jfinal.template.Directive; 028import com.jfinal.template.Engine; 029import io.jboot.Jboot; 030import io.jboot.aop.JbootAopFactory; 031import io.jboot.aop.jfinal.JfinalHandlers; 032import io.jboot.aop.jfinal.JfinalPlugins; 033import io.jboot.app.ApplicationUtil; 034import io.jboot.components.cache.support.JbootCaptchaCache; 035import io.jboot.components.cache.support.JbootTokenCache; 036import io.jboot.components.gateway.JbootGatewayHandler; 037import io.jboot.components.gateway.JbootGatewayManager; 038import io.jboot.components.limiter.LimiterManager; 039import io.jboot.components.mq.JbootmqManager; 040import io.jboot.components.rpc.JbootrpcManager; 041import io.jboot.components.schedule.JbootScheduleManager; 042import io.jboot.core.listener.JbootAppListenerManager; 043import io.jboot.core.log.JbootLogFactory; 044import io.jboot.db.ArpManager; 045import io.jboot.support.metric.JbootMetricConfig; 046import io.jboot.support.metric.MetricServletHandler; 047import io.jboot.support.metric.request.JbootRequestMetricHandler; 048import io.jboot.support.seata.JbootSeataManager; 049import io.jboot.support.sentinel.JbootSentinelManager; 050import io.jboot.support.sentinel.SentinelConfig; 051import io.jboot.support.sentinel.SentinelHandler; 052import io.jboot.support.shiro.JbootShiroManager; 053import io.jboot.support.swagger.JbootSwaggerConfig; 054import io.jboot.support.swagger.JbootSwaggerController; 055import io.jboot.support.swagger.JbootSwaggerManager; 056import io.jboot.utils.*; 057import io.jboot.web.JbootActionMapping; 058import io.jboot.web.JbootWebConfig; 059import io.jboot.web.PathVariableActionMapping; 060import io.jboot.web.attachment.AttachmentHandler; 061import io.jboot.web.attachment.LocalAttachmentContainerConfig; 062import io.jboot.web.controller.JbootControllerManager; 063import io.jboot.web.controller.annotation.GetMapping; 064import io.jboot.web.controller.annotation.PostMapping; 065import io.jboot.web.controller.annotation.RequestMapping; 066import io.jboot.web.converter.ArrayConverters; 067import io.jboot.web.converter.TypeConverterFunc; 068import io.jboot.web.directive.JbootOutputDirectiveFactory; 069import io.jboot.web.directive.SharedEnumObject; 070import io.jboot.web.directive.annotation.*; 071import io.jboot.web.handler.JbootActionHandler; 072import io.jboot.web.handler.JbootHandler; 073import io.jboot.web.handler.PathVariableActionHandler; 074import io.jboot.web.json.JbootJson; 075import io.jboot.web.render.JbootRenderFactory; 076import io.jboot.web.xss.XSSHandler; 077import io.jboot.wechat.WechatSupport; 078 079import java.io.File; 080import java.util.ArrayList; 081import java.util.List; 082import java.util.Properties; 083 084 085public class JbootCoreConfig extends JFinalConfig { 086 087 private List<Routes.Route> routeList = new ArrayList<>(); 088 089 090 public JbootCoreConfig() { 091 092 initSystemProperties(); 093 094 // 自动为 Interceptor 和 Controller 等添加依赖注入 095 AopManager.me().setInjectDependency(true); 096 AopManager.me().setAopFactory(JbootAopFactory.me()); 097 098 Aop.inject(this); 099 100 initWebRootPath(); 101 102 JbootAppListenerManager.me().onInit(); 103 } 104 105 106 /** 107 * apollo、sentinel 等配置需要通过 System Properites 来进行配置的 108 * 而 System Properties 的配置需要在启动的时候同 java -D 添加配置,极为不方便 109 * 此时,可以添加在 jboot-system.properties 里添加,来代替 java -D 的情况 110 */ 111 private void initSystemProperties() { 112 //加载 jboot-system.properties 代替启动参数的 -D 配置 113 File systemPropFile = new File(PathKit.getRootClassPath(), "jboot-system.properties"); 114 if (systemPropFile.exists() && systemPropFile.isFile()) { 115 Properties properties = PropKit.use(systemPropFile).getProperties(); 116 if (properties != null && !properties.isEmpty()) { 117 for (Object key : properties.keySet()) { 118 if (StrUtil.isNotBlank(key)) { 119 String newKey = key.toString().trim(); 120 String systemValue = System.getProperty(newKey); 121 if (StrUtil.isNotBlank(systemValue)) { 122 continue; 123 } 124 String newValue = properties.getProperty(newKey); 125 if (StrUtil.isNotBlank(newValue)) { 126 System.setProperty(newKey, newValue.trim()); 127 } 128 } 129 } 130 } 131 } 132 } 133 134 135 /** 136 * 在 JFinal.initPathKit() 这个方法中,如果 webRootPath 会为 null 137 * 其会去通过 PathKit.detectWebRootPath() 去初始化一个错误的路径 138 * 此方法的目的是为了防止 webRootPath 为 null 139 */ 140 private void initWebRootPath() { 141 String webRootPath = ReflectUtil.getStaticFieldValue(PathKit.class, "webRootPath"); 142 if (webRootPath == null) { 143 PathKit.setWebRootPath(PathKit.getRootClassPath()); 144 } 145 } 146 147 148 @Override 149 public void configConstant(Constants constants) { 150 151 JbootAppListenerManager.me().onConstantConfigBefore(constants); 152 153 constants.setRenderFactory(JbootRenderFactory.me()); 154 constants.setDevMode(Jboot.isDevMode()); 155 156 constants.setLogFactory(new JbootLogFactory()); 157 constants.setMaxPostSize(1024 * 1024 * 2000); 158 constants.setReportAfterInvocation(false); 159 160 constants.setControllerFactory(JbootControllerManager.me()); 161 constants.setJsonFactory(JbootJson::new); 162 constants.setInjectDependency(true); 163 164 constants.setTokenCache(new JbootTokenCache()); 165 constants.setCaptchaCache(new JbootCaptchaCache()); 166 167 constants.setBaseUploadPath(LocalAttachmentContainerConfig.getInstance().buildUploadAbsolutePath()); 168 constants.setJsonDatePattern(DateUtil.datetimePattern); 169 170 if (JbootWebConfig.getInstance().isPathVariableEnable()) { 171 constants.setActionMapping(PathVariableActionMapping::new); 172 } else { 173 constants.setActionMapping(JbootActionMapping::new); 174 } 175 176 JbootAppListenerManager.me().onConstantConfig(constants); 177 178 } 179 180 181 @Override 182 public void configRoute(Routes routes) { 183 184 routes.setMappingSuperClass(true); 185 186 List<Class<Controller>> controllerClassList = ClassScanner.scanSubClass(Controller.class); 187 if (ArrayUtil.isNotEmpty(controllerClassList)) { 188 for (Class<Controller> clazz : controllerClassList) { 189 String[] valueAndViewPath = getMappingAndViewPath(clazz); 190 if (valueAndViewPath != null) { 191 initRoutes(routes, clazz, valueAndViewPath[0], valueAndViewPath[1]); 192 } 193 } 194 } 195 196 JbootSwaggerConfig swaggerConfig = Jboot.config(JbootSwaggerConfig.class); 197 if (swaggerConfig.isConfigOk()) { 198 routes.add(swaggerConfig.getPath(), JbootSwaggerController.class, swaggerConfig.getPath()); 199 } 200 201 JbootAppListenerManager.me().onRouteConfig(routes); 202 203 for (Routes.Route route : routes.getRouteItemList()) { 204 JbootControllerManager.me().setMapping(route.getControllerPath(), route.getControllerClass()); 205 } 206 207 routeList.addAll(routes.getRouteItemList()); 208 } 209 210 private String removeLastSlash(String path) { 211 while (path.endsWith("/") && path.length() > 1) { 212 path = path.substring(0, path.length() - 1); 213 } 214 return path; 215 } 216 217 public static String[] getMappingAndViewPath(Class<? extends Controller> clazz) { 218 RequestMapping rm = clazz.getAnnotation(RequestMapping.class); 219 if (rm != null) { 220 return new String[]{AnnotationUtil.get(rm.value()), AnnotationUtil.get(rm.viewPath())}; 221 } 222 223 Path path = clazz.getAnnotation(Path.class); 224 if (path != null) { 225 return new String[]{AnnotationUtil.get(path.value()), AnnotationUtil.get(path.viewPath())}; 226 } 227 228 GetMapping gp = clazz.getAnnotation(GetMapping.class); 229 if (gp != null) { 230 return new String[]{AnnotationUtil.get(gp.value()), AnnotationUtil.get(gp.viewPath())}; 231 } 232 233 PostMapping pp = clazz.getAnnotation(PostMapping.class); 234 if (pp != null) { 235 return new String[]{AnnotationUtil.get(pp.value()), AnnotationUtil.get(pp.viewPath())}; 236 } 237 238 return null; 239 } 240 241 242 private void initRoutes(Routes routes, Class<Controller> controllerClass, String path, String viewPath) { 243 if (StrUtil.isBlank(path)) { 244 return; 245 } else { 246 path = AnnotationUtil.get(path); 247 } 248 249 path = removeLastSlash(path); 250 viewPath = AnnotationUtil.get(viewPath); 251 252 if (Path.NULL_VIEW_PATH.equals(viewPath)) { 253 routes.add(path, controllerClass); 254 } else { 255 routes.add(path, controllerClass, viewPath); 256 } 257 } 258 259 260 @Override 261 public void configEngine(Engine engine) { 262 263 engine.setOutputDirectiveFactory(JbootOutputDirectiveFactory.me); 264 265 //通过 java -jar xxx.jar 在单独的jar里运行 266 if (ApplicationUtil.runInFatjar()) { 267 engine.setToClassPathSourceFactory(); 268 engine.setBaseTemplatePath("webapp"); 269 } 270 271 List<Class> directiveClasses = ClassScanner.scanClass(); 272 for (Class<?> clazz : directiveClasses) { 273 274 if (Directive.class.isAssignableFrom(clazz)) { 275 JFinalDirective directive = clazz.getAnnotation(JFinalDirective.class); 276 if (directive != null) { 277 String name = AnnotationUtil.get(directive.value()); 278 if (directive.override()) { 279 //remove old directive 280 engine.removeDirective(name); 281 } 282 engine.addDirective(name, (Class<? extends Directive>) clazz); 283 } 284 } else if (clazz.isEnum()) { 285 JFinalSharedEnum sharedEnum = clazz.getAnnotation(JFinalSharedEnum.class); 286 if (sharedEnum != null) { 287 String name = AnnotationUtil.get(sharedEnum.value(), clazz.getSimpleName()); 288 if (sharedEnum.override()) { 289 engine.removeSharedObject(name); 290 } 291 engine.addSharedObject(name, SharedEnumObject.create((Class<? extends Enum<?>>) clazz)); 292 } 293 } 294 295 JFinalSharedMethod sharedMethod = clazz.getAnnotation(JFinalSharedMethod.class); 296 if (sharedMethod != null) { 297 engine.addSharedMethod(ClassUtil.newInstance(clazz)); 298 } 299 300 JFinalSharedStaticMethod sharedStaticMethod = clazz.getAnnotation(JFinalSharedStaticMethod.class); 301 if (sharedStaticMethod != null) { 302 engine.addSharedStaticMethod(clazz); 303 } 304 305 JFinalSharedObject sharedObject = clazz.getAnnotation(JFinalSharedObject.class); 306 if (sharedObject != null) { 307 engine.addSharedObject(AnnotationUtil.get(sharedObject.value()), ClassUtil.newInstance(clazz)); 308 } 309 310 311 } 312 313 JbootAppListenerManager.me().onEngineConfig(engine); 314 } 315 316 317 @Override 318 public void configPlugin(Plugins plugins) { 319 320 List<ActiveRecordPlugin> arps = ArpManager.me().getActiveRecordPlugins(); 321 for (ActiveRecordPlugin arp : arps) { 322 plugins.add(arp); 323 } 324 325 JbootAppListenerManager.me().onPluginConfig(new JfinalPlugins(plugins)); 326 327 } 328 329 330 @Override 331 public void configInterceptor(Interceptors interceptors) { 332 // 拦截器的 inject 通过 AopManager.me().setInjectDependency(true); 去配置 333 JbootAppListenerManager.me().onInterceptorConfig(interceptors); 334 } 335 336 @Override 337 public void configHandler(Handlers handlers) { 338 339 //先添加用户的handler,再添加jboot自己的handler 340 //用户的 handler 优先于 jboot 的 handler 执行 341 JbootAppListenerManager.me().onHandlerConfig(new JfinalHandlers(handlers)); 342 343 //一般的项目没必要添加门户网关的 Gateway 344 //在某些情况下,必须要添加的,可以自行添加 345 if (JbootGatewayManager.me().isConfigOk()) { 346 handlers.add(new JbootGatewayHandler()); 347 } 348 349 handlers.add(new AttachmentHandler()); 350 351 SentinelConfig sentinelConfig = SentinelConfig.get(); 352 if (sentinelConfig.isEnable() && sentinelConfig.isReqeustEnable()) { 353 handlers.add(new SentinelHandler()); 354 } 355 356 //metrics 处理 357 JbootMetricConfig metricsConfig = Jboot.config(JbootMetricConfig.class); 358 if (metricsConfig.isEnable() && metricsConfig.isConfigOk()) { 359 360 if (StrUtil.isNotBlank(metricsConfig.getAdminServletMapping())) { 361 handlers.add(new MetricServletHandler(metricsConfig.getAdminServletMapping())); 362 } 363 364 if (metricsConfig.isRequestMetricEnable()) { 365 handlers.add(new JbootRequestMetricHandler()); 366 } 367 } 368 369 if (JbootWebConfig.getInstance().isEscapeParasEnable()) { 370 handlers.add(new XSSHandler()); 371 } 372 373 handlers.add(new JbootHandler()); 374 375 //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler 376 if (handlers.getActionHandler() == null) { 377 if (JbootWebConfig.getInstance().isPathVariableEnable()) { 378 handlers.setActionHandler(new PathVariableActionHandler()); 379 } else { 380 handlers.setActionHandler(new JbootActionHandler()); 381 } 382 } 383 384 } 385 386 @Override 387 public void onStart() { 388 389 JbootAppListenerManager.me().onStartBefore(); 390 391 // 初始化 Jboot 内置组件 392 JbootrpcManager.me().init(); 393 JbootShiroManager.me().init(routeList); 394 JbootScheduleManager.me().init(); 395 JbootSwaggerManager.me().init(); 396 LimiterManager.me().init(); 397 JbootSeataManager.me().init(); 398 JbootSentinelManager.me().init(); 399 400 if (ClassUtil.hasClass("com.jfinal.weixin.sdk.api.ApiConfigKit")) { 401 new WechatSupport().autoSupport(); 402 } 403 404 JbootAppListenerManager.me().onStart(); 405 406 //自定义参数转换方法 407 TypeConverter.me().setConvertFunc(new TypeConverterFunc()); 408 ArrayConverters.init(); 409 410 //一般情况下,各个模块会在 onStart 进行添加监听器 411 //此时可以主动去启动下 mq 412 JbootmqManager.me().init(); 413 414 //使用场景:需要等所有组件 onStart() 完成之后,再去执行某些工作的时候 415 JbootAppListenerManager.me().onStartFinish(); 416 417 418 } 419 420 @Override 421 public void onStop() { 422 JbootAppListenerManager.me().onStop(); 423 424 JbootScheduleManager.me().stop(); 425 JbootSeataManager.me().stop(); 426 JbootrpcManager.me().stop(); 427 428 JbootmqManager.me().stop(); 429 } 430 431 432}