How to solve circular dependency in spring dynamic proxy? Why use level 3 caching?

Programmer Xiaohang 2021-02-23 16:47:38
solve circular dependency spring dynamic


Preface

Research on 『 Spring How to solve circular dependency 』 When , come to know Spring With the help of Three level cache To solve the problem of circular dependency .

Also in the last section left a question :

  1. Why do circular dependencies use level 3 caching ? Instead of using a second level cache ?
  2. AOP Does dynamic proxy have any effect on circular dependency ?

This article is also around the above content .

Notes are also being collated , It might have been a little messy before .

Step by step , Take a look at circular dependency ?

Let's start with a brief review Bean The creation process of , Of course, you can also read it directly 『 Single case Bean The creation of 』 This article .

But consider reading the last article before reading this article 、Debug wait , It will take time , Therefore, a small part of this article will make a brief summary of the previous article , It is also equivalent to a summary of my own learning .

Let's review the concept of level 3 caching .

singletonObjects: First level cache , Store singleton objects ,Bean Instantiated , Initialization complete . earlySingletonObjects: Second level cache , Storage singletonObject, This Bean Instantiate , It's not initialized yet . singletonFactories: Three level cache , Storage singletonFactory.

Bean The creation process of

@Service
public class CircularServiceA {
private String fieldA = " Field A";
}

Single case Bean The creation process of

Through the above process , It can be seen that Spring Creating Bean The focus of the process is AbstractAutowireCapableBeanFactory The following three steps in :

  1. Instantiation createBeanInstance: Where instantiation Bean Also on Bean Assign a value , Like in the example fieldA Fields will be assigned here .
  2. Attribute injection populateBean: It can be understood as right Bean The properties inside are assigned values .( Will rely on others Bean)
  3. initialization initializeBean: Perform initialization and Bean Post processor for .

Instantiation assignment source code can be read : BeanUtils.instantiateClass(constructorToUse)

If you have to rely on others Bean Well ?

Then if CircularServiceA Dependent on other Bean Well ?

@Service
public class CircularServiceA {
private String fieldA = " Field A";
@Autowired
private CircularServiceB circularServiceB;
}
@Service
public class CircularServiceB {
}

A Rely on B

When A Rely on B When , stay createBeanInstance This step , It's not true B Assign properties .

But in populatedBean Look for dependencies here , And create B.

Creation process under circular dependency

The scenario of circular dependency , It has been explained in the last article , Here is just a picture to illustrate .

@Service
public class CircularServiceA {
private String fieldA = " Field A";
@Autowired
private CircularServiceB circularServiceB;
}
@Service
public class CircularServiceB {
@Autowired
private CircularServiceA circularServiceA;
}

A B Cyclic dependence

stay A and B In the scenario of circular dependency :

B populatedBean Find dependencies A When , From the first level cache, although not get A, But found A In creation .

here , Get... From the third level cache A Of singletonFactory Call factory methods , establish getEarlyBeanReference A And return .

B Quote to A ,B It can be initialized , then A It can also be initialized .

Can L2 cache solve circular dependency

Through the graph above , Analyze carefully , In fact, remove the L2 cache , stay B Try to get A Go straight back to A Example , Is it possible ?

The answer is : Tolerable !

But why use the third level cache ?

A lot of information on the Internet is related to dynamic agents , Let's continue to analyze from the aspect of dynamic agent .

Dynamic proxy scenarios

stay JavaConfig( Configuration class ) Add @EnableAspectJAutoProxy annotation , Turn on AOP , adopt Debug Take a step-by-step look at the impact of dynamic proxies on circular dependencies .

Dynamic proxy ,Bean The creation process of

@Service
public class CircularServiceA {
private String fieldA = " Field A";
public void methodA() {
System.out.println(" Method A perform ");
}
}
@Aspect
@Component
public class AspectA {
@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
public void beforeA() {
System.out.println("beforeA perform ");
}
}

Only A Under the circumstances , to A Add section , Start Debug.

The previous process is the same , stay initializeBean It's starting to make a difference .

This step requires initialization Bean And implement Bean Post processor for .

Execute post processor

One of the processors is :AnnotationAwareAspectJAutoProxyCreator In fact, it's an annotation section , Will jump to AbstractAutoProxyCreator Class postProcessAfterInitialization Method

postProcessAfterInitialization

As shown in the figure :wrapIfNecessary Method will determine whether the proxy condition is met , Yes, return a proxy object , Otherwise, return to the current Bean.

Subsequent calls getProxy 、createAopProxy wait , Finally, it goes to the next part .

It's going to end up here ,AOP We will not take a closer look at the agency related issues .

Let's go all the way , until initializeBean end of execution .

A Replaced with a proxy object

At this time, we found that :A Replaced with a proxy object .

therefore doCreateBean return , And then put it in the first level cache are proxy objects .

The red box shows the difference

Dynamic agents with circular dependencies

This time, turn circular dependency on :

@Service
public class CircularServiceA {
private String fieldA = " Field A";
@Autowired
private CircularServiceB circularServiceB;
public void methodA() {
System.out.println(" Method A perform ");
}
}
@Aspect
@Component
public class AspectA {
@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
public void beforeA() {
System.out.println("beforeA perform ");
}
}
@Service
public class CircularServiceB {
@Autowired
private CircularServiceA circularServiceA;
public void methodB() {
}
}
@Aspect
@Component
public class AspectB {
@Before("execution(public void com.liuzhihang.circular.CircularServiceB.methodB())")
public void beforeB() {
System.out.println("beforeB perform ");
}
}

Start Debug, Some of the previous column flows , It's no different from normal . And the only difference is , establish B When , You need to get it from the L3 cache A.

At this time in getSingleton Method is called :singletonObject = singletonFactory.getObject();

B Property assignment , Get... From L3 cache A

Sometimes I'm confused singletonFactory.getObject() Where is called ?

The third level cache gets objects

So this block calls getEarlyBeanReference, Start traversal execution BeanPostProcessor.

getEarlyBeanReference

getEarlyBeanReference

notice wrapIfNecessary I see ! This will get a Proxy object .

That is to say, return to , And put it in the second level cache is a A Proxy object of .

such B And it's done !

To A It's time to initialize and execute the postprocessor ! because A There are also agents , therefore A It will also be carried out to postProcessAfterInitialization This part !

Judge the L2 cache

But in execution wrapIfNecessary Before , It will first determine whether the proxy object's tag cache is available A 了 .

this.earlyProxyReferences.remove(cacheKey) != bean

But what this gets is A Proxy object of . Must be false . So it won't be generated again A Proxy object of .

agent - Cyclic dependence

summary

You can see , Under cyclic dependence , The difference between agency and non agency is :

singletonObject = singletonFactory.getObject();

In the case of circular dependency B Medium A assignment :

  1. No agency :getObject Go straight back to the original Bean
  2. There are agents. :getObject Returned is the proxy object

And then put it all in Second level cache .

Why Level 3 caching ?

  1. Let's take out the third level cache

After removing the L3 cache ,Bean Create directly earlySingletonObjects, It seems that it can also .

If there is an agent , stay earlySingletonObjects Just drop the proxy object .

But it can lead to a problem : In the instantiation phase, the postprocessor has to be executed , There is a judgment AnnotationAwareAspectJAutoProxyCreator And create a proxy object .

Think about it , Would it be right Bean The life cycle of .

Again , First create singletonFactory Is that : When you really need instantiation , Reuse singletonFactory.getObject() obtain Bean perhaps Bean Agent for . It's equivalent to delaying instantiation .

  1. Let's remove the L2 cache

If the L2 cache is removed , You need to be directly in singletonFactory.getObject() Stage initialization complete , And put it in the first level cache .

B and C All depend on A

There's a scene ,B and C It all depends on A.

You know, with an agent singletonFactory.getObject() Get the proxy object .

Getting proxy objects multiple times is different

And multiple calls singletonFactory.getObject() The proxy object returned is different , It will lead to B and C Depending on different A.

So if you get B After that, put it directly into the first level cache , then C And then get ?

The first level buffer stores those that have been initialized Bean, Need to know A Rely on B and C ,A It's not initialized yet .

Summary

There are many scenarios for circular dependency , This article is only through Debug , To learn about circular dependencies and AOP The relationship between , And understand why you need to use level 3 caching .

Of course ,Spring What was it like at the beginning of the design ? How to develop into the present one step by step ?

I can't study it slowly , So only in the current version , To speculate on the author's intentions .

deficiencies , A lot of correct .

- <End /> -

This article is from WeChat official account. - Programmer Xiaohang (gh_liuzhihang) , author : Liu Zhihang

The source and reprint of the original text are detailed in the text , If there is any infringement , Please contact the yunjia_community@tencent.com Delete .

Original publication time : 2021-01-29

Participation of this paper Tencent cloud media sharing plan , You are welcome to join us , share .

版权声明
本文为[Programmer Xiaohang]所创,转载请带上原文链接,感谢
https://javamana.com/2021/02/20210223164213528y.html

  1. J2EE
  2. Vue uses SDK to upload seven cows
  3. k8s-dns
  4. JavaScript mailbox verification - regular verification
  5. k8s-dashboard
  6. How many questions can you answer?
  7. Spring annotation -- transactional
  8. [k8s cluster] construction steps
  9. k8s-kubeadm
  10. k8s-etcd
  11. Using HashMap to improve search performance in Java
  12. There is no class problem when Maven publishes jar package
  13. JavaScriptBOM操作
  14. J2EE
  15. k8s-prometheus-memory
  16. k8s-prometheus disk
  17. k8s-prometheus
  18. JavaScript BOM operation
  19. k8s-prometheus-memory
  20. k8s-prometheus disk
  21. k8s-prometheus
  22. Linux Disk Command
  23. Linux FS
  24. 使用docker-compose &WordPress建站
  25. Linux Command
  26. This time, thoroughly grasp the depth of JavaScript copy
  27. Linux Disk Command
  28. Linux FS
  29. Using docker compose & WordPress to build a website
  30. Linux Command
  31. 摊牌了,我 HTTP 功底贼好!
  32. shiro 报 Submitted credentials for token
  33. It's a showdown. I'm good at it!
  34. Shiro submitted credentials for token
  35. Linux Stress test
  36. Linux Root Disk Extension
  37. Linux Stress test
  38. Linux Root Disk Extension
  39. Redis高级客户端Lettuce详解
  40. springboot学习-综合运用(一)
  41. 忘记云服务器上MySQL数据库的root密码时如何重置密码?
  42. Detailed explanation of lettuce, an advanced client of redis
  43. Springboot learning integrated application (1)
  44. Linux File Recover
  45. Linux-Security
  46. How to reset the password when you forget the root password of MySQL database on the cloud server?
  47. Linux File Recover
  48. Linux-Security
  49. LiteOS:盘点那些重要的数据结构
  50. Linux Memory
  51. Liteos: inventory those important data structures
  52. Linux Memory
  53. 手把手教你使用IDEA2020创建SpringBoot项目
  54. Hand in hand to teach you how to create a springboot project with idea2020
  55. spring boot 整合swagger2生成API文档
  56. Spring boot integrates swagger2 to generate API documents
  57. linux操作系统重启后 解决nginx的pid消失问题
  58. Solve the problem of nginx PID disappearing after Linux operating system restart
  59. JAVA版本号含义
  60. The meaning of java version number