Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.zodiac.autoconfigure.proxy.SmartProxyAutoConfiguration Maven / Gradle / Ivy
package org.zodiac.autoconfigure.proxy;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.slf4j.Logger;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.util.ClassUtils;
import org.zodiac.autoconfigure.application.main.ApplicationMainProperties;
import org.zodiac.commons.constants.SystemPropertiesConstants;
import org.zodiac.commons.logging.SmartSlf4jLoggerFactory;
import org.zodiac.commons.proxy.DispatcherProxyInvocation;
import org.zodiac.commons.proxy.InvocationChainFilter;
import org.zodiac.commons.util.ArrayUtil;
import org.zodiac.commons.util.Asserts;
import org.zodiac.commons.util.Classes;
import org.zodiac.commons.util.Colls;
import org.zodiac.commons.util.Reflections;
import org.zodiac.commons.util.lang.Strings;
import org.zodiac.core.proxy.DispatcherFactoryBeanSmartProxyInvocation;
import org.zodiac.core.proxy.SmartFactoryBeanProxy;
import org.zodiac.core.proxy.SmartProxy;
import org.zodiac.core.proxy.SmartProxyConfigurator;
import org.zodiac.core.proxy.SmartProxyFilter;
import org.zodiac.core.proxy.SmartProxyFor;
import org.zodiac.core.proxy.SmartProxyIgnored;
/**
* Intelligent AOP enhanced proxy configurator supports proxy creation of
* various types of beans, such as: Simple Object, {@link FactoryBean} and
* {@link org.mybatis.spring.mapper.MapperFactoryBean} etc.
*
*/
@SpringBootConfiguration
@Order(value = Ordered.LOWEST_PRECEDENCE - 1)
@ConditionalOnProperty(value = SystemPropertiesConstants.Zodiac.SPRING_MAIN_SMART_PROXY_ENABLED, havingValue = "true", matchIfMissing = true)
public class SmartProxyAutoConfiguration implements InitializingBean, BeanPostProcessor, SmartProxyConfigurator {
/**
* Excludes bean class base packages.
*/
public static final String[] EXCLUDE_BASE_PACKAGES = { "org.springframework", "java.", "javax." };
// JDK proxy interface field.
public static final Field PROXY_INTERFACE_FIELD = Reflections.findFieldNullable(Proxy.class, "h", InvocationHandler.class);
// Mybatis mapper proxy classes and methods and fields.
public static final Class> MAPPER_FACTORY_BEAN_CLASS = Classes.resolveClassNameNullable("org.mybatis.spring.mapper.MapperFactoryBean");
public static final Field MAPPER_FACTORY_BEAN_INTERFACE_FIELD = Reflections.findFieldNullable(MAPPER_FACTORY_BEAN_CLASS, "mapperInterface",
Class.class);
public static final Class> MAPPER_PROXY_CLASS = Classes.resolveClassNameNullable("org.apache.ibatis.binding.MapperProxy");
public static final Field MAPPER_PROXY_INTERFACE_FIELD = Reflections.findFieldNullable(MAPPER_PROXY_CLASS, "mapperInterface", Class.class);
protected final Logger log = SmartSlf4jLoggerFactory.getLogger(getClass());
private final Map, List> knownProxiedMapping = Colls.concurrentMap(4);
// @Value("${" + KEY_SMART_PROXY + ".base-packages:}")
private String[] basePackages;
//@Autowired
private DefaultListableBeanFactory beanFactory;
//@Autowired(required = false)
private List processors;
public SmartProxyAutoConfiguration(ApplicationMainProperties applicationMainProperties,
DefaultListableBeanFactory beanFactory, ObjectProvider> processorsProvider) {
this.basePackages = applicationMainProperties.getSmartProxy().getBasePackages();
this.beanFactory = beanFactory;
this.processors = processorsProvider.getIfAvailable();
}
@Override
public void afterPropertiesSet() throws Exception {
this.processors = Colls.safeList(processors);
AnnotationAwareOrderComparator.sort(processors);
}
/**
* {@link AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization(Object, String)}
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
/*Gets actual original target class.*/
Class> targetClass = getOriginalActualTargetClass(bean, beanName);
if (skipNoShouldProxy(bean, beanName, targetClass)) {
return bean;
}
if (Objects.isNull(targetClass)) {
/*Unable proxy*/
log.warn("Skip bean that cannot be proxied, because cannot get actual original target class. - '{}'", bean);
return bean;
}
/*Skip exclude beans*/
if (skipExcludeBean(bean, beanName, targetClass)) {
return bean;
}
/*Add known proxied mappings.*/
boolean hasSupportProxied = false;
for (SmartProxyFilter p : processors) {
if (p.supportTypeProxy(bean, targetClass)) {
hasSupportProxied = true;
/*Add proxies handler mapping.*/
addKnownProxiedMapping(targetClass, p);
}
}
if (hasSupportProxied) {
/*Wrap to proxy bean.*/
return wrapEnhanceProxyBean(bean, beanName, targetClass);
}
return bean;
}
/**
* Wrap object bean to enhanced proxy bean.
*
* @param bean bean object
* @param beanName bean name
* @param targetClass target class
* @return proxyed object
*/
private Object wrapEnhanceProxyBean(Object bean, String beanName, final Class> targetClass) {
Enhancer enhancer = new Enhancer() {
@Override
protected void setNamePrefix(String namePrefix) {
Class> proxyClass = (bean instanceof FactoryBean) ? SmartFactoryBeanProxy.class : SmartProxy.class;
super.setNamePrefix(targetClass.getName().concat(Classes.CGLIB_CLASS_SEPARATOR).concat(proxyClass.getSimpleName()));
}
};
/*Sets enhanced interfaces or superClass.*/
List> enhancedInterfaces = new ArrayList<>(4);
if (targetClass.isInterface()) { // Is interface
enhancedInterfaces.add(targetClass);
} else {
/*Is class*/
/*[START] extension logic.(In order to enhance the class modified by final)*/
SmartProxyFor proxyFor = AnnotationUtils.findAnnotation(targetClass, SmartProxyFor.class);
if (Objects.nonNull(proxyFor)) {
/*Priority is given to display defined enhanced interfaces.*/
Class>[] interfaces = proxyFor.interfaces();
if (ArrayUtil.emptyArray(interfaces)) {
/*Fallback, using all interfaces of the targetClass.*/
interfaces = targetClass.getInterfaces();
}
enhancedInterfaces.addAll(Arrays.asList(interfaces));
/*[END] extension logic.*/
} else {
/*No interfaces*/
Asserts.isTrue(!Modifier.isFinal(targetClass.getModifiers()),
() -> String.format("Enhance proxy target class must a interface or not final type. - %s", targetClass));
enhancer.setSuperclass(targetClass);
}
}
if (bean instanceof FactoryBean) {
enhancedInterfaces.add(SmartFactoryBeanProxy.class);
enhancer.setCallback(new DispatcherFactoryBeanSmartProxyInvocation(beanFactory, this, bean, beanName, targetClass));
} else {
enhancedInterfaces.add(SmartProxy.class);
enhancer.setCallback(new DispatcherProxyInvocation(beanFactory, this, beanName, targetClass, () -> bean));
}
enhancer.setInterfaces(enhancedInterfaces.toArray(new Class[0]));
final Object proxy = enhancer.create();
log.info("Created smart proxy: '{}' of actual original target class: '{}'", proxy, targetClass);
return proxy;
}
/**
* Addidition known proxied and {@link SmartProxyFilter} mappings.
*
* @param targetClass target class
* @param processor filter
*/
private void addKnownProxiedMapping(Class> targetClass, SmartProxyFilter processor) {
List processors = knownProxiedMapping.get(targetClass);
if (Objects.isNull(processors)) {
processors = new ArrayList<>(4);
}
if (!processors.contains(processor)) {
processors.add(processor);
}
knownProxiedMapping.put(targetClass, processors);
}
@Override
public final List getProcessors(Class> targetClass) {
return Colls.safeList(knownProxiedMapping.get(targetClass));
}
/**
* Whether kip not should proxy bean.
*
* @param bean bean object
* @param beanName bean name
* @param targetClass type of target object being proxied
* @return skipped or not
*/
protected boolean skipNoShouldProxy(Object bean, String beanName, final Class> targetClass) {
if (null == bean || Strings.blank(beanName) || null == targetClass) {
return false;
}
SmartProxyIgnored proxyIgnored = AnnotationUtils.findAnnotation(targetClass, SmartProxyIgnored.class);
if (null != proxyIgnored) {
/*Ignored class*/
return true;
}
if (Reflections.hasDeaultConstructor(targetClass)) {
/*
No default constructor, is it against for CGLIB.
In this case, the proxy throws an exception {@code java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given}.
Refer to Superclass has no null constructors but no arguments were given
*/
return true;
}
if (Strings.startsWithAny(bean.getClass().getName(), EXCLUDE_BASE_PACKAGES)) {
/*Ignore spring internal bean?*/
return true;
} else if (Classes.anyTypeOf(bean, SmartFactoryBeanProxy.class, SmartProxy.class)) {
/*Proxied bean? (Only SmartProxy proxy is allowed once)*/
return true;
}
return false;
}
/**
* Skip exclude beans by base packages.
*
* @param bean bean object
* @param beanName bean name
* @param targetClass target class
* @return skipped or not
*/
protected boolean skipExcludeBean(Object bean, String beanName, Class> targetClass) {
return !(ArrayUtil.emptyArray(basePackages) || Strings.startsWithAny(Classes.getPackageName(targetClass), basePackages));
}
/**
* Get the actual type of the bean according to the default policy. If the
* bean has been proxied, it represents the interface or class of the actual
* proxy.
*
* @param bean bean object
* @param beanName bean name
* @return target class
*/
@SuppressWarnings("rawtypes")
protected Class> getOriginalActualTargetClass(Object bean, String beanName) {
if (bean.getClass().getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
/*CGLIB-proxy-bean*/
Class> targetClass = bean.getClass().getSuperclass();
if (Objects.nonNull(targetClass)) {
return targetClass;
}
Class>[] interfaces = bean.getClass().getInterfaces();
return (Objects.nonNull(interfaces) && interfaces.length > 0) ? interfaces[0] : null;
} else if (Proxy.class.isInstance(bean)) {
/*JDK proxy bean*/
return Reflections.getField(PROXY_INTERFACE_FIELD, bean, true).getClass();
} else if (bean instanceof FactoryBean) {
/*FactoryBean*/
/*e.g: org.mybatis.spring.mapper.MapperFactoryBean#getObjectType()*/
if (Objects.nonNull(MAPPER_FACTORY_BEAN_CLASS) && MAPPER_FACTORY_BEAN_CLASS.isInstance(bean)) {
Object proxyInvocation = null;
try {
proxyInvocation = ((FactoryBean) bean).getObject();
} catch (Exception e) {
throw new IllegalStateException(e);
}
if (Objects.nonNull(proxyInvocation) && Proxy.class.isInstance(proxyInvocation)) {
Object mapperProxy = Reflections.getField(PROXY_INTERFACE_FIELD, proxyInvocation, true);
/*e.g: org.apache.ibatis.binding.MapperProxy@6cb2ee5c ==> com.sun.proxy.$Proxy96*/
if (Objects.nonNull(MAPPER_PROXY_CLASS) && MAPPER_PROXY_CLASS.isInstance(mapperProxy)) {
return Reflections.getField(MAPPER_PROXY_INTERFACE_FIELD, mapperProxy, true);
}
return mapperProxy.getClass();
}
}
return ((FactoryBean) bean).getObjectType();
}
return bean.getClass();
}
}