sách gpt4 ăn đã đi

Giải quyết vấn đề không thể khởi động dự án có thể làm mới cho phép cấu hình Spring-Cloud-OpenFeign

In lại Tác giả: qq735679552 Thời gian cập nhật: 28-09-2022 22:32:09 30 4
mua khóa gpt4 giày nike

CFSDN nhấn mạnh vào giá trị tạo ra nguồn mở và chúng tôi cam kết xây dựng nền tảng chia sẻ tài nguyên để mọi nhân viên CNTT có thể tìm thấy thế giới tuyệt vời của bạn tại đây.

Bài viết trên blog CFSDN này giải quyết vấn đề không thể khởi động dự án có thể làm mới khi bật cấu hình Spring-Cloud-OpenFeign. Nó được tác giả sưu tầm và biên soạn. Nếu bạn quan tâm đến bài viết này, hãy nhớ thích nó.

Giải quyết vấn đề không thể khởi động dự án có thể làm mới cho phép cấu hình Spring-Cloud-OpenFeign

Bài viết này liên quan đến thiết kế và nguyên tắc cơ bản cũng như vị trí vấn đề. Nó tương đối sâu và dài nên được chia thành hai bài:

  • Trên: Mô tả ngắn gọn về vấn đề và nguyên tắc của Spring Cloud RefreshScope
  • Next: Các lỗi hiện tại do spring-cloud-openfeign + spring-cloud-sleuth gây ra và cách khắc phục

Gần đây, tôi muốn nhận ra rằng cấu hình của OpenFeign có thể được làm mới động trong dự án (chủ yếu là cấu hình Tùy chọn của Feign), ví dụ:

?
1
2
3
4
5
6
7
8
giả vờ:
     khách hàng:
      cấu hình:
        mặc định :
          # Hết thời gian liên kết
          kết nốiThời gian chờ: 500
          #Đọc hết thời gian
          thời gian chờ đọc: 8000

Chúng tôi có thể nhận thấy rằng thời gian chờ để gọi một FeignClient nhất định là không hợp lý và cần phải sửa đổi tạm thời. Chúng tôi không muốn khởi động lại quy trình hoặc làm mới toàn bộ ApplicationContext vì điều này, vì vậy chúng tôi đã đặt phần cấu hình này vào spring-cloud. -config và sử dụng tính năng làm mới động. Cơ chế được làm mới. Bản chính thức cung cấp phương thức cấu hình này, tham khảo: Tài liệu chính thức - Hỗ trợ Spring @RefreshScope.

Tức là thêm cấu hình cho dự án:

?
1
feign.client.refresh-enabled: ĐÚNG VẬY

Tuy nhiên, trong dự án của chúng tôi, sau khi thêm cấu hình này, quá trình khởi động không thành công và đã báo cáo lỗi không thể tìm thấy các hạt liên quan:

Nguyên nhân do: org.springframework.beans.factory.NoSuchBeanDefinitionException: Không có bean nào có tên 'feign.Request.Options-testService1Client' có sẵn tại org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:863) tại org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1344) tại org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309) tại org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) tại org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1160) tại org.springframework.cloud.openfeign.FeignContext.getInstance(FeignContext.java:57) tại org.springframework.cloud.openfeign.FeignClientFactoryBean.getOptionsByName(FeignClientFactoryBean.java:363) tại org.springframework.cloud.openfeign.FeignClientFactoryBean.configureUsingConfiguration(FeignClientFactoryBean.java:195) tại org.springframework.cloud.openfeign.FeignClientFactoryBean.configureFeign(FeignClientFactoryBean.java:158) tại org.springframework.cloud.openfeign.FeignClientFactoryBean.feign(FeignClientFactoryBean.java:132) tại org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:382) tại org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:371) tại org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:235) tại org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1231) tại org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1173) tại org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) tại org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ... 74 nữa 。

Phân tích vấn đề

Thông qua tên Bean này, chúng ta thực sự có thể thấy rằng Bean này chính là Feign.Options mà chúng tôi đề cập lần đầu tiên là được làm mới động, chứa các cấu hình như thời gian chờ kết nối và thời gian chờ đọc. Phần sau tên là contextId trong chú thích @FeignClient phía trên FeignClient mà chúng tôi đã tạo.

Khi tạo FeignClient, bạn cần tải Feign.Options Bean này. Mỗi FeignClient có ApplicationContext riêng. Bean Feign.Options này thuộc về ApplicationContext riêng biệt của mỗi FeignClient. Điều này được thực hiện thông qua NamedContextFactory của Spring Cloud. Để phân tích sâu hơn về NamedContextFactory, các bạn có thể tham khảo bài viết này của mình:

Bật làm mới động cho cấu hình OpenFeign thực sự có nghĩa là làm mới Feign.Options Bean của mỗi FeignClient. Vậy làm thế nào để đạt được nó? Trước tiên chúng ta hãy xem việc triển khai các hạt làm mới động của đám mây mùa xuân. Đầu tiên chúng ta cần hiểu Scope là gì.

Phạm vi đậu

Hiểu theo nghĩa đen thì Scope là phạm vi của Bean. Từ quan điểm triển khai, Phạm vi là cách chúng ta có được Bean khi chúng ta có được nó.

Spring framework đi kèm với hai Scope quen thuộc, đó là singleton và nguyên mẫu. Singleton có nghĩa là mỗi khi bạn nhận được Bean từ BeanFactory (getBean), cùng một đối tượng sẽ được trả về mỗi lần cho cùng một Bean, tức là chế độ singleton. Nguyên mẫu có nghĩa là mỗi khi bạn nhận được Bean từ BeanFactory, một đối tượng mới sẽ được tạo và trả về cho cùng một Bean mỗi lần, tức là ở chế độ xuất xưởng.

Đồng thời, chúng ta cũng có thể mở rộng Phạm vi theo nhu cầu của bản thân và xác định cách lấy Đậu. Để đưa ra một ví dụ đơn giản, chúng tôi tùy chỉnh TestScope. Phạm vi tùy chỉnh trước tiên cần xác định một lớp triển khai giao diện org.springframework.beans.factory.config.Scope để xác định các hoạt động liên quan đến việc lấy các hạt trong Phạm vi này.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
công cộng giao diện Phạm vi {
     //Lấy Bean này, nó sẽ được gọi khi BeanFactory.getBean
     Đối tượng get(String name, ObjectFactory objectFactory);
     //Phương thức này sẽ được gọi khi BeanFactory.destroyScopedBean được gọi
     @Có thể rỗng
     Đối tượng remove(String name);
     // Đăng ký hủy cuộc gọi lại
     // Đây là một triển khai tùy chọn cung cấp các lệnh gọi lại để hủy đậu đã đăng ký bên ngoài. Cuộc gọi lại được truyền ở đây có thể được thực thi khi xóa.
     vô hiệu registerDestructionCallback(Tên chuỗi, Gọi lại có thể chạy);
     // Nếu một Bean không có trong BeanFactory mà được tạo theo ngữ cảnh, ví dụ mỗi http request tạo ra một Bean độc lập nên không thể lấy được từ BeanFactory mà sẽ được lấy từ đây.
     // Đây cũng là một cách triển khai tùy chọn
     Đối tượng resolveContextualObject(String key);
     // Triển khai tùy chọn, tương tự như session id để người dùng phân biệt các ngữ cảnh khác nhau
     Chuỗi getConversationId();
}

Hãy triển khai một Phạm vi đơn giản:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
công cộng tĩnh lớp học Phạm vi kiểm tra thực hiện Phạm vi {
     @Ghi đè
     công cộng Đối tượng get(String name, ObjectFactory objectFactory) {
         trở lại objectFactory.getObject();
     }
     @Ghi đè
     công cộng Đối tượng xóa (Tên chuỗi) {
         trở lại vô giá trị ;
     }
     @Ghi đè
     công cộng vô hiệu registerDestructionCallback(Tên chuỗi, Gọi lại có thể chạy) {
     }
     @Ghi đè
     công cộng Đối tượng resolveContextualObject(String key) {
         trở lại vô giá trị ;
     }
     @Ghi đè
     công cộng Chuỗi getConversationId() {
         trở lại vô giá trị ;
     }
}

Phạm vi này chỉ thực hiện phương thức get. Tạo một Bean mới trực tiếp từ objectFactory đã truyền. Mỗi lệnh gọi BeanFactory.getFactory theo loại Scope này sẽ trả về một Bean mới, và các Bean thuộc loại Scope này được tự động nạp vào các Bean khác nhau cũng là các instance khác nhau. Viết bài kiểm tra:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Cấu hình
công cộng tĩnh lớp học Cấu hình {
     @Đậu
     // Tên của Phạm vi tùy chỉnh là testScope
     @tổ chức .springframework.context.annotation.Scope(giá trị = "Phạm vi thử nghiệm" )
     công cộng Một a() {
         trở lại mới MỘT();
     }
     // Tự động tải vào
     @Autowired
     riêng tư Một là;
}
 
công cộng tĩnh lớp học MỘT {
     công cộng vô hiệu Bài kiểm tra() {
         Hệ thống.out.println( cái này );
     }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
công cộng tĩnh vô hiệu main(String[] args) {
     //Tạo một bối cảnh ứng dụng
     Chú thíchConfigApplicationContext chú thíchConfigApplicationContext = mới Chú thíchConfigApplicationContext();
     // Đăng ký Phạm vi tùy chỉnh của chúng tôi
     chú thíchConfigApplicationContext.getBeanFactory().registerScope( "Phạm vi thử nghiệm" , mới Phạm vi kiểm tra());
     // Đăng ký các Bean cấu hình chúng ta cần
     chú thíchConfigApplicationContext.register(Config. lớp học );
     // Gọi làm mới để khởi tạo ApplicationContext
     chú thíchConfigApplicationContext.refresh();
     // Lấy Bean cấu hình
     Cấu hình config = annotationConfigApplicationContext.getBean(Config. lớp học );
     //Gọi Bean được nạp tự động
     config.a.test();
     //Gọi getBean từ BeanFactory để lấy A
     chú thíchConfigApplicationContext.getBean(A. lớp học ).Bài kiểm tra();
     chú thíchConfigApplicationContext.getBean(A. lớp học ).Bài kiểm tra();
}

Khi thực thi mã, từ đầu ra có thể thấy rằng ba chữ A này là các đối tượng khác nhau:

?
1
2
3
com.hopegaming.spring.cloud.parent.ScopeTest$A @5241cf67
com.hopegaming.spring.cloud.parent.ScopeTest$A @716a7124
com.hopegaming.spring.cloud.parent.ScopeTest$A @77192705

Hãy sửa đổi Bean của chúng ta một lần nữa và biến nó thành Bean dùng một lần:

?
1
2
3
4
5
6
7
8
9
10
công cộng tĩnh lớp học MỘT thực hiện Dùng một lầnĐậu {
     công cộng vô hiệu Bài kiểm tra() {
         Hệ thống.out.println( cái này );
     }
 
     @Ghi đè
     công cộng vô hiệu hủy hoại() ném Ngoại lệ {
         Hệ thống.out.println( cái này + "bị phá hủy" );
     }
}

Sau đó sửa đổi Phạm vi tùy chỉnh của chúng tôi:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
công cộng tĩnh lớp học Phạm vi kiểm tra thực hiện Phạm vi {
     riêng tư Gọi lại có thể chạy được;
     @Ghi đè
     công cộng Đối tượng get(String name, ObjectFactory objectFactory) {
         trở lại objectFactory.getObject();
     }
 
     @Ghi đè
     công cộng Đối tượng xóa (Tên chuỗi) {
         System.out.println(tên + "đã bị xóa" );
         cái này .callback.run();
         Hệ thống.out.println( "gọi lại đã hoàn tất" );
         trở lại vô giá trị ;
     }
 
     @Ghi đè
     công cộng vô hiệu registerDestructionCallback(Tên chuỗi, Gọi lại có thể chạy) {
         Hệ thống.out.println( "registerDestructionCallback được gọi" );
         cái này .callback = gọi lại;
     }
 
     @Ghi đè
     công cộng Đối tượng resolveContextualObject(String key) {
         Hệ thống.out.println( "resolveContextualObject được gọi" );
         trở lại vô giá trị ;
     }
 
     @Ghi đè
     công cộng Chuỗi getConversationId() {
         Hệ thống.out.println( "getConversationId được gọi" );
         trở lại vô giá trị ;
     }
}

Trong mã kiểm tra, hãy thêm lệnh gọi destroyScopedBean để hủy đậu:

?
1
chú thíchConfigApplicationContext.getBeanFactory().destroyScopedBean( "Một" );

Chạy mã và bạn có thể thấy đầu ra tương ứng:

registerDestructionCallback được gọi là a đã xóa com.hopegaming.spring.cloud.parent.ScopeTest$A@716a7124 đã hủy cuộc gọi lại hoàn tất 。

Đối với dùng một lầnBean hoặc các loại đậu khác có loại vòng đời liên quan, BeanFactory sẽ chuyển các lệnh gọi lại hoạt động cần thiết cho vòng đời thông qua registerDestructionCallback. Khi sử dụng BeanFactory.destroyScopedBean để tiêu diệt một Bean, phương thức loại bỏ của Scope sẽ được gọi. Khi thao tác hoàn tất, chúng ta có thể gọi lại để hoàn thành vòng đời của Bean.

Tiếp theo, chúng tôi cố gắng triển khai Phạm vi đơn lẻ theo cách rất đơn giản, chủ yếu dựa trên ConcurrentHashMap:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
công cộng tĩnh lớp học Phạm vi kiểm tra thực hiện Phạm vi {
 
     riêng tư cuối cùng ConcurrentHashMap bản đồ = mới ConcurrentHashMap<>();
     riêng tư cuối cùng ConcurrentHashMap gọi lại = mới ConcurrentHashMap<>();
 
     @Ghi đè
     công cộng Đối tượng get(String name, ObjectFactory objectFactory) {
         Hệ thống.out.println( "get được gọi là" );
         trở lại map.compute(tên, (k, v) -> {
             nếu như (trong == vô giá trị ) {
                 v = objectFactory.getObject();
             }
             trở lại TRONG;
         });
     }
 
     @Ghi đè
     công cộng Đối tượng xóa (Tên chuỗi) {
         cái này .map.remove(tên);
         System.out.println(tên + "đã bị xóa" );
         cái này .callback.get(tên).run();
         Hệ thống.out.println( "gọi lại đã hoàn tất" );
         trở lại vô giá trị ;
     }
 
     @Ghi đè
     công cộng vô hiệu registerDestructionCallback(Tên chuỗi, Gọi lại có thể chạy) {
         Hệ thống.out.println( "registerDestructionCallback được gọi" );
         cái này .callback.put(tên, lệnh gọi lại);
     }
 
     @Ghi đè
     công cộng Đối tượng resolveContextualObject(String key) {
         trở lại vô giá trị ;
     }
 
     @Ghi đè
     công cộng Chuỗi getConversationId() {
         trở lại vô giá trị ;
     }
}

Chúng tôi sử dụng hai ConcurrentHashMap để lưu trữ các Bean trong Phạm vi này và Lệnh gọi lại hủy tương ứng. Trong cách triển khai này, nó tương tự như việc triển khai mẫu đơn. Sau đó sử dụng chương trình kiểm tra sau để kiểm tra:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
công cộng tĩnh vô hiệu main(String[] args) {
     Chú thíchConfigApplicationContext chú thíchConfigApplicationContext = mới Chú thíchConfigApplicationContext();
     chú thíchConfigApplicationContext.getBeanFactory().registerScope( "Phạm vi thử nghiệm" , mới Phạm vi kiểm tra());
     chú thíchConfigApplicationContext.register(Config. lớp học );
     chú thíchConfigApplicationContext.refresh();
     Cấu hình config = annotationConfigApplicationContext.getBean(Config. lớp học );
     config.a.test();
     chú thíchConfigApplicationContext.getBean(A. lớp học ).Bài kiểm tra();
     // Tên phương thức đăng ký Bean trong lớp Config là a nên tên Bean cũng là a
     chú thíchConfigApplicationContext.getBeanFactory().destroyScopedBean( "Một" );
     config.a.test();
     chú thíchConfigApplicationContext.getBean(A. lớp học ).Bài kiểm tra();
}

Trước khi hủy Bean, chúng ta sử dụng tính năng tự động tải và BeanFactory.getBean để yêu cầu Bean A và gọi phương thức kiểm tra tương ứng. Sau đó tiêu diệt Bean. Sau đó, sử dụng tính năng tự động tải và BeanFactory.getBean để yêu cầu lấy Bean A và gọi phương thức thử nghiệm. Có thể thấy từ kết quả đầu ra rằng BeanFactory.getBean yêu cầu một hạt đậu mới, nhưng hạt đậu được tải tự động vẫn là hạt đậu bị phá hủy. Vậy làm thế nào để đạt được việc nạp đậu mới tự động, tức là nạp lại?

Điều này liên quan đến một cấu hình khác phía trên chú thích Phạm vi, nhằm chỉ định chế độ proxy:

?
1
2
3
4
5
6
7
8
9
10
@Mục tiêu ({Loại phần tử.TYPE, Loại phần tử.METHOD})
@Giữ chân (Chính sách lưu giữ.RUNTIME)
@Đã ghi chép
công cộng @giao diện Phạm vi {
     @Bí danh ( "tên phạm vi" )
     Giá trị chuỗi() mặc định "" ;
     @Bí danh ( "giá trị" )
     Chuỗi scopeName() mặc định "" ;
     Chế độ proxy ScopedProxyMode() mặc định ScopedProxyMode. MẶC ĐỊNH;
}

Cấu hình thứ ba, ScopedProxyMode, định cấu hình xem đối tượng Bean gốc hay đối tượng Bean proxy được lấy khi lấy Bean này (điều này cũng ảnh hưởng đến việc tải tự động):

?
1
2
3
4
5
6
7
8
9
10
công cộng liệt kê Chế độ Proxy được phạm vi {
     //Sử dụng cấu hình mặc định Nếu không có cấu hình ngoại vi nào khác thì KHÔNG.
     MẶC ĐỊNH,
     //Sử dụng đối tượng ban đầu là Bean
     KHÔNG,
     //Sử dụng proxy động của JDK
     GIAO DIỆN,
     //Sử dụng proxy động CGLIB
     LỚP MỤC TIÊU
}

Hãy kiểm tra tác dụng của việc chỉ định đối tượng thực tế của Scope Bean làm proxy. Chúng tôi sửa đổi mã kiểm tra ở trên và sử dụng proxy động CGLIB. Sửa đổi mã:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Cấu hình
công cộng tĩnh lớp học Cấu hình {
     @Đậu
     @tổ chức .springframework.context.annotation.Scope(giá trị = "Phạm vi thử nghiệm"
             // Chỉ định chế độ proxy dựa trên CGLIB
             , proxyMode = ScopedProxyMode.TARGET_CLASS
     )
     công cộng Một a() {
         trở lại mới MỘT();
     }
     @Autowired
     riêng tư Một là;
}

Viết phương thức chính của bài kiểm tra:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
công cộng tĩnh vô hiệu main(String[] args) {
     Chú thíchConfigApplicationContext chú thíchConfigApplicationContext = mới Chú thíchConfigApplicationContext();
     chú thíchConfigApplicationContext.getBeanFactory().registerScope( "Phạm vi thử nghiệm" , mới Phạm vi kiểm tra());
     chú thíchConfigApplicationContext.register(Config. lớp học );
     chú thíchConfigApplicationContext.refresh();
     Cấu hình config = annotationConfigApplicationContext.getBean(Config. lớp học );
     config.a.test();
     chú thíchConfigApplicationContext.getBean(A. lớp học ).Bài kiểm tra();
     //Xem loại phiên bản Bean
     System.out.println(config.a.getClass());
     System.out.println(annotationConfigApplicationContext.getBean(A. lớp học ).getClass());
     // Lúc này chúng ta cần lưu ý rằng tên của proxy Bean đã thay đổi và cần được lấy thông qua ScopedProxyUtils
     chú thíchConfigApplicationContext.getBeanFactory().destroyScopedBean(ScopedProxyUtils.getTargetBeanName( "Một" ));
     config.a.test();
     chú thíchConfigApplicationContext.getBean(A. lớp học ).Bài kiểm tra();
}

Thực hiện chương trình, kết quả là:

get được gọi là registerDestructionCallback được gọi là com.hopegaming.spring.cloud.parent.ScopeTest$A@3dd69f5a get được gọi là com.hopegaming.spring.cloud.parent.ScopeTest$A@3dd69f5a class com.hopegaming.spring.cloud.parent.ScopeTest$A$$EnhancerBySpringCGLIB$$2fa625ee class com.hopegaming.spring.cloud.parent.ScopeTest$A$$EnhancerBySpringCGLIB$$2fa625ee scopedTarget.a bị xóa com.hopegaming.spring.cloud.parent.ScopeTest$A@3dd69f5a bị hủy callback kết thúc get được gọi là registerDestructionCallback được gọi là com.hopegaming.spring.cloud.parent.ScopeTest$A@3aa3193a get được gọi là com.hopegaming.spring.cloud.parent.ScopeTest$A@3aa3193a 。

Như có thể thấy từ đầu ra:

  • Mỗi khi Bean được tải tự động được gọi, phương thức get của Phạm vi tùy chỉnh sẽ được gọi để lấy lại Bean.
  • Mỗi khi lấy được Bean thông qua BeanFactory, phương thức get của Phạm vi tùy chỉnh cũng sẽ được gọi để lấy lại Bean.
  • Phiên bản Bean thu được là một đối tượng proxy CGLIB
  • Sau khi Bean bị phá hủy, cho dù nó có được thông qua BeanFactory hay Bean được tải tự động thì đó vẫn là Bean mới.

Vậy làm thế nào Scope đạt được điều này? Hãy phân tích ngắn gọn mã nguồn.

Khái niệm cơ bản về phạm vi

Nếu một Bean không khai báo bất kỳ Scope nào thì Scope của nó sẽ được gán cho một singleton, tức là Bean mặc định là một singleton. Điều này tương ứng với BeanFactory. Trước khi đăng ký Bean, cần phải tạo định nghĩa Bean. Giá trị mặc định này sẽ được gán khi Bean được xác định, tương ứng với mã nguồn:

Tóm tắtBeanFactory 。

?
1
2
3
4
5
6
7
8
9
được bảo vệ RootBeanDefinition getMergedBeanDefinition(
     Chuỗi BeanName, BeanDefinition bd, @Có thể rỗng BeanDefinition chứaBd)
     ném Ngoại lệ BeanDefinitionStoreException {
     //Bỏ qua mã nguồn mà chúng ta không quan tâm
     nếu như (!StringUtils.hasLength(mbd.getScope())) {
         mbd.setScope(SCOPE_SINGLETON);
     }
     //Bỏ qua mã nguồn mà chúng ta không quan tâm
}

Trước khi khai báo Bean có Phạm vi đặc biệt, chúng ta cần xác định Phạm vi tùy chỉnh và đăng ký nó với BeanFactory. Tên Phạm vi này phải là duy nhất trên toàn cầu, vì tên này được sử dụng để phân biệt các Phạm vi khác nhau sau này. Đăng ký Phạm vi mã nguồn tương ứng:

Tóm tắtBeanFactory 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Ghi đè
công cộng vô hiệu registerScope(Chuỗi scopeName, Phạm vi phạm vi) {
     Khẳng định.notNull(scopeName, "Định danh phạm vi không được để là null" );
     Assert.notNull(phạm vi, "Phạm vi không được để là null" );
     // Không thể sử dụng cho hai phạm vi đặt trước, singleton và nguyên mẫu.
     nếu như (SCOPE_SINGLETON.bằng(scopeName) || SCOPE_PROTOTYPE.bằng(scopeName)) {
         ném mới Ngoại lệ bất hợp pháp "Không thể thay thế phạm vi hiện tại 'singleton' và 'prototype'" );
     }
     // Đặt nó vào bản đồ phạm vi. Khóa là tên và giá trị là Phạm vi tùy chỉnh.
     Phạm vi trước = cái này .scopes.put(tên phạm vi, phạm vi);
     // Có thể thấy rằng những cái được đặt sau sẽ thay thế những cái trước đó. Chúng ta nên cố gắng tránh điều này.
     nếu như (trước đó != vô giá trị && trước != phạm vi) {
         nếu như (logger.isDebugEnabled()) {
             logger.debug( "Đang thay thế phạm vi '" + phạm viTên + "' từ [" + trước + "] ĐẾN [" + phạm vi + "]" );
         }
     }
     khác {
         nếu như (logger.isTraceEnabled()) {
             logger.trace( "Đang đăng ký phạm vi '" + phạm viTên + "' với việc thực hiện [" + phạm vi + "]" );
         }
     }
}

Sau khi khai báo Bean có Phạm vi đặc biệt, sẽ có logic đặc biệt khi lấy Bean. Tham khảo mã nguồn cốt lõi của việc lấy Bean thông qua BeanFactory:

Tóm tắtBeanFactory 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@SuppressCảnh báo ( "chưa được kiểm tra" )
được bảo vệ T doGetBean(
     Tên chuỗi, @Có thể rỗng Lớp bắt buộc Type, @Có thể rỗng Đối tượng[] args, Boolean typeCheckOnly)
     ném Ngoại lệ đậu {
     //Bỏ qua mã nguồn mà chúng ta không quan tâm
     //Tạo cá thể Bean
     nếu như ( mbd . isSingleton ( ) ) {
         //Tạo hoặc trả về một cá thể đơn lẻ
     } khác nếu như (mbd.isPrototype()) {
         // Tạo một thể hiện mới mỗi lần
     } khác {
         // Vào đây nghĩa là Bean này thuộc về Custom Scope
         Chuỗi scopeName = mbd.getScope();
         // Phải có tên phạm vi
         nếu như (!StringUtils.hasLength(scopeName)) {
             ném mới Ngoại lệ bất hợp pháp "Không có tên phạm vi nào được xác định cho bean ´" + tên phụ nữ + "'" );
         }
         // Lấy Scope tương ứng thông qua tên Scope tùy chỉnh. Cần phải đăng ký thủ công.
         Phạm vi phạm vi = cái này .scopes.get(tên phạm vi);
         nếu như (phạm vi == vô giá trị ) {
             ném mới Ngoại lệ bất hợp pháp "Không có phạm vi nào được đăng ký cho tên phạm vi '" + phạm viTên + "'" );
         }
         thử {
             // Gọi phương thức get của Phạm vi tùy chỉnh để lấy Bean
             Đối tượng scopedInstance = scope.get(beanName, () -> {
                 // Đồng thời, chuyển vào các lệnh gọi lại của vòng đời cần thiết để tạo Bean, được sử dụng để tạo Bean.
                 beforePrototypeCreation(tên hạt đậu);
                 thử {
                     trở lại createBean(beanName, mbd, args);
                 }
                 Cuối cùng {
                     sau khi tạo Prototype(tên hạt đậu);
                 }
             });
             beanInstance = getObjectForBeanInstance(scopedInstance, tên, beanName, mbd);
         }
         nắm lấy (IllegalStateException ví dụ) {
             ném mới ScopeNotActiveException(tên_bean, tên_scope, ví dụ);
         }
     }
     //Bỏ qua mã nguồn mà chúng ta không quan tâm
}

Đồng thời, nếu chúng ta định nghĩa phương thức proxy của Scope Bean là CGLIB thì khi lấy định nghĩa Bean, định nghĩa Bean của Scope proxy sẽ được tạo dựa trên định nghĩa Bean ban đầu, tương ứng với mã nguồn:

Sử dụng ScopedProxyUtils.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
công cộng tĩnh BeanDefinitionHolder createScopedProxy(Định nghĩa BeanDefinitionHolder,
             Đăng ký BeanDefinitionRegistry, Boolean proxyTargetClass) {
 
     // Tên đậu mục tiêu ban đầu
     Chuỗi originalBeanName = definition.getBeanName();
     // Lấy định nghĩa Bean đích ban đầu
     BeanDefinition targetDefinition = definition.getBeanDefinition();
     // Lấy tên proxy proxy
     Chuỗi targetBeanName = getTargetBeanName(originalBeanName);
 
     //Tạo Bean kiểu ScopedProxyFactoryBean
     RootBeanDefinition proxyDefinition = mới RootBeanDefinition(ScopedProxyFactoryBean. lớp học );
     //Theo các thuộc tính được xác định bởi Bean đích ban đầu, hãy định cấu hình các thuộc tính liên quan được xác định bởi Bean proxy, bỏ qua phần mã nguồn này
    
     // Sao chép vào định nghĩa Bean proxy dựa trên các thuộc tính tự động tải của Bean đích ban đầu
     proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
     proxyDefinition.setPrimary(targetDefinition.isPrimary());
     nếu như (mục tiêuĐịnh nghĩa trường hợp của AbstractBeanĐịnh nghĩa) {
         proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
     }
 
     // Đặt Bean ban đầu được xác định là không được tải tự động và không phải là Chính
     // Bằng cách này, các hạt thu được thông qua BeanFactory và được tải tự động là các hạt proxy thay vì các hạt đích ban đầu.
     targetDefinition.setAutowireỨng viên( SAI );
     targetDefinition.setPrimary( SAI );
 
     // Đăng ký Bean với tên mới
     registry.registerBeanDefinition(targetBeanName, targetDefinition);
 
     trở lại mới BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
 
riêng tư tĩnh cuối cùng Chuỗi TARGET_NAME_PREFIX = "Mục tiêu có phạm vi." ;
// Đây là một phương thức công cụ để lấy tên của proxy Bean. Nó cũng được sử dụng khi chúng ta hủy Bean ở trên.
công cộng tĩnh Chuỗi getTargetBeanName(Chuỗi originalBeanName) {
     trở lại TARGET_NAME_PREFIX + tên_Bean_ban đầu;
}

Đậu proxy này làm gì? Trên thực tế, công dụng chính là mỗi khi bất kỳ phương thức nào của Bean được gọi, Bean sẽ được lấy thông qua BeanFactory và được gọi. Mã nguồn tham khảo:

Lớp proxy ScopedProxyFactoryBean.

?
1
2
3
4
5
6
công cộng lớp học Phạm viProxyFactoryBean mở rộng Cấu hình proxy
         thực hiện FactoryBean<đối tượng="">, BeanFactoryAware, AopInfrastructureBean {
     riêng tư cuối cùng SimpleBeanTargetSource có phạm viTargetSource = mới Nguồn mục tiêu đơn giản();
     // Đây là proxy thực tế được tạo thông qua SimpleBeanTargetSource. Tất cả các lệnh gọi phương thức Bean sẽ được gọi thông qua proxy này.
     riêng tư Đối tượng proxy;
}

SimpleBeanTargetSource là nguồn proxy thực tế. Phương pháp cốt lõi là sử dụng tên Bean để lấy Bean thông qua BeanFactory:

?
1
2
3
4
5
6
công cộng lớp học SimpleBeanMục tiêuNguồn mở rộng Tóm tắtBeanFactoryBasedTargetSource {
     @Ghi đè
     công cộng Đối tượng getTarget() ném Ngoại lệ {
         trở lại getBeanFactory().getBean(getTargetBeanName());
     }
}

Lấy Bean này thông qua BeanFactory Từ phân tích mã nguồn ở trên, bạn có thể biết rằng đối với Bean của Phạm vi tùy chỉnh, phương thức get của Phạm vi tùy chỉnh sẽ được gọi.

Sau đó là sự hủy diệt của Bean. Khi BeanFactory tạo đối tượng Bean, registerDestructionCallback của Phạm vi tùy chỉnh sẽ được gọi để chuyển vào lệnh gọi lại quá trình hủy Bean:

Tóm tắtBeanFactory 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
được bảo vệ vô hiệu registerDisposableBeanIfNecessary(Chuỗi beanName, Đối tượng bean, RootBeanDefinition mbd) {
     AccessControlContext acc = (System.getSecurityManager() != vô giá trị ? getAccessControlContext() : vô giá trị );
     nếu như (!mbd.isPrototype() && yêu cầuDestruction(bean, mbd)) {
         nếu như ( mbd . isSingleton ( ) ) {
             //Đối với người độc thân
             registerDisposableBean(tênbean, mới Bộ chuyển đổi đậu dùng một lần
                     bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
         }
         khác {
             //Đối với phạm vi tùy chỉnh
             Phạm vi phạm vi = cái này .scopes.get(mbd.getScope());
             nếu như (phạm vi == vô giá trị ) {
                 ném mới Ngoại lệ bất hợp pháp "Không có phạm vi nào được đăng ký cho tên phạm vi '" + mbd.getScope() + "'" );
             }
             //Gọi registerDestructionCallback
             phạm vi.registerDestructionCallback(beanName, mới Bộ chuyển đổi đậu dùng một lần
                     bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
         }
     }
}

Khi chúng ta muốn hủy Scope Bean, chúng ta cần gọi phương thức destroyScopedBean của BeanFactory. Phương thức này sẽ gọi loại bỏ Scope tùy chỉnh:

Tóm tắtBeanFactory 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Ghi đè
công cộng vô hiệu destroyScopedBean(Chuỗi tên bean) {
     RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
     // Chỉ được sử dụng cho Scope Bean tùy chỉnh
     nếu như (mbd.isSingleton() || mbd.isPrototype()) {
         ném mới Ngoại lệ bất hợp pháp
                 "Tên đậu '" + tên phụ nữ + "' không tương ứng với một đối tượng trong phạm vi có thể thay đổi" );
     }
     Chuỗi scopeName = mbd.getScope();
     Phạm vi phạm vi = cái này .scopes.get(tên phạm vi);
     nếu như (phạm vi == vô giá trị ) {
         ném mới Ngoại lệ bất hợp pháp "Không có phạm vi SPI nào được đăng ký cho tên phạm vi '" + phạm viTên + "'" );
     }
     // Gọi phương thức loại bỏ của Phạm vi tùy chỉnh
     Đối tượng bean = scope.remove(beanName);
     nếu như (đậu != vô giá trị ) {
         destroyBean(beanName, Bean, mbd);
     }
}

Đến đây là kết thúc bài viết về việc giải quyết vấn đề dự án có thể làm mới không thể khởi động khi cấu hình Spring-Cloud-OpenFeign được bật. Để biết thêm nội dung cấu hình Spring-Cloud-OpenFeign có liên quan, vui lòng tìm kiếm các bài viết trước của tôi hoặc tiếp tục duyệt qua các chủ đề liên quan bên dưới. Bài viết mong mọi người ủng hộ mình trong thời gian tới! .

Liên kết gốc: https://www.cnblogs.com/zhxdick/p/15359268.html.

Cuối cùng, bài viết về giải quyết vấn đề kích hoạt cấu hình Spring-Cloud-OpenFeign để làm mới dự án không thể bắt đầu ở đây. Nếu bạn muốn biết thêm về cách giải quyết vấn đề kích hoạt cấu hình Spring-Cloud-OpenFeign để làm mới dự án. không thể bắt đầu. Về nội dung, vui lòng tìm kiếm các bài viết của CFSDN hoặc tiếp tục duyệt các bài viết liên quan. Tôi hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .

30 4 0
qq735679552
Hồ sơ

Tôi là một lập trình viên xuất sắc, rất giỏi!

Nhận phiếu giảm giá taxi Didi miễn phí
Phiếu giảm giá taxi Didi
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress