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}