代理技术分为很多种,如 Java 的动态代理 Proxy,还有基于 ASM 框架的 CGLIB 代理
一个 Demo
我们先看一个 Demo,这里的 AppConfig 并没有使用 @Configuration 注解
@ComponentScan("org.wesoft.spring.cglib")
public class AppConfig {
@Bean
public Foo foo(){
System.out.println("foo init");
return new Foo();
}
@Bean
public Bar bar(){
System.out.println("bar init");
foo();
return new Bar();
}
}
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
System.out.println(ac.getBean(AppConfig.class));
}
}
foo init
bar init
foo init
org.wesoft.spring.cglib.AppConfig@71bbf57e
当我们执行 main 方法的时候,可以看到 foo init,打印了两次,的确这是我们预期的,因为我们在 bar 中又调用了 foo 方法,但是,这样好么?foo 方法不应该是单例的么?为什么还会被实例化呢?
使用 @Configuration 注解
@Configuration
@ComponentScan("org.wesoft.spring.cglib")
public class AppConfig {
@Bean
public Foo foo(){
System.out.println("foo init");
return new Foo();
}
@Bean
public Bar bar(){
System.out.println("bar init");
foo();
return new Bar();
}
}
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// 如果加上 @Configuration 那么 这个类就被代理了
System.out.println(ac.getBean(AppConfig.class));
}
}
foo init
bar init
org.wesoft.spring.cglib.AppConfig$$EnhancerBySpringCGLIB$$623e8cd1@711f39f9
此时,我们 foo init 只打印了一次,我们在看 AppConfig,它被 CGLIB 代理了
所以我们知道了 @Configuration 注意的意义是:让 AppConfig(配置类)保证实现 Spring 的单例原则,所以进行了代理,保证 @Bean 产生的对象都是单例的
那么 @Configuration 是如何实现代理的呢?
CGLIB 代理
什么是 CGLIB 代理,它的实现是怎样的呢?和 JDK 动态代理有什么区别呢?
JDK 动态代理
JDK 动态代理是基于聚合,也就是针对实现了某一接口的类
CGLIB 代理
CGLIB 代理是基于继承,它可以实现某一个类的代理
CGLIB 案例
简单的实现一个类的 CGLIB 代理,在原方法前后各增加了一句话
public class A {
public void print() {
System.out.println("hello cglib");
}
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(A.class); // 设置父类
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib begin");
Object returnType = methodProxy.invokeSuper(o, objects);
System.out.println("cglib end");
return returnType;
}
});
A proxy = (A) enhancer.create();
proxy.print();
}
// 输出结果
cglib begin
hello cglib
cglib end
Spring 的 CGLIB 实现
通过这行代码完成了 CGLIB 代理,实现类是 ConfigurationClassPostProcessor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
// ★★★ 关键代码:进行 CGLIB 动态代理
enhanceConfigurationClasses(beanFactory);
// ★★★ 添加 BeanPostProcessor
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
我们来研究一下 enhanceConfigurationClasses(beanFactory);
这段逻辑
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// AppConfig 上是否配置了 @Configuration
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
// 不能是一个抽象类
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'.");
}
// 将 配置类 放入 map 中
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
// 如果 没有一个是 @Configuration 的 全配置类(full),就直接返回
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
// 开始完成代理
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
try {
// Set enhanced subclass of the user-specified bean class
// 原 AppConfig 类
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
if (configClass != null) {
// 代理的 AppConfig 类
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()));
}
// 关键代码:修改 BD 中的 beanClass 为 代理类
beanDef.setBeanClass(enhancedClass);
}
}
}
catch (Throwable ex) {
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// To handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances.
// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// This ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
// 是不是一个 factoryBean
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
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);
}
}
// 是否是当前要执行的工厂方法
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
// 执行父类的方法
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 执行 bean 自己的方法,就是从 spring 中 getBean()
// 从而保证了单例
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
CGLIB @Bean 方法的代理逻辑
- 先 getBean,如果 bean 不存在则进行创建,如果是 @Bean 方法(工厂方法)则创建时,先将其加入正在创建的 ThreadLocal
中 - 执行 @Bean 方法
- 判断当前执行 bean 方法是否是当前线程正在创建的 bean 方法
- 如果当前执行 bean 方法是当前线程正在创建的 bean 方法,直接调用其父类的方法,然后再从 ThreadLocal
将其剔除 - 如果当前执行 bean 方法不是当前线程正在创建的 bean 方法,如,方法中调用其他 bean 方法,
- 则会从单例池中寻找所调用的 bean 方法
- 给当前正在执行的 bean 方法注册一个所调用的 bean 方法的依赖关系
- 返回从单例池找到的 bean 方法(则不在调用父类的方法,就保证了单例)
- 如果当前执行 bean 方法是当前线程正在创建的 bean 方法,直接调用其父类的方法,然后再从 ThreadLocal