$ v* _9 O$ j0 p3 k; |6 _7 Q3 ^import java.lang.annotation.*; M. K6 n2 O) f6 i, ~, `) U) h6 g5 [% {' z
@Target({ElementType.TYPE, ElementType.METHOD})" b- r3 n' }9 Q2 m2 _
@Retention(RetentionPolicy.RUNTIME) # j& t5 F/ k; [9 E. k@Documented % d8 ]+ D) [$ w0 }. J& s8 b- ^@Scope(BeanMyScope.SCOPE_MY) //@19 S' O' f: p7 i, ?+ G9 j4 I9 F
public @interface MyScope {0 @* d3 t: t9 W
/**0 Y7 e7 L4 l/ e7 q9 F2 C: ^! Y9 i% k
* @see Scope#proxyMode() ( ?. W: S5 j+ s */ - B6 F$ v" F: q5 q$ v1 p ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;//@2! T: j, d2 ^% e
}. m; l! K# j! \; {* N' B
@1:使用了@Scope注解,value为引用了一个常量,值为my,一会下面可以看到。2 M6 [, C* V5 M+ ]7 O0 ?5 P. R% X
) z6 `8 J+ ?- x' o7 \; y4 x@2:注意这个地方,参数名称也是proxyMode,类型也是ScopedProxyMode,而@Scope注解中有个和这个同样类型的参数,spring容器解析的时候,会将这个参数的值赋给@MyScope注解上面的@Scope注解的proxyMode参数,所以此处我们设置proxyMode值,最后的效果就是直接改变了@Scope中proxyMode参数的值。此处默认值取的是ScopedProxyMode.TARGET_CLASS ! v2 i" g; z3 v* P: } / j" n {: `, Y @5 [@MyScope注解对应的Scope实现如下6 R: H) v$ u( }" _4 G, r w
" O/ p$ a. l4 j" y
package com.javacode2018.lesson002.demo18.test3;* q7 u2 u: z" {; b$ w( i7 u
! U1 k" p* }3 V5 O8 X6 J9 Nimport org.springframework.beans.factory.ObjectFactory;) k* _# A+ f+ _
import org.springframework.beans.factory.config.Scope;( [! o3 E! U# j n+ N! I( L- W$ Q0 J
import org.springframework.lang.Nullable; . A& x; d8 W8 h- v, {/ t/ r" q7 g: m0 H% O4 u8 q
/** g( f7 F6 P$ U( F) D" c * @see MyScope 作用域的实现6 L+ V. u0 i+ O6 f3 Q7 X
*/ % D( J0 _5 W. r, k6 o' j1 ipublic class BeanMyScope implements Scope { + s, |8 e( e1 X1 c8 W# \- H4 W: n( a/ t5 F+ y$ |. z
public static final String SCOPE_MY = "my"; //@1 - v; E3 Y I8 a* f) v* r 4 F; t7 P& g3 \" _1 ` @Override 9 M# g1 x9 Y9 S, z! X" U: V- [2 W public Object get(String name, ObjectFactory<?> objectFactory) { / i j9 b; D5 ]8 {& H System.out.println("BeanMyScope >>>>>>>>> get:" + name); //@29 N& H7 y6 k" {, E
return objectFactory.getObject(); //@3 " P" L l) r' W; j4 H. O7 O } 0 O7 O# p& _( D1 K2 q% X8 a ; L# u+ h t: }9 B4 _! u @Nullable7 T% z$ N) A7 Y" m) L8 @
@Override 6 g7 X0 K- U3 j2 J" z6 b4 F# | public Object remove(String name) { # `. E( G3 Y; C$ \" l7 e1 e return null;) n) I) s8 w% X/ ]) M
}' o; {* ]4 d. W& \! }# Q7 A7 K
" R; c. n* ^+ {' P! A+ m @Override # [% }% D% p t/ T$ \+ M public void registerDestructionCallback(String name, Runnable callback) {" T# G1 G `5 Y- r6 |5 ~
, ^) Q6 \/ S# J/ L' ? f5 @
} 8 {/ J3 q' j% K) G0 @ e. j8 `: @1 X2 d8 E- i( Y8 P; U
@Nullable% t, c& \6 J g" ]$ [
@Override' X4 N& a' y7 X0 [+ T- h8 \
public Object resolveContextualObject(String key) { ' S6 q) p& F( L return null; $ v* w" L3 V4 q }+ o( P5 k3 m F% Y, W& ?
( H$ `% R( Z d) }; N L/ q* @" G @Nullable . a. @& T \, t, m1 U e2 I$ j* X) W @Override1 \5 c" ^- u2 f- j1 b: `4 H+ Z
public String getConversationId() { z ?2 i8 B `' w1 N( S8 |
return null; ' t6 C+ V1 T1 H w g' e5 @6 c' j E' ~ } + b) |8 L. K m+ G, t- S% }4 X}9 i2 s: \. h6 p4 h: {
@1:定义了一个常量,作为作用域的值- B% G# s! W, n) @- ^% U. L
2 i& }/ ^+ S2 C2 f5 i@2:这个get方法是关键,自定义作用域会自动调用这个get方法来创建bean对象,这个地方输出了一行日志,为了一会方便看效果 x4 ] G0 F( a: t : C$ a, P3 R# X9 c' I@3:通过objectFactory.getObject()获取bean实例返回。 * y: w# [/ R; y% U( Q' Y# I$ n/ T0 Z
下面来创建个类,作用域为上面自定义的作用域0 @0 i- J% L3 Y' t8 {' c. b" O
: f* H* t0 D; }" t
package com.javacode2018.lesson002.demo18.test3;/ e$ v& } o8 H; y* r8 {5 E |
: O) B5 f7 q9 himport org.springframework.stereotype.Component;: [: r. U# R/ P* Z
$ `9 Z- C! k/ c7 A0 s; K
import java.util.UUID; 5 [+ r7 ^5 N$ `' I5 I+ ~ 3 T# C9 j* U* }. \. ]. [5 U@Component% i. C# ^! x2 u3 t5 h# @
@MyScope //@1 ) X7 J0 j, d9 j0 J7 `- ]public class User { . Q8 r _: a# ?4 f7 [ # I9 \1 B1 e* B5 d5 w9 { private String username;1 w6 i5 x2 \! z6 q$ X
% x5 X2 B2 M i
public User() { 5 N& M2 x$ G, X7 ^3 U
System.out.println("---------创建User对象" + this); //@2; y' h3 V! S8 Q
this.username = UUID.randomUUID().toString(); //@3 0 h6 C: q0 X1 D } o/ T4 s; {1 k: t. L% w 5 P0 x- E5 w ~, ?4 U) L# j public String getUsername() { ! V! Z) n9 @8 V9 I return username;! w9 S3 i$ b; l% B0 n3 M+ N
} 4 {" x; `0 ]7 X9 }1 X+ F$ T ( x4 P4 t/ q. Q3 i6 v public void setUsername(String username) {' F$ i, i8 a1 _7 T: h
this.username = username; 6 a8 _1 k9 ~7 `; U+ b* O }; ^8 e7 ?! H* {$ t2 L+ V
- |: E' i3 \8 Q' Q
} 3 A4 k+ k5 L* y y" i@1:使用了自定义的作用域@MyScope& q: t y4 m; ]) N" W( @! T
6 v. M2 d) q2 J& _) a* X
@2:构造函数中输出一行日志" @7 y2 o ]- G& x1 ~3 K
. {9 @/ q; H9 g! Z5 ]& d+ O' |
@3:给username赋值,通过uuid随机生成了一个 & E% p& O; ^: O8 Z; X% h2 Z / }5 K x3 w' @/ C7 J7 Q来个spring配置类,加载上面@Compontent标注的组件% i& Z' ~7 z! Y% r6 I
; ^# g4 v/ K; T+ O' ]3 D
package com.javacode2018.lesson002.demo18.test3; ! W. E2 c5 o: m# q- k1 H" I# C" S; C' {6 j& I% b% }! |* O# g
import org.springframework.context.annotation.ComponentScan; 8 f e, p+ a: D L2 E# U' Eimport org.springframework.context.annotation.Configuration;8 }2 e0 a5 n% d/ H
% }8 d! }' }3 v
@ComponentScan8 u# s* _% t+ |3 m3 M
@Configuration ' S7 Q' ], o/ m! Q8 V1 C7 ?public class MainConfig3 {4 u& ]% }) O0 z. o6 B( \; X1 p
} 8 t6 ?' }+ ?& ]1 {下面重点来了,测试用例1 B% ^. S" U) i2 _/ D% N
. \' |% ], ?4 W2 B/ I
@Test2 J! m' P* Q7 V. U) k
public void test3() throws InterruptedException {- i3 L% `7 \$ }7 [& I* y
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();3 k" e( V& Y2 M
//将自定义作用域注册到spring容器中 ! k I& {; g$ f8 @/ C9 a t context.getBeanFactory().registerScope(BeanMyScope.SCOPE_MY, new BeanMyScope());//@1 ) V: h" d' }/ b" D" s context.register(MainConfig3.class); 8 q+ c V: n) E7 R context.refresh();7 u z a% S" J' I$ ]
i( f" R' h6 W. N- j9 W: H$ ^ System.out.println("从容器中获取User对象"); 9 v) @6 u. x2 i+ Q6 A6 t' A& d1 b1 c9 Z User user = context.getBean(User.class); //@2 4 f' E* O; \) x+ @7 ~ System.out.println("user对象的class为:" + user.getClass()); //@3 ; v/ ~* X9 Y5 u' M 6 m2 b. ]+ j; r, Q System.out.println("多次调用user的getUsername感受一下效果\n"); ! r* L4 \1 n$ H$ F# o3 { for (int i = 1; i <= 3; i++) { : G' \7 x0 O- i' H/ w; n0 O System.out.println(String.format("********\n第%d次开始调用getUsername", i)); 5 o; `7 t! J( g, i3 B System.out.println(user.getUsername());3 ?6 F: m/ x' t, O3 e
System.out.println(String.format("第%d次调用getUsername结束\n********\n", i)); 8 t' T" O6 Q" M6 `3 ]9 \) c7 d } $ O, e; ?% E' {, Y7 Y}/ i' _# Q3 i1 W; e- j
@1:将自定义作用域注册到spring容器中 / H3 p2 x- M& Y 7 E' N! z: k6 {) i@2:从容器中获取User对应的bean & G! o$ W. R- {/ v6 U4 F4 R$ G* X; Z+ \# k4 K
@3:输出这个bean对应的class,一会认真看一下,这个类型是不是User类型的 " k H9 j2 X; U1 {8 x6 m/ L7 N/ n X( n* r: f- E+ S! H- n
代码后面又搞了3次循环,调用user的getUsername方法,并且方法前后分别输出了一行日志。- r" J+ k9 S/ T9 Q2 ~5 `) b8 o1 D
8 T7 p% y0 d# p( O见证奇迹的时候到了,运行输出 * X3 V& x' I) i2 k8 D s8 u) U5 H0 G3 R6 H' e
从容器中获取User对象 h" ^* L$ j4 R& i9 o% Z& ?- L. l" Euser对象的class为:class com.javacode2018.lesson002.demo18.test3.User$$EnhancerBySpringCGLIB$$802331272 I- y% k% f% V q4 u% z# b
多次调用user的getUsername感受一下效果- [. x% h0 N6 K% T+ U& T: f+ Z
$ }8 Q8 G, x. J1 a r/ L
******** 0 e5 i& Y# `9 A3 Y第1次开始调用getUsername& I- P% n9 W* v2 ?/ `
BeanMyScope >>>>>>>>> get:scopedTarget.user4 B4 s. k' h; ?
---------创建User对象com.javacode2018.lesson002.demo18.test3.User@6a370f4 / r7 e; a+ ?9 B& h7b41aa80-7569-4072-9d40-ec9bfb92f438 : Y0 k+ ?1 _% M- j1 A: M* k" U第1次调用getUsername结束9 L+ F) B) V) m" E- @1 h
******** ; P( V- h9 k) ?, ~. O6 c/ D* P$ @/ y7 v& v1 k5 x
******** , ?4 w6 Y Q1 R第2次开始调用getUsername: p k, Y9 _; G; F+ E0 y6 B
BeanMyScope >>>>>>>>> get:scopedTarget.user; ?. e4 M6 @ a T3 g5 Q' Z5 ~
---------创建User对象com.javacode2018.lesson002.demo18.test3.User@1613674b 1 U+ l1 y! {1 o& E1 o y01d67154-95f6-44bb-93ab-05a34abdf51f) S" M. ^, h/ F& f2 w
第2次调用getUsername结束 8 t9 }: |" s5 C2 j- G( R) f6 H********- U, g/ K4 h- `/ b. ?
6 f) F' a, Y. p0 {& W& y# W
******** : K( }" |* W" a0 H& k9 U第3次开始调用getUsername6 a3 X5 q* r. d! g( m5 B7 b3 p4 u l. ~& {
BeanMyScope >>>>>>>>> get:scopedTarget.user3 P9 K+ a1 G* \* u8 x
---------创建User对象com.javacode2018.lesson002.demo18.test3.User@27ff5d15 " }4 k) T% Z" g* W+ }76d0e86f-8331-4303-aac7-4acce0b258b8 * h5 x; M; ]1 P第3次调用getUsername结束 9 U- Z4 h& \8 k- k& W4 @4 k2 @, [8 b5 K******** 3 J7 V: u1 P$ @7 D9 V从输出的前2行可以看出: % t; i4 e& ~3 I6 x6 v3 z& a8 L/ r0 h/ c2 N
调用context.getBean(User.class)从容器中获取bean的时候,此时并没有调用User的构造函数去创建User对象 % |+ q! D5 q t$ ]+ I& | 2 v3 H+ ^" ]# ~* i1 `第二行输出的类型可以看出,getBean返回的user对象是一个cglib代理对象。9 V1 C& Z- X: M* B
, |' U( A) o* w后面的日志输出可以看出,每次调用user.getUsername方法的时候,内部自动调用了BeanMyScope#get 方法和 User的构造函数。. z+ X0 m: V& z7 m
6 D5 U0 P; W m0 |3 N通过上面的案例可以看出,当自定义的Scope中proxyMode=ScopedProxyMode.TARGET_CLASS的时候,会给这个bean创建一个代理对象,调用代理对象的任何方法,都会调用这个自定义的作用域实现类(上面的BeanMyScope)中get方法来重新来获取这个bean对象。6 p7 a! a3 R2 M6 R; o1 I. j
) o% B1 r& U. O P$ v- `动态刷新@Value具体实现 . G2 v4 d0 s; K& m * ?) C ~7 E ]6 X2 w/ @那么我们可以利用上面讲解的这种特性来实现@Value的动态刷新,可以实现一个自定义的Scope,这个自定义的Scope支持@Value注解自动刷新,需要使用@Value注解自动刷新的类上面可以标注这个自定义的注解,当配置修改的时候,调用这些bean的任意方法的时候,就让spring重启初始化一下这个bean,这个思路就可以实现了,下面我们来写代码。: `# p/ {5 x7 t! V) ^
8 e% }, x- Q6 s* P& g6 a. F9 ]: g
先来自定义一个Scope:RefreshScope + n' F0 L8 T# R- J- u" d9 C' g9 x# v. C$ l( ]/ ?/ `
package com.javacode2018.lesson002.demo18.test4; 7 J. V' { O# R! Q; w" R! U & F" J c* e% f9 v% f, t2 eimport org.springframework.context.annotation.Scope;! o* q1 R+ x* I) v* m6 }" z6 Q5 e
import org.springframework.context.annotation.ScopedProxyMode; % f% v2 |8 r9 m+ b: W3 j% o& L W% S( r) Q @
import java.lang.annotation.*; " j' o) n& p% J 3 S# X9 a: n9 M$ q2 D$ u@Target({ElementType.TYPE, ElementType.METHOD}), m5 A; i0 |/ ~: l, l( q4 h
@Retention(RetentionPolicy.RUNTIME)# n; p8 a3 W0 `& ~: l
@Scope(BeanRefreshScope.SCOPE_REFRESH)( h, p5 B" o1 Y) S$ H8 s* L
@Documented 2 H* z/ Q* N' }$ M" _public @interface RefreshScope { : ^4 e6 g8 {7 g6 k. t* g* j ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS; //@1 , T& j$ I; a6 s/ E9 S' @: F, D; R} & u; Z x* B# V6 V# E/ c4 l* F1 f* \要求标注@RefreshScope注解的类支持动态刷新@Value的配置 - e) }! z0 R$ t/ V 7 z9 g& O6 @ e: X& I@1:这个地方是个关键,使用的是ScopedProxyMode.TARGET_CLASS( P9 y0 a. d j5 M% \
% {- P$ f2 e! a' W2 X4 H
这个自定义Scope对应的解析类, Y' n. N6 a6 ]2 b- A
/ m; M/ v8 A9 ]5 s4 y' a' T4 M下面类中有几个无关的方法去掉了,可以忽略 . w1 k0 z' s6 W" E5 J* ^- Q* S/ O; \+ Z
package com.javacode2018.lesson002.demo18.test4;/ b) r* ]# j4 n% _' t
/ v( d9 _% W: _. b- Z2 X- w9 o+ W" {+ U3 J' ]2 y
import org.springframework.beans.factory.ObjectFactory; 9 u3 \8 e5 o M4 ?% Cimport org.springframework.beans.factory.config.Scope;) c+ Y1 R* i5 ~
import org.springframework.lang.Nullable; : ^/ ^9 J G% W' m+ |9 ` 3 }6 Q; C+ D( nimport java.util.concurrent.ConcurrentHashMap; / o) A: D5 ^, K. H6 K6 ], X, D& M E( I# x" x9 I& S
public class BeanRefreshScope implements Scope {( v( _2 }0 q1 ~
4 v/ w0 i6 P$ u2 K/ A7 n
public static final String SCOPE_REFRESH = "refresh";, k. C1 X4 J& t5 m- m* ?* h
8 d' Q/ r- \9 z0 C. T% i
private static final BeanRefreshScope INSTANCE = new BeanRefreshScope(); - ^4 i6 t! N* q2 y' K! o2 t& J7 ?# q& H
//来个map用来缓存bean % k, c5 v% i" G% I! M2 c private ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(); //@1 # E: C8 P7 |9 u- t6 L& j& Z 5 q1 r! u! z# \ J private BeanRefreshScope() {" G* b9 N* R9 Y
}1 e$ L+ O2 R) @; A/ Z9 C( a
( M4 @; R, j2 ` q! z u6 E public static BeanRefreshScope getInstance() {! S- j$ m3 k9 o' Z9 C* O
return INSTANCE;2 i' X" L! \, [1 g' ]/ |
}2 k. l9 w! I% T' e2 B0 S" O: \
' ]; N" E& B1 ]4 {
/** ; K# K9 C: W: t# U4 f, v7 D * 清理当前 + E* c$ _3 H. E( u */ / ]1 F! j/ g. v7 s0 @ Z5 x! R% G0 U public static void clean() {" O( ]+ J# o" u) ?! C
INSTANCE.beanMap.clear(); ; w7 D6 O: |6 F1 r2 N } + \" b2 C A% u# e3 @, o! p6 D; A C( [5 B9 t# q
@Override/ L9 z) ~- T6 A7 |0 D1 v
public Object get(String name, ObjectFactory<?> objectFactory) {9 }7 A4 E' c F% y4 L: S
Object bean = beanMap.get(name);. A1 b# A. ~# k8 ~; x, I
if (bean == null) { $ `" X8 X# O6 U+ Y# E# z7 { W% G bean = objectFactory.getObject();9 }, q9 l6 U' q+ ?6 G' ]6 V" _$ B. D
beanMap.put(name, bean); 8 G* {8 m# \2 R, r } 0 v' h5 ~; J. _; ^* j: F: x4 B) n return bean;9 i8 h! ?7 h6 D# y; F6 u6 o: o2 N
}8 o: _" r- A2 e9 Z/ Y* L$ ^