代理技术分为很多种,如 Java 的动态代理 Proxy,还有基于 ASM 框架的 CGLIB 代理

一个 Demo

我们先看一个 Demo,这里的 AppConfig 并没有使用 @Configuration 注解

  1. @ComponentScan("org.wesoft.spring.cglib")
  2. public class AppConfig {
  3. @Bean
  4. public Foo foo(){
  5. System.out.println("foo init");
  6. return new Foo();
  7. }
  8. @Bean
  9. public Bar bar(){
  10. System.out.println("bar init");
  11. foo();
  12. return new Bar();
  13. }
  14. }
  15. public class App {
  16. public static void main(String[] args) {
  17. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  18. ac.register(AppConfig.class);
  19. ac.refresh();
  20. System.out.println(ac.getBean(AppConfig.class));
  21. }
  22. }
  23. foo init
  24. bar init
  25. foo init
  26. org.wesoft.spring.cglib.AppConfig@71bbf57e

当我们执行 main 方法的时候,可以看到 foo init,打印了两次,的确这是我们预期的,因为我们在 bar 中又调用了 foo 方法,但是,这样好么?foo 方法不应该是单例的么?为什么还会被实例化呢?

使用 @Configuration 注解

  1. @Configuration
  2. @ComponentScan("org.wesoft.spring.cglib")
  3. public class AppConfig {
  4. @Bean
  5. public Foo foo(){
  6. System.out.println("foo init");
  7. return new Foo();
  8. }
  9. @Bean
  10. public Bar bar(){
  11. System.out.println("bar init");
  12. foo();
  13. return new Bar();
  14. }
  15. }
  16. public class App {
  17. public static void main(String[] args) {
  18. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  19. ac.register(AppConfig.class);
  20. ac.refresh();
  21. // 如果加上 @Configuration 那么 这个类就被代理了
  22. System.out.println(ac.getBean(AppConfig.class));
  23. }
  24. }
  25. foo init
  26. bar init
  27. 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 代理,在原方法前后各增加了一句话

  1. public class A {
  2. public void print() {
  3. System.out.println("hello cglib");
  4. }
  5. }
  6. public static void main(String[] args) throws IllegalAccessException, InstantiationException {
  7. Enhancer enhancer = new Enhancer();
  8. enhancer.setSuperclass(A.class); // 设置父类
  9. enhancer.setCallback(new MethodInterceptor() {
  10. @Override
  11. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  12. System.out.println("cglib begin");
  13. Object returnType = methodProxy.invokeSuper(o, objects);
  14. System.out.println("cglib end");
  15. return returnType;
  16. }
  17. });
  18. A proxy = (A) enhancer.create();
  19. proxy.print();
  20. }
  21. // 输出结果
  22. cglib begin
  23. hello cglib
  24. cglib end

Spring 的 CGLIB 实现

通过这行代码完成了 CGLIB 代理,实现类是 ConfigurationClassPostProcessor

  1. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
  2. private static void invokeBeanFactoryPostProcessors(
  3. Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
  4. for (BeanFactoryPostProcessor postProcessor : postProcessors) {
  5. postProcessor.postProcessBeanFactory(beanFactory);
  6. }
  7. }
  8. @Override
  9. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  10. int factoryId = System.identityHashCode(beanFactory);
  11. if (this.factoriesPostProcessed.contains(factoryId)) {
  12. throw new IllegalStateException(
  13. "postProcessBeanFactory already called on this post-processor against " + beanFactory);
  14. }
  15. this.factoriesPostProcessed.add(factoryId);
  16. if (!this.registriesPostProcessed.contains(factoryId)) {
  17. // BeanDefinitionRegistryPostProcessor hook apparently not supported...
  18. // Simply call processConfigurationClasses lazily at this point then.
  19. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
  20. }
  21. // ★★★ 关键代码:进行 CGLIB 动态代理
  22. enhanceConfigurationClasses(beanFactory);
  23. // ★★★ 添加 BeanPostProcessor
  24. beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
  25. }

我们来研究一下 enhanceConfigurationClasses(beanFactory); 这段逻辑

  1. public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
  2. Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
  3. for (String beanName : beanFactory.getBeanDefinitionNames()) {
  4. BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
  5. // AppConfig 上是否配置了 @Configuration
  6. if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
  7. // 不能是一个抽象类
  8. if (!(beanDef instanceof AbstractBeanDefinition)) {
  9. throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
  10. beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
  11. }
  12. else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
  13. logger.info("Cannot enhance @Configuration bean definition '" + beanName +
  14. "' since its singleton instance has been created too early. The typical cause " +
  15. "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
  16. "return type: Consider declaring such methods as 'static'.");
  17. }
  18. // 将 配置类 放入 map 中
  19. configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
  20. }
  21. }
  22. // 如果 没有一个是 @Configuration 的 全配置类(full),就直接返回
  23. if (configBeanDefs.isEmpty()) {
  24. // nothing to enhance -> return immediately
  25. return;
  26. }
  27. // 开始完成代理
  28. ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
  29. for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
  30. AbstractBeanDefinition beanDef = entry.getValue();
  31. // If a @Configuration class gets proxied, always proxy the target class
  32. beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
  33. try {
  34. // Set enhanced subclass of the user-specified bean class
  35. // 原 AppConfig 类
  36. Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
  37. if (configClass != null) {
  38. // 代理的 AppConfig 类
  39. Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
  40. if (configClass != enhancedClass) {
  41. if (logger.isTraceEnabled()) {
  42. logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
  43. "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
  44. }
  45. // 关键代码:修改 BD 中的 beanClass 为 代理类
  46. beanDef.setBeanClass(enhancedClass);
  47. }
  48. }
  49. }
  50. catch (Throwable ex) {
  51. throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
  52. }
  53. }
  54. }
  1. @Override
  2. @Nullable
  3. public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
  4. MethodProxy cglibMethodProxy) throws Throwable {
  5. ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
  6. String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
  7. // Determine whether this bean is a scoped-proxy
  8. if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
  9. String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
  10. if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
  11. beanName = scopedBeanName;
  12. }
  13. }
  14. // To handle the case of an inter-bean method reference, we must explicitly check the
  15. // container for already cached instances.
  16. // First, check to see if the requested bean is a FactoryBean. If so, create a subclass
  17. // proxy that intercepts calls to getObject() and returns any cached bean instance.
  18. // This ensures that the semantics of calling a FactoryBean from within @Bean methods
  19. // is the same as that of referring to a FactoryBean within XML. See SPR-6602.
  20. // 是不是一个 factoryBean
  21. if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
  22. factoryContainsBean(beanFactory, beanName)) {
  23. Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
  24. if (factoryBean instanceof ScopedProxyFactoryBean) {
  25. // Scoped proxy factory beans are a special case and should not be further proxied
  26. }
  27. else {
  28. // It is a candidate FactoryBean - go ahead with enhancement
  29. return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
  30. }
  31. }
  32. // 是否是当前要执行的工厂方法
  33. if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
  34. // The factory is calling the bean method in order to instantiate and register the bean
  35. // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
  36. // create the bean instance.
  37. if (logger.isInfoEnabled() &&
  38. BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
  39. logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
  40. "assignable to Spring's BeanFactoryPostProcessor interface. This will " +
  41. "result in a failure to process annotations such as @Autowired, " +
  42. "@Resource and @PostConstruct within the method's declaring " +
  43. "@Configuration class. Add the 'static' modifier to this method to avoid " +
  44. "these container lifecycle issues; see @Bean javadoc for complete details.",
  45. beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
  46. }
  47. // 执行父类的方法
  48. return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
  49. }
  50. // 执行 bean 自己的方法,就是从 spring 中 getBean()
  51. // 从而保证了单例
  52. return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
  53. }

CGLIB @Bean 方法的代理逻辑

  1. 先 getBean,如果 bean 不存在则进行创建,如果是 @Bean 方法(工厂方法)则创建时,先将其加入正在创建的 ThreadLocal
  2. 执行 @Bean 方法
  3. 判断当前执行 bean 方法是否是当前线程正在创建的 bean 方法
    1. 如果当前执行 bean 方法是当前线程正在创建的 bean 方法,直接调用其父类的方法,然后再从 ThreadLocal 将其剔除
    2. 如果当前执行 bean 方法不是当前线程正在创建的 bean 方法,如,方法中调用其他 bean 方法,
      1. 则会从单例池中寻找所调用的 bean 方法
      2. 给当前正在执行的 bean 方法注册一个所调用的 bean 方法的依赖关系
      3. 返回从单例池找到的 bean 方法(则不在调用父类的方法,就保证了单例)