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.aop; 017 018import com.jfinal.aop.AopFactory; 019import com.jfinal.aop.Inject; 020import com.jfinal.core.Controller; 021import com.jfinal.log.Log; 022import com.jfinal.plugin.activerecord.Model; 023import com.jfinal.proxy.Proxy; 024import com.jfinal.proxy.ProxyManager; 025import io.jboot.aop.annotation.*; 026import io.jboot.aop.cglib.JbootCglibProxyFactory; 027import io.jboot.aop.javassist.JbootJavassistProxyFactory; 028import io.jboot.app.JbootApplicationConfig; 029import io.jboot.app.config.JbootConfigKit; 030import io.jboot.app.config.JbootConfigManager; 031import io.jboot.app.config.annotation.ConfigModel; 032import io.jboot.components.event.JbootEventListener; 033import io.jboot.components.mq.JbootmqMessageListener; 034import io.jboot.components.rpc.*; 035import io.jboot.components.rpc.annotation.RPCInject; 036import io.jboot.db.model.JbootModel; 037import io.jboot.exception.JbootException; 038import io.jboot.service.JbootServiceBase; 039import io.jboot.utils.*; 040import io.jboot.web.controller.JbootController; 041 042import javax.annotation.PostConstruct; 043import java.io.Serializable; 044import java.lang.reflect.Field; 045import java.lang.reflect.Method; 046import java.util.HashMap; 047import java.util.List; 048import java.util.Map; 049import java.util.concurrent.ConcurrentHashMap; 050 051public class JbootAopFactory extends AopFactory { 052 053 private static final Log LOG = Log.getLog(JbootAopFactory.class); 054 055 //排除默认的映射 056 private final static Class<?>[] DEFAULT_EXCLUDES_MAPPING_CLASSES = new Class[]{ 057 JbootEventListener.class 058 , JbootmqMessageListener.class 059 , Serializable.class 060 }; 061 062 private static JbootAopFactory me = new JbootAopFactory(); 063 064 065 public static JbootAopFactory me() { 066 return me; 067 } 068 069 private boolean defaultLazyInit = false; 070 071 public boolean isDefaultLazyInit() { 072 return defaultLazyInit; 073 } 074 075 public void setDefaultLazyInit(boolean defaultLazyInit) { 076 this.defaultLazyInit = defaultLazyInit; 077 } 078 079 private Map<String, Object> beansCache = new ConcurrentHashMap<>(); 080 private Map<String, Class<?>> beanNameClassesMapping = new ConcurrentHashMap<>(); 081 082 083 protected JbootAopFactory() { 084 085 if ("javassist".equalsIgnoreCase(JbootApplicationConfig.get().getProxy())) { 086 ProxyManager.me().setProxyFactory(new JbootJavassistProxyFactory()); 087 } else { 088 ProxyManager.me().setProxyFactory(new JbootCglibProxyFactory()); 089 } 090 091 setInjectSuperClass(true); 092 initBeanMapping(); 093 } 094 095 @Override 096 protected Object createObject(Class<?> targetClass) { 097 ConfigModel configModel = targetClass.getAnnotation(ConfigModel.class); 098 if (configModel != null) { 099 return JbootConfigManager.me().get(targetClass); 100 } 101 102 StaticConstruct staticConstruct = targetClass.getAnnotation(StaticConstruct.class); 103 if (staticConstruct != null) { 104 return ClassUtil.newInstanceByStaticConstruct(targetClass, staticConstruct); 105 } 106 107 return Proxy.get(targetClass); 108 } 109 110 111 @Override 112 protected void doInject(Class<?> targetClass, Object targetObject) throws ReflectiveOperationException { 113 targetClass = getUsefulClass(targetClass); 114 doInjectTargetClass(targetClass, targetObject); 115 doInvokePostConstructMethod(targetClass, targetObject); 116 } 117 118 119 /** 120 * 执行 @PostConstruct 注解方法 121 * 122 * @param targetClass 123 * @param targetObject 124 * @throws ReflectiveOperationException 125 */ 126 protected void doInvokePostConstructMethod(Class<?> targetClass, Object targetObject) throws ReflectiveOperationException { 127 Method[] methods = targetClass.getDeclaredMethods(); 128 for (Method method : methods) { 129 if (method.getParameterCount() == 0 && method.getAnnotation(PostConstruct.class) != null) { 130 method.setAccessible(true); 131 method.invoke(targetObject); 132 break; 133 } 134 } 135 136 Class<?> superClass = targetClass.getSuperclass(); 137 if (notSystemClass(superClass)) { 138 doInvokePostConstructMethod(superClass, targetObject); 139 } 140 } 141 142 143 /** 144 * 执行注入操作 145 * 146 * @param targetClass 147 * @param targetObject 148 * @throws ReflectiveOperationException 149 */ 150 protected void doInjectTargetClass(Class<?> targetClass, Object targetObject) throws ReflectiveOperationException { 151 Field[] fields = targetClass.getDeclaredFields(); 152 153 if (fields.length != 0) { 154 for (Field field : fields) { 155 Object fieldValue = null; 156 if (defaultLazyInit) { 157 fieldValue = createFieldObjectLazy(targetObject, field); 158 } else { 159 Lazy Lazy = field.getAnnotation(Lazy.class); 160 if (Lazy != null) { 161 fieldValue = createFieldObjectLazy(targetObject, field); 162 } else { 163 fieldValue = createFieldObjectNormal(targetObject, field); 164 } 165 } 166 167 if (fieldValue != null) { 168 field.setAccessible(true); 169 field.set(targetObject, fieldValue); 170 } 171 } 172 } 173 174 175 // 是否对超类进行注入 176 if (injectSuperClass) { 177 Class<?> superClass = targetClass.getSuperclass(); 178 if (notSystemClass(superClass)) { 179 doInjectTargetClass(superClass, targetObject); 180 } 181 } 182 } 183 184 185 protected Object createFieldObjectLazy(Object targetObject, Field field) throws ReflectiveOperationException { 186 return JbootLazyLoaderFactory.me().getLoader().loadLazyObject(targetObject, field); 187 } 188 189 190 public Object createFieldObjectNormal(Object targetObject, Field field) throws ReflectiveOperationException { 191 Inject inject = field.getAnnotation(Inject.class); 192 if (inject != null) { 193 Bean bean = field.getAnnotation(Bean.class); 194 String beanName = bean != null ? AnnotationUtil.get(bean.name()) : null; 195 if (StrUtil.isNotBlank(beanName)) { 196 return createFieldObjectByBeanName(targetObject, field, beanName); 197 } else { 198 return createFieldObjectByJfinalOriginal(targetObject, field, inject); 199 } 200 } 201 202 RPCInject rpcInject = field.getAnnotation(RPCInject.class); 203 if (rpcInject != null) { 204 return createFieldObjectByRPCComponent(targetObject, field, rpcInject); 205 } 206 207 ConfigValue configValue = field.getAnnotation(ConfigValue.class); 208 if (configValue != null) { 209 return createFieldObjectByConfigValue(targetObject, field, configValue); 210 } 211 212 return null; 213 } 214 215 216 protected boolean notSystemClass(Class clazz) { 217 return clazz != JbootController.class 218 && clazz != Controller.class 219 && clazz != JbootServiceBase.class 220 && clazz != Object.class 221 && clazz != JbootModel.class 222 && clazz != Model.class 223 && clazz != null; 224 } 225 226 227 private Object createFieldObjectByBeanName(Object targetObject, Field field, String beanName) throws ReflectiveOperationException { 228 Object fieldInjectedObject = beansCache.get(beanName); 229 if (fieldInjectedObject == null) { 230 Class<?> fieldInjectedClass = beanNameClassesMapping.get(beanName); 231 if (fieldInjectedClass == null || fieldInjectedClass == Void.class) { 232 fieldInjectedClass = field.getType(); 233 } 234 235 fieldInjectedObject = doGet(fieldInjectedClass); 236 beansCache.put(beanName, fieldInjectedObject); 237 } 238 239 return fieldInjectedObject; 240 } 241 242 /** 243 * JFinal 原生 service 注入 244 * 245 * @param targetObject 246 * @param field 247 * @param inject 248 * @throws ReflectiveOperationException 249 */ 250 private Object createFieldObjectByJfinalOriginal(Object targetObject, Field field, Inject inject) throws ReflectiveOperationException { 251 Class<?> fieldInjectedClass = inject.value(); 252 if (fieldInjectedClass == Void.class) { 253 fieldInjectedClass = field.getType(); 254 } 255 256 return doGet(fieldInjectedClass); 257 } 258 259 /** 260 * 注入 rpc service 261 * 262 * @param targetObject 263 * @param field 264 * @param rpcInject 265 */ 266 private Object createFieldObjectByRPCComponent(Object targetObject, Field field, RPCInject rpcInject) { 267 try { 268 Class<?> fieldInjectedClass = field.getType(); 269 JbootrpcReferenceConfig config = ReferenceConfigCache.get(fieldInjectedClass, rpcInject); 270 Jbootrpc jbootrpc = JbootrpcManager.me().getJbootrpc(); 271 return jbootrpc.serviceObtain(fieldInjectedClass, config); 272 } catch (NullPointerException npe) { 273 LOG.error("Can not inject rpc service for \"" + field.getName() + "\" in class \"" + ClassUtil.getUsefulClass(targetObject.getClass()).getName() + "\", because @RPCInject.check ==\"true\" and target is not available. \n" + rpcInject, npe); 274 } catch (Exception ex) { 275 LOG.error("Can not inject rpc service for \"" + field.getName() + "\" in class \"" + ClassUtil.getUsefulClass(targetObject.getClass()).getName() + "\" \n" + rpcInject, ex); 276 } 277 return null; 278 } 279 280 /** 281 * 注入配置文件 282 * 283 * @param targetObject 284 * @param field 285 * @param configValue 286 * @throws IllegalAccessException 287 */ 288 private Object createFieldObjectByConfigValue(Object targetObject, Field field, ConfigValue configValue) throws IllegalAccessException { 289 String key = AnnotationUtil.get(configValue.value()); 290 Class<?> fieldInjectedClass = field.getType(); 291 String value = JbootConfigManager.me().getConfigValue(key); 292 293 Object fieldObject = null; 294 if (StrUtil.isNotBlank(value)) { 295 fieldObject = JbootConfigKit.convert(fieldInjectedClass, value, field.getGenericType()); 296 } 297 298 if (fieldObject == null) { 299 field.setAccessible(true); 300 fieldObject = field.get(targetObject); 301 } 302 return fieldObject; 303 } 304 305 306 /** 307 * 允许多次执行 AddMapping,方便在应用运行中可以切换 Mapping 308 * 309 * @param from 310 * @param to 311 * @param <T> 312 * @return 313 */ 314 @Override 315 public synchronized <T> AopFactory addMapping(Class<T> from, Class<? extends T> to) { 316 if (from == null || to == null) { 317 throw new IllegalArgumentException("The parameter from and to can not be null"); 318 } 319 320 if (mapping == null) { 321 mapping = new HashMap<>(128, 0.25F); 322 } 323 324 Class mappingClass = mapping.get(from); 325 if (mappingClass != null) { 326 if (mappingClass == to) { 327 return this; 328 } else { 329 singletonCache.remove(mappingClass); 330 } 331 } 332 333 mapping.put(from, to); 334 return this; 335 } 336 337 338 protected void setFieldValue(Field field, Object toObj, Object data) throws IllegalAccessException { 339 field.setAccessible(true); 340 field.set(toObj, data); 341 } 342 343 344 /** 345 * 初始化 @Bean 注解的映射关系 346 */ 347 private void initBeanMapping() { 348 349 // 初始化 @Configuration 里的 beans 350 initConfigurationBeansObject(); 351 352 // 添加映射 353 initBeansMapping(); 354 } 355 356 357 /** 358 * 初始化 @Configuration 里的 bean 配置 359 */ 360 private void initConfigurationBeansObject() { 361 List<Class> configurationClasses = ClassScanner.scanClassByAnnotation(Configuration.class, true); 362 for (Class<?> configurationClass : configurationClasses) { 363 Object configurationObj = ClassUtil.newInstance(configurationClass, false); 364 if (configurationObj == null) { 365 throw new NullPointerException("Can not new instance for class: " + configurationClass.getName()); 366 } 367 Method[] methods = configurationClass.getDeclaredMethods(); 368 for (Method method : methods) { 369 Bean beanAnnotation = method.getAnnotation(Bean.class); 370 if (beanAnnotation != null) { 371 Class<?> returnType = method.getReturnType(); 372 if (returnType == void.class) { 373 throw new JbootException("@Bean annotation can not use for void method: " + ClassUtil.buildMethodString(method)); 374 } 375 376 String beanName = StrUtil.obtainDefault(AnnotationUtil.get(beanAnnotation.name()), method.getName()); 377 if (beansCache.containsKey(beanName)) { 378 throw new JbootException("Application has contains beanName \"" + beanName + "\" for " + getBean(beanName) 379 + ", Can not add again by method: " + ClassUtil.buildMethodString(method)); 380 } 381 382 try { 383 Object methodObj = method.invoke(configurationObj); 384 if (methodObj != null) { 385 beansCache.put(beanName, methodObj); 386 singletonCache.put(returnType, methodObj); 387 } 388 } catch (Exception ex) { 389 throw new RuntimeException(ex); 390 } 391 } 392 } 393 } 394 } 395 396 397 /** 398 * 添加 所有的 Bean 和实现类 的映射 399 */ 400 private void initBeansMapping() { 401 List<Class> classes = ClassScanner.scanClassByAnnotation(Bean.class, true); 402 for (Class implClass : classes) { 403 Bean bean = (Bean) implClass.getAnnotation(Bean.class); 404 String beanName = AnnotationUtil.get(bean.name()); 405 if (StrUtil.isNotBlank(beanName)) { 406 if (beanNameClassesMapping.containsKey(beanName)) { 407 throw new JbootException("application has contains beanName \"" + beanName + "\" for " + getBean(beanName) 408 + ", can not add for class " + implClass); 409 } 410 beanNameClassesMapping.put(beanName, implClass); 411 } else { 412 Class<?>[] interfaceClasses = implClass.getInterfaces(); 413 414 if (interfaceClasses.length == 0) { 415 //add self 416 this.addMapping(implClass, implClass); 417 } else { 418 Class<?>[] excludes = buildExcludeClasses(implClass); 419 for (Class<?> interfaceClass : interfaceClasses) { 420 if (!inExcludes(interfaceClass, excludes)) { 421 this.addMapping(interfaceClass, implClass); 422 } 423 } 424 } 425 } 426 } 427 } 428 429 430 private Class<?>[] buildExcludeClasses(Class<?> implClass) { 431 BeanExclude beanExclude = implClass.getAnnotation(BeanExclude.class); 432 433 //对某些系统的类 进行排除,例如:Serializable 等 434 return beanExclude == null 435 ? DEFAULT_EXCLUDES_MAPPING_CLASSES 436 : ArrayUtil.concat(DEFAULT_EXCLUDES_MAPPING_CLASSES, beanExclude.value()); 437 } 438 439 440 private boolean inExcludes(Class<?> interfaceClass, Class<?>[] excludes) { 441 for (Class<?> ex : excludes) { 442 if (ex.isAssignableFrom(interfaceClass)) { 443 return true; 444 } 445 } 446 return false; 447 } 448 449 450 public <T> T getBean(String name) { 451 T ret = (T) beansCache.get(name); 452 if (ret == null) { 453 if (beanNameClassesMapping.containsKey(name)) { 454 try { 455 ret = (T) doGet(beanNameClassesMapping.get(name)); 456 beansCache.put(name, ret); 457 } catch (ReflectiveOperationException e) { 458 throw new RuntimeException(e); 459 } 460 } 461 } 462 463 return ret; 464 } 465 466 public void setBean(String name, Object obj) { 467 beansCache.put(name, obj); 468 } 469 470}