一、前置知识
1. Main方法添加代码:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
2. IDEA添加如下代码
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
3. 检验配置是否成功
二、简单使用CGLib
CGLib不能对final方法、类进行代理。 CGLib与动态代理区别:
Java动态代理
只能够对接口
进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;- Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用
ASM
框架直接对字节码进行操作,在类的执行过程中比较高效。
2.1 创建要被代理对象
public class Target {
public void sayHello() {
System.out.println("hello");
}
}
2.2 创建回调方法
public class MyTargetInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println(String.format("日志记录:当前类:%s,当前方法名称:%s, 参数:%s", obj.getClass(), method.getName(), args));
return methodProxy.invokeSuper(obj, args);
}
}
2.3 创建代理对象
public class EnHanceDemo {
public static void main(String[] args) {
// #1 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// #2 设置目标类型
enhancer.setSuperclass(Target.class);
// #3 设置回调
enhancer.setCallback(new MyTargetInterceptor());
Target target = (Target) enhancer.create();
target.sayHello();
}
}
// OUTPUT
// 日志记录:当前类:class com.clarencezero.c1_beanfactory.cglib.Target$$EnhancerByCGLIB$$ca012000,当前方法名称:sayHello, 参数:[Ljava.lang.Object;@579bb367
// hello
- 使用
InvocationHandler
可能会导致死循环
,因为invoce中调用的任何原代理方法,均会重新代理到invode方法中。- 可以使用
MethodInterceptor
避免上述的死循环
发生。- 当我们只对特定方法进行拦截,对其他方法放行时,只需要添加
CallbackFilter
即可:- 提供
BeanGenerator
在运行时动态创建Bean对象。- …
CallBackFilter
public class Target {
public String sayHello() {
System.out.println("hello");
return "hello";
}
}
Enhancer enhancer = new Enhancer();
// 设置CallBackFilter
CallbackHelper callbackHelper = new CallbackHelper(Target.class, new Class[0]) {
@Override
protected Object getCallback(Method method) {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){
return (FixedValue) () -> "Hello cglib";
}else{
return NoOp.INSTANCE;
}
}
};
enhancer.setSuperclass(Target.class);
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
Target proxy = (Target) enhancer.create();
System.out.println(proxy.sayHello());
System.out.println(proxy.toString());
System.out.println(proxy.hashCode());
// Hello cglib
// com.clarencezero.c1_beanfactory.cglib.Target$$EnhancerByCGLIB$$3cea8bfa@4dcbadb4
// 1305193908
三、Spring解析配置类示意图
下图简单描述Spring解析配置类流程,蓝色表示Spring对配置类对象进行CGLib代理,下面将着重分析这一步骤。
重点是第6步及第7步,在这里对带有注解 @Configuration
的配置类进行 CGLib
提升。
四、源码分析
4.1 回调ConfigurationClassPostProcessor#postProcessBeanFactory方法
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
// #1 循环遍历BeanDefinitionNames,获取全配置注解(通过判断属性configurationClass值是否为full)
// 若为全配置类,则添加到configBeanDefs集合中,供后续进行CGLib代理
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
if (IN_NATIVE_IMAGE) {
throw new BeanDefinitionStoreException("@Configuration classes need to be marked as proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
}
// #2 完成CGLib代理
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
Class<?> configClass = beanDef.getBeanClass();
// △2-1:完成对全注解的CGLib动态代理
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
// 2-2: 设置生成的代理对象类型
beanDef.setBeanClass(enhancedClass);
}
}
}
4.2 ConfigurationClassEnhancer#newEnhancer
/**
* 创建一个新的CGLIB {@link Enhancer}实例。
*/
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
// #1 设置当前类为父类
enhancer.setSuperclass(configSuperClass);
// #2 添加EnhancedConfiguration接口(EnhancedConfiguration接口继承BeanFactoryAware意识,可以得到BeanFactory)
// 有了BeanFactory工厂实例 配合动态代理就可以改变全配置类的方法的默认行为。全配置类@Bean注解,如果另一个@Bean在非全配置类中会实例化两个Bean,
// 但在全配置类中,通过动态代理,只会返回单例的Bean实例对象。
enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});
enhancer.setUseFactory(false);
// #3 设置命名策略
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// #4 通过CGLibAPI生成BeanFactory成员变量,同时设置setBeanFactory方法。相当于组装成员变量与方法行为。
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// #5 配置过滤器
// new BeanMethodInterceptor(), 方法过滤器
// new BeanFactoryAwareMethodInterceptor()
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
4.2.1 BeanMethodInterceptor
这个方法拦截配置类中被 @Bean
标注的方法,当第一次实例该对象时,拦截器(BeanMethodInterceptor)判断此时为第一次调用,所以会调用原方法(invokeSuper)实例化Bean对象,并完成正常的Spring生命周期流程。当第二次被其他@Bean注解的方法调用时,拦截器判断该方法已被调用过,所以会直接通过BeanFactory工厂取回。
这么做的目的是 确保正确处理语义
,比如@Bean注解默认表示为单例,如果没有代理,则调用此注解的方法会返回多个实例,这样就违背了@Bean设计原则。
/**
* 增强 {@link Bean @Bean} 方法以检查提供的BeanFactory是否存在此bean对象。
*/
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
// #1 从代理对象获取BeanFactory
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
// #2 从method中推断bean名称,为方法名称
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// #3 确定此bean是否为作用域代理
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// #4 处理FactoryBean
// 要处理bean间方法引用的情况,我们必须显式检查。
// 首先,检查所请求的bean是否为FactoryBean。
// 如果是这样,创建一个子类代理,以拦截对getObject()的调用并返回所有缓存的bean实例。
// 这确保了从@Bean方法中调用FactoryBean的语义与在XML中引用FactoryBean的语义相同。 请参阅SPR-6602。
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
// 从FactoryBean工厂中获取FactoryBean自身对象
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
} else {
// It is a candidate FactoryBean - go ahead with enhancement
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
// #5 检查给定的方法是否对应容器的当前调用的工厂方法
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// 5-1 (第一次)调用父类方法(原对象方法)
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 5-2 从BeanFactory工厂获取该方法所对应的实例
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
五、总结
简单总结了CBLib的API用法,理清使用CGLibAPI的脉络,至于细节,等有需要的时候再去深究。
本文简单总结了Spring解析 配置类
的流程,也了解CGLib在何时对配置类进行代理。
六、附录
CGLib生成的配置类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.clarencezero.c1_beanfactory.beanfactorypostprocessortest;
import java.lang.reflect.Method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
public class MyConfiguration$$EnhancerBySpringCGLIB$$88165706 extends MyConfiguration implements EnhancedConfiguration {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private MethodInterceptor CGLIB$CALLBACK_1;
private NoOp CGLIB$CALLBACK_2;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$b$0$Method;
private static final MethodProxy CGLIB$b$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$a$1$Method;
private static final MethodProxy CGLIB$a$1$Proxy;
private static final Method CGLIB$setBeanFactory$6$Method;
private static final MethodProxy CGLIB$setBeanFactory$6$Proxy;
public BeanFactory $$beanFactory;
static void CGLIB$STATICHOOK3() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.clarencezero.c1_beanfactory.beanfactorypostprocessortest.MyConfiguration$$EnhancerBySpringCGLIB$$88165706");
Class var1;
CGLIB$setBeanFactory$6$Method = ReflectUtils.findMethods(new String[]{"setBeanFactory", "(Lorg/springframework/beans/factory/BeanFactory;)V"}, (var1 = Class.forName("org.springframework.beans.factory.BeanFactoryAware")).getDeclaredMethods())[0];
CGLIB$setBeanFactory$6$Proxy = MethodProxy.create(var1, var0, "(Lorg/springframework/beans/factory/BeanFactory;)V", "setBeanFactory", "CGLIB$setBeanFactory$6");
Method[] var10000 = ReflectUtils.findMethods(new String[]{"b", "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/B;", "a", "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/A;"}, (var1 = Class.forName("com.clarencezero.c1_beanfactory.beanfactorypostprocessortest.MyConfiguration")).getDeclaredMethods());
CGLIB$b$0$Method = var10000[0];
CGLIB$b$0$Proxy = MethodProxy.create(var1, var0, "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/B;", "b", "CGLIB$b$0");
CGLIB$a$1$Method = var10000[1];
CGLIB$a$1$Proxy = MethodProxy.create(var1, var0, "()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/A;", "a", "CGLIB$a$1");
}
final B CGLIB$b$0() {
return super.b();
}
public final B b() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (B)var10000.intercept(this, CGLIB$b$0$Method, CGLIB$emptyArgs, CGLIB$b$0$Proxy) : super.b();
}
final A CGLIB$a$1() {
return super.a();
}
public final A a() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (A)var10000.intercept(this, CGLIB$a$1$Method, CGLIB$emptyArgs, CGLIB$a$1$Proxy) : super.a();
}
final void CGLIB$setBeanFactory$6(BeanFactory var1) throws BeansException {
super.setBeanFactory(var1);
}
public final void setBeanFactory(BeanFactory var1) throws BeansException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_1;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_1;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$setBeanFactory$6$Method, new Object[]{var1}, CGLIB$setBeanFactory$6$Proxy);
} else {
super.setBeanFactory(var1);
}
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -2136804029:
if (var10000.equals("b()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/B;")) {
return CGLIB$b$0$Proxy;
}
break;
case -1528727197:
if (var10000.equals("a()Lcom/clarencezero/c1_beanfactory/beanfactorypostprocessortest/A;")) {
return CGLIB$a$1$Proxy;
}
break;
case 2095635076:
if (var10000.equals("setBeanFactory(Lorg/springframework/beans/factory/BeanFactory;)V")) {
return CGLIB$setBeanFactory$6$Proxy;
}
}
return null;
}
public MyConfiguration$$EnhancerBySpringCGLIB$$88165706() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
MyConfiguration$$EnhancerBySpringCGLIB$$88165706 var1 = (MyConfiguration$$EnhancerBySpringCGLIB$$88165706)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
Callback[] var10001 = (Callback[])var10000;
var1.CGLIB$CALLBACK_2 = (NoOp)((Callback[])var10000)[2];
var1.CGLIB$CALLBACK_1 = (MethodInterceptor)var10001[1];
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0];
}
}
static {
CGLIB$STATICHOOK4();
CGLIB$STATICHOOK3();
}
static void CGLIB$STATICHOOK4() {
}
}