|
@@ -0,0 +1,167 @@
|
|
|
+package cn.nosum.support.processor;
|
|
|
+
|
|
|
+import cn.nosum.support.annotation.DynamicClass;
|
|
|
+import cn.nosum.support.annotation.DynamicMethod;
|
|
|
+import cn.nosum.support.config.DynamicMethodsEndpointRegistrar;
|
|
|
+import cn.nosum.support.endpoint.DynamicMethodWrapper;
|
|
|
+import cn.nosum.support.endpoint.MethodEndpoint;
|
|
|
+import org.apache.commons.logging.Log;
|
|
|
+import org.apache.commons.logging.LogFactory;
|
|
|
+import org.springframework.aop.framework.Advised;
|
|
|
+import org.springframework.aop.support.AopUtils;
|
|
|
+import org.springframework.beans.BeansException;
|
|
|
+import org.springframework.beans.factory.config.BeanPostProcessor;
|
|
|
+import org.springframework.core.MethodIntrospector;
|
|
|
+import org.springframework.core.Ordered;
|
|
|
+import org.springframework.core.annotation.AnnotatedElementUtils;
|
|
|
+import org.springframework.core.annotation.AnnotationUtils;
|
|
|
+import org.springframework.util.ReflectionUtils;
|
|
|
+
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Bean post-processor that registers methods annotated with {@link DynamicMethod}.
|
|
|
+ *
|
|
|
+ * @author Young
|
|
|
+ */
|
|
|
+public class DynamicMethodAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered {
|
|
|
+
|
|
|
+ private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));
|
|
|
+
|
|
|
+ private final Log logger = LogFactory.getLog(getClass());
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
|
|
+ return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
|
|
+ if (this.nonAnnotatedClasses.contains(bean.getClass())) {
|
|
|
+ return bean;
|
|
|
+ }
|
|
|
+ Class<?> targetClass = AopUtils.getTargetClass(bean);
|
|
|
+ Collection<DynamicClass> classLevelDynamicMethods = findConsumerClass(targetClass);
|
|
|
+ if (classLevelDynamicMethods.isEmpty()){
|
|
|
+ return bean;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<Method, Set<DynamicMethod>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
|
|
|
+ (MethodIntrospector.MetadataLookup<Set<DynamicMethod>>) method -> {
|
|
|
+ Set<DynamicMethod> listenerMethods = findConsumerClass(method);
|
|
|
+ return (!listenerMethods.isEmpty() ? listenerMethods : null);
|
|
|
+ });
|
|
|
+ Set<Method> allMethods = Arrays.stream(bean.getClass().getDeclaredMethods()).filter(method-> !annotatedMethods.containsKey(method)).collect(Collectors.toSet());
|
|
|
+ if (allMethods.isEmpty() && annotatedMethods.isEmpty()) {
|
|
|
+ this.nonAnnotatedClasses.add(bean.getClass());
|
|
|
+ if (this.logger.isTraceEnabled()) {
|
|
|
+ this.logger.trace("No @DynamicMethod annotations found on bean type: " + bean.getClass());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Non-empty set of methods
|
|
|
+ for (Map.Entry<Method, Set<DynamicMethod>> entry : annotatedMethods.entrySet()) {
|
|
|
+ Method method = entry.getKey();
|
|
|
+ for (DynamicMethod dynamicMethod : entry.getValue()) {
|
|
|
+ processDynamicMethod(DynamicMethodWrapper.builder().name(dynamicMethod.name()).build(), method, bean, beanName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // empty set of methods
|
|
|
+ for (Method method : allMethods) {
|
|
|
+ processDynamicMethod(DynamicMethodWrapper.builder().name(method.getName()).build(), method, bean, beanName);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.logger.isDebugEnabled()) {
|
|
|
+ this.logger.debug(annotatedMethods.size() + " @DynamicMethod methods processed on bean '"
|
|
|
+ + beanName + "': " + annotatedMethods);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return bean;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void processDynamicMethod(DynamicMethodWrapper dynamicMethod, Method method, Object bean, String beanName) {
|
|
|
+ Method methodToUse = checkProxy(method, bean);
|
|
|
+ MethodEndpoint endpoint = new MethodEndpoint();
|
|
|
+ endpoint.setBean(bean);
|
|
|
+ endpoint.setMethod(method);
|
|
|
+ processMethod(endpoint, dynamicMethod, bean, methodToUse, beanName);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void processMethod(MethodEndpoint endpoint,
|
|
|
+ DynamicMethodWrapper dynamicMethod, Object bean, Object adminTarget, String beanName) {
|
|
|
+ endpoint.setBeanName(beanName);
|
|
|
+ DynamicMethodsEndpointRegistrar.registerEndpoint(endpoint,dynamicMethod);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查代理对象
|
|
|
+ *
|
|
|
+ * @param methodArg 方法
|
|
|
+ * @param bean Bean 实例
|
|
|
+ * @return 如果目标类已经被代理,则检查注解是否存在于类本身
|
|
|
+ */
|
|
|
+ private Method checkProxy(Method methodArg, Object bean) {
|
|
|
+ Method method = methodArg;
|
|
|
+ if (AopUtils.isJdkDynamicProxy(bean)) {
|
|
|
+ try {
|
|
|
+ method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
|
|
+ Class<?>[] proxiedInterfaces = ((Advised) bean).getProxiedInterfaces();
|
|
|
+ for (Class<?> iface : proxiedInterfaces) {
|
|
|
+ try {
|
|
|
+ method = iface.getMethod(method.getName(), method.getParameterTypes());
|
|
|
+ break;
|
|
|
+ } catch (NoSuchMethodException ignored) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (SecurityException ex) {
|
|
|
+ ReflectionUtils.handleReflectionException(ex);
|
|
|
+ } catch (NoSuchMethodException ex) {
|
|
|
+ throw new IllegalStateException(String.format(
|
|
|
+ "@DynamicMethod method '%s' found on bean target class '%s', " +
|
|
|
+ "but not found in any interface(s) for bean JDK proxy. Either " +
|
|
|
+ "pull the method up to an interface or switch to subclass (CGLIB) " +
|
|
|
+ "proxies by setting proxy-target-class/proxyTargetClass " +
|
|
|
+ "attribute to 'true'", method.getName(),
|
|
|
+ method.getDeclaringClass().getSimpleName()), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return method;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取类上的 {@link DynamicMethod} 注解
|
|
|
+ *
|
|
|
+ * @param clazz 类
|
|
|
+ * @return {@link DynamicMethod} 注解集合
|
|
|
+ */
|
|
|
+ private Collection<DynamicClass> findConsumerClass(Class<?> clazz) {
|
|
|
+ Set<DynamicClass> consumerMethods = new HashSet<>();
|
|
|
+ DynamicClass ann = AnnotationUtils.findAnnotation(clazz, DynamicClass.class);
|
|
|
+ if (ann != null) {
|
|
|
+ consumerMethods.add(ann);
|
|
|
+ }
|
|
|
+ return consumerMethods;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取方法上的 {@link DynamicMethod} 注解
|
|
|
+ *
|
|
|
+ * @param method 方法
|
|
|
+ * @return {@link DynamicMethod} 注解集合
|
|
|
+ */
|
|
|
+ private Set<DynamicMethod> findConsumerClass(Method method) {
|
|
|
+ Set<DynamicMethod> dynamicMethods = new HashSet<>();
|
|
|
+ DynamicMethod ann = AnnotatedElementUtils.findMergedAnnotation(method, DynamicMethod.class);
|
|
|
+ if (ann != null) {
|
|
|
+ dynamicMethods.add(ann);
|
|
|
+ }
|
|
|
+ return dynamicMethods;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int getOrder() {
|
|
|
+ return LOWEST_PRECEDENCE;
|
|
|
+ }
|
|
|
+}
|