SpringProxyUtils.java 4.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
/**
 * @author lengleng
 * @date 2022/3/29
 */
package io.seata.spring.util;

import io.seata.common.util.CollectionUtils;
import io.seata.rm.tcc.remoting.parser.DubboUtil;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.AopUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * Proxy tools base on spring 主要解决 v1.4.2 兼容性问题 https://github.com/seata/seata/issues/3709
 *
 * @author zhangsen
 */
public class SpringProxyUtils {

	private SpringProxyUtils() {
	}

	/**
	 * Find target class class.
	 * @param proxy the proxy
	 * @return the class
	 * @throws Exception the exception
	 */
	public static Class<?> findTargetClass(Object proxy) throws Exception {
		if (proxy == null) {
			return null;
		}
		if (AopUtils.isAopProxy(proxy) && proxy instanceof Advised) {
			// #issue 3709
			final TargetSource targetSource = ((Advised) proxy).getTargetSource();
			if (!targetSource.isStatic()) {
				return targetSource.getTargetClass();
			}
			return findTargetClass(targetSource.getTarget());
		}
		return proxy.getClass();
	}

	public static Class<?>[] findInterfaces(Object proxy) throws Exception {
		if (AopUtils.isJdkDynamicProxy(proxy)) {
			AdvisedSupport advised = getAdvisedSupport(proxy);
			return getInterfacesByAdvised(advised);
		}
		else {
			return new Class<?>[] {};
		}
	}

	private static Class<?>[] getInterfacesByAdvised(AdvisedSupport advised) {
		Class<?>[] interfaces = advised.getProxiedInterfaces();
		if (interfaces.length > 0) {
			return interfaces;
		}
		else {
			throw new IllegalStateException("Find the jdk dynamic proxy class that does not implement the interface");
		}
	}

	/**
	 * Gets advised support.
	 * @param proxy the proxy
	 * @return the advised support
	 * @throws Exception the exception
	 */
	public static AdvisedSupport getAdvisedSupport(Object proxy) throws Exception {
		Field h;
		if (AopUtils.isJdkDynamicProxy(proxy)) {
			h = proxy.getClass().getSuperclass().getDeclaredField("h");
		}
		else {
			h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
		}
		h.setAccessible(true);
		Object dynamicAdvisedInterceptor = h.get(proxy);
		Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
		advised.setAccessible(true);
		return (AdvisedSupport) advised.get(dynamicAdvisedInterceptor);
	}

	/**
	 * Is proxy boolean.
	 * @param bean the bean
	 * @return the boolean
	 */
	public static boolean isProxy(Object bean) {
		if (bean == null) {
			return false;
		}
		// check dubbo proxy ?
		return DubboUtil.isDubboProxyName(bean.getClass().getName())
				|| (Proxy.class.isAssignableFrom(bean.getClass()) || AopUtils.isAopProxy(bean));
	}

	/**
	 * Get the target class , get the interface of its agent if it is a Proxy
	 * @param proxy the proxy
	 * @return target interface
	 * @throws Exception the exception
	 */
	public static Class<?> getTargetInterface(Object proxy) throws Exception {
		if (proxy == null) {
			throw new java.lang.IllegalArgumentException("proxy can not be null");
		}

		// jdk proxy
		if (Proxy.class.isAssignableFrom(proxy.getClass())) {
			Proxy p = (Proxy) proxy;
			return p.getClass().getInterfaces()[0];
		}

		return getTargetClass(proxy);
	}

	/**
	 * Get the class type of the proxy target object, if hadn't a target object, return
	 * the interface of the proxy
	 * @param proxy the proxy
	 * @return target interface
	 * @throws Exception the exception
	 */
	protected static Class<?> getTargetClass(Object proxy) throws Exception {
		if (proxy == null) {
			throw new java.lang.IllegalArgumentException("proxy can not be null");
		}
		// not proxy
		if (!AopUtils.isAopProxy(proxy)) {
			return proxy.getClass();
		}
		AdvisedSupport advisedSupport = getAdvisedSupport(proxy);
		Object target = advisedSupport.getTargetSource().getTarget();
		/*
		 * the Proxy of sofa:reference has no target
		 */
		if (target == null) {
			if (CollectionUtils.isNotEmpty(advisedSupport.getProxiedInterfaces())) {
				return advisedSupport.getProxiedInterfaces()[0];
			}
			else {
				return proxy.getClass();
			}
		}
		else {
			return getTargetClass(target);
		}
	}

	/**
	 * get the all interfaces of bean, if the bean is null, then return empty array
	 * @param bean the bean
	 * @return target interface
	 */
	public static Class<?>[] getAllInterfaces(Object bean) {
		Set<Class<?>> interfaces = new HashSet<>();
		if (bean != null) {
			Class<?> clazz = bean.getClass();
			while (!Object.class.getName().equalsIgnoreCase(clazz.getName())) {
				Class<?>[] clazzInterfaces = clazz.getInterfaces();
				interfaces.addAll(Arrays.asList(clazzInterfaces));
				clazz = clazz.getSuperclass();
			}
		}
		return interfaces.toArray(new Class[0]);
	}

}