) w6 z0 s: V H. L. R4 D; M5 c, K@MyScope注解对应的Scope实现如下 " s Z9 \3 ?! O2 J) s" M& D' k s V
package com.javacode2018.lesson002.demo18.test3;* s& |& R9 |2 e' F) @% d$ ]
0 ?" t" v W3 c' u s
import org.springframework.beans.factory.ObjectFactory; 6 c% J' |! K+ O- q* z( gimport org.springframework.beans.factory.config.Scope;" D7 q! O6 {1 ~3 K M2 p! c+ b
import org.springframework.lang.Nullable;" ?0 D! ]! s) d# f# {, N9 Z( {. S0 N
; y d% u( k8 o5 @; \: d/**8 H3 Y* g0 m! Y1 X5 t* B8 q9 Y
* @see MyScope 作用域的实现' B6 |8 B2 u! ^- ]
*/! u5 s" {9 N7 P2 _8 j
public class BeanMyScope implements Scope {7 A6 \7 q. h; ?5 u0 }/ l9 m7 X+ d
5 R/ m, j5 V, c
public static final String SCOPE_MY = "my"; //@1 2 P4 j+ ?; A' f7 q+ g' x: f ! l1 Z$ G: W: ? i) d& n) y @Override: E6 k0 H" X8 G0 x. ]; g9 ^9 \1 m
public Object get(String name, ObjectFactory<?> objectFactory) { + Y% D' x, D. i8 b7 s$ g p" u System.out.println("BeanMyScope >>>>>>>>> get:" + name); //@2% u9 |. ?- K7 `! N; S# u# I5 B
return objectFactory.getObject(); //@34 b |6 Q% `+ X/ y# i6 @4 L/ n5 i
}, o6 X9 ]$ W: y, k1 O4 A
* W# G& F1 S5 J7 V# J1 @
@Nullable# K/ k) n q& a
@Override 5 }# d* W# i$ w8 z, M5 F& e public Object remove(String name) {3 E8 V9 D8 Q3 W4 ?8 B2 `' H
return null;& [( ~9 T7 [, j8 ?7 M, m
}9 N& V; g* ~. w: H7 r/ s% m
5 R$ F& H8 v9 l( v
@Override * H; ~8 E+ ^5 s) m! l public void registerDestructionCallback(String name, Runnable callback) { # [) ?4 M f8 }# k$ a& c9 u9 N4 `( c( |9 C! H/ f z' f
} * }( I" y: G# I$ S! v' x3 m" p( u- _ ! x" E8 ?; a. w4 K/ s @Nullable 5 F3 z7 ?( h- K1 S) o& i1 q" ?* Z @Override e( r" U+ Q/ ^9 t0 b* w public Object resolveContextualObject(String key) {% z' W! N6 u. F/ J! n
return null; O; E8 m0 ]5 N- a
}8 N( s$ X9 C% B: ~5 j5 B
9 @. [" O# W2 d) \ @Nullable. j/ R9 C- [3 d
@Override! Z$ v6 F" R+ G$ \- Z
public String getConversationId() {1 j0 l0 g4 j. O
return null; 0 b# U$ @" S* Y$ @2 b }4 v$ p$ j) F+ d0 P. k$ B$ n
}8 U. F% Y) O. R+ q1 C* q1 H3 T
@1:定义了一个常量,作为作用域的值 # ?8 D# [# B; a. N& i/ z/ n4 S: C0 D9 x- I6 w/ l9 k
@2:这个get方法是关键,自定义作用域会自动调用这个get方法来创建bean对象,这个地方输出了一行日志,为了一会方便看效果 2 L% @: W. K( Y1 \4 b M( P) ~8 [& C% V! k
@3:通过objectFactory.getObject()获取bean实例返回。 6 |* N* ~6 N# i# t- B$ m( G7 }- V, n7 w. ]% Y+ y1 D
下面来创建个类,作用域为上面自定义的作用域 & T6 \! M% a$ v0 L+ r. q) c; e Q 5 o7 B- D1 g9 [$ ]+ m, t# C& Dpackage com.javacode2018.lesson002.demo18.test3; $ j( x9 U, I T# A9 \; g1 i6 z+ A- D0 r: Z, T6 E2 F/ b
import org.springframework.stereotype.Component; $ X+ _# U" K. X7 n- A- }4 L 6 X3 [$ K# x3 W7 {import java.util.UUID;3 R6 E$ d8 Y( A( r! M* ^
" ~& E0 \, A4 x" q$ o@Component; }) U# v' D1 W) d2 o# k8 [
@MyScope //@1 8 J: ]+ ]" B: I6 G+ X2 R9 [
public class User { - N J$ O% [. ?0 }& x$ z$ p9 u' C* q$ h
private String username; ( |' c) u! x4 \4 r* w1 I3 [+ }7 Q5 Q' i
public User() { $ [+ x& x7 ?; d! L( F6 C
System.out.println("---------创建User对象" + this); //@2 / b) e- g. ~! F1 l2 \# H this.username = UUID.randomUUID().toString(); //@33 e t! P( P8 O2 ^8 `5 j
} , M0 y4 p7 R" L- v: _9 \7 P2 G! K. v7 S j/ V2 X
public String getUsername() { " m N3 z6 s: D; V. P' ^( Y return username; : B- h4 N c1 U } : W8 N2 ]+ S8 X5 e2 X! g+ a+ @4 C* r0 q5 S+ y6 [
public void setUsername(String username) { f d- c" J( l. g; i( r, E7 e7 ^
this.username = username; \$ R- L/ Z+ n* m; L4 j2 V% B }5 ^2 F- ?" d$ u2 G ~3 t
' a# e$ F' n7 @* m$ Q% E, p% k9 a} $ w4 p3 T* @- S' A" u- p s3 ^3 H$ G@1:使用了自定义的作用域@MyScope 3 |& B' P! S& j$ |4 y9 ^0 m* M0 r- R1 S6 y' v5 ^( U# A
@2:构造函数中输出一行日志 5 m7 [6 ]8 ~6 p% ? " F4 B: h" E$ M6 X/ e R6 K! u@3:给username赋值,通过uuid随机生成了一个7 j, |; w6 T9 [6 x
* P4 I0 d- {8 l5 E! l
来个spring配置类,加载上面@Compontent标注的组件 / |& u2 W( I: P$ c7 P$ J! B% s, U4 K
package com.javacode2018.lesson002.demo18.test3; G2 S# @+ \1 q3 k& Y7 i
/ j/ e4 I; w6 o, `4 J4 S
import org.springframework.context.annotation.ComponentScan;7 U+ C0 ?3 Q1 M9 P {- d
import org.springframework.context.annotation.Configuration; ; h/ }' k% ?1 b7 V* W) L+ ] 5 f6 q8 T8 ]% Z$ `* t3 d@ComponentScan & }& d" S# F& O1 i* o1 l@Configuration . E, O8 e1 g* F! N2 ?+ m8 Cpublic class MainConfig3 { 2 C4 E+ b9 W* t( C* O& y( L ]. g, E} ) X3 a3 H8 b8 M# N! @7 S3 C下面重点来了,测试用例$ s- K" Y; J$ p9 y
) ?, @) B' T: q7 M( M( @
@Test* i! z* e! x8 x9 v0 y& Y
public void test3() throws InterruptedException { * j6 g# h8 q' v4 a& w# M8 u AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();' a& r' \( B0 c/ x5 F( Y
//将自定义作用域注册到spring容器中8 e; s& p- F& Q0 U7 q# u! y
context.getBeanFactory().registerScope(BeanMyScope.SCOPE_MY, new BeanMyScope());//@1 0 z$ W) I# ~ B( ? context.register(MainConfig3.class); 5 C P: b4 t( n$ b9 R" w' ~ context.refresh();. W% `% Z2 w5 X5 l- P* ], g
6 ?3 l8 Y: s# @- G! z: j System.out.println("从容器中获取User对象");6 F7 h6 s: l. L
User user = context.getBean(User.class); //@2 , ^- r! J3 j, v! e' X9 Q System.out.println("user对象的class为:" + user.getClass()); //@3/ A5 P9 ?6 _0 |6 ]% V
" a' B1 O4 e( P" O- L% p* T System.out.println("多次调用user的getUsername感受一下效果\n"); * q" }1 u& o- w; | for (int i = 1; i <= 3; i++) { " _7 L8 U2 U& [ j System.out.println(String.format("********\n第%d次开始调用getUsername", i)); 6 \8 D: W- l. ^ u+ \% k System.out.println(user.getUsername());1 M K5 G6 m4 W9 v/ G" M. D+ C* C6 P5 ?
System.out.println(String.format("第%d次调用getUsername结束\n********\n", i));" [9 K: D: M6 t v$ Q- H
}' f b0 l9 k$ ~( d) g
} 4 Y* _0 `/ O9 h8 E9 G6 K@1:将自定义作用域注册到spring容器中 , b5 I3 o- H: U( X& q2 {$ L + r, H: K' z0 M4 Z. y@2:从容器中获取User对应的bean 1 |2 q5 B+ X. P- t3 r$ v/ } " p0 \( ]$ l/ I% G ^; B@3:输出这个bean对应的class,一会认真看一下,这个类型是不是User类型的5 z0 N; X8 ~/ f2 A+ p1 b
- ]" r+ ]+ d% X: T) M
代码后面又搞了3次循环,调用user的getUsername方法,并且方法前后分别输出了一行日志。 3 V0 k v- ?* L) D 9 b" u7 d' z) L6 t" ~" h$ z9 C9 w! H见证奇迹的时候到了,运行输出& x, d' v N* B2 J7 s) P& z7 Y
4 l% B* y2 V e7 b( h4 k
从容器中获取User对象 3 w3 \$ U. u' ^user对象的class为:class com.javacode2018.lesson002.demo18.test3.User$$EnhancerBySpringCGLIB$$80233127 7 Q* f. q7 b0 S5 v* m1 x* d" F多次调用user的getUsername感受一下效果& X0 N$ @4 c; [8 M
4 L% C6 n5 o) G) ]# g
******** ! C' G, B$ \' Z第1次开始调用getUsername3 O. f/ w# s( D* y2 z! N" L4 [/ ~4 P& x
BeanMyScope >>>>>>>>> get:scopedTarget.user4 ?7 n; s. c" f# I5 _
---------创建User对象com.javacode2018.lesson002.demo18.test3.User@6a370f4; V) o0 ?! o2 g9 Y( a) ~& X' ]
7b41aa80-7569-4072-9d40-ec9bfb92f438 8 z) f$ E. W+ Y9 p0 Y5 h第1次调用getUsername结束 ! A7 ]+ D) w8 [, t******** + ?! P7 W4 P) C# T" [ ] ; z! u) l" q8 T }7 _% W ^/ H; L******** 4 d) x+ Y1 h j' P1 m3 K$ }6 s. q第2次开始调用getUsername 5 {, H# h2 l& Q) ]9 N9 T5 uBeanMyScope >>>>>>>>> get:scopedTarget.user 2 P- ?' z9 I3 ]% @% ~---------创建User对象com.javacode2018.lesson002.demo18.test3.User@1613674b- m8 \8 }% [2 J; ]; H v
01d67154-95f6-44bb-93ab-05a34abdf51f' O Z G- r9 m, N1 ^
第2次调用getUsername结束 : T: M& V7 a* k3 F1 X4 G: ?********& F! N. y& L7 F8 d* y3 ^
& |3 Q3 `9 n' S% n; P
******** $ T+ M7 X- d3 |2 E! j+ M- _. j第3次开始调用getUsername & o% u/ r; A2 {BeanMyScope >>>>>>>>> get:scopedTarget.user s- ` L9 v- S+ p0 f---------创建User对象com.javacode2018.lesson002.demo18.test3.User@27ff5d15 3 S4 T- d m0 l7 G76d0e86f-8331-4303-aac7-4acce0b258b8 : d% x" b5 n3 U c第3次调用getUsername结束 7 j; K* R5 Q1 Y+ j5 Z, G********, Q2 u5 X) H2 d% g
从输出的前2行可以看出:6 e! O9 q/ P4 Q
/ T1 z* g' N5 M( ^1 _, x# t3 [
调用context.getBean(User.class)从容器中获取bean的时候,此时并没有调用User的构造函数去创建User对象3 x9 `# h+ M( K. C" X- k
& G" C k, Y& u: V0 h# q
第二行输出的类型可以看出,getBean返回的user对象是一个cglib代理对象。 4 m( L7 \/ q4 @$ L0 s* _ k4 q( w
后面的日志输出可以看出,每次调用user.getUsername方法的时候,内部自动调用了BeanMyScope#get 方法和 User的构造函数。4 X% F: e% Z" s& H* z! u; M
' i C5 a" t2 g9 Z i2 H动态刷新@Value具体实现- V/ S; n3 u* U
( }- Z' Z9 b) {( x7 u- ~那么我们可以利用上面讲解的这种特性来实现@Value的动态刷新,可以实现一个自定义的Scope,这个自定义的Scope支持@Value注解自动刷新,需要使用@Value注解自动刷新的类上面可以标注这个自定义的注解,当配置修改的时候,调用这些bean的任意方法的时候,就让spring重启初始化一下这个bean,这个思路就可以实现了,下面我们来写代码。8 |" A. Y( ^- M
8 }( [0 L" ?) g7 |3 s8 |4 n9 w8 i0 T先来自定义一个Scope:RefreshScope : W0 l: n) m Z: @( w; J7 [% ?1 g+ g; D( ~4 {" l5 r9 [
package com.javacode2018.lesson002.demo18.test4; ! C* K% m8 A! }% W- F ! ^) r% ^* E: {: X1 {7 v6 ~4 Q- {import org.springframework.context.annotation.Scope; " G4 ~* n4 h# F' nimport org.springframework.context.annotation.ScopedProxyMode;1 f9 `! \9 C0 D0 P# `. w
% ]7 M% H& r+ O7 |9 |& w
import java.lang.annotation.*; + i% r+ r1 {( q9 g7 y& F' c, k; P& O5 [ v* t7 X1 K& U v
@Target({ElementType.TYPE, ElementType.METHOD})9 z+ m& Q. t# D" t& T4 O. i
@Retention(RetentionPolicy.RUNTIME)/ j1 a' ^5 C, E, z! g$ F3 Z3 }
@Scope(BeanRefreshScope.SCOPE_REFRESH) : b8 \8 o) [# T. { G@Documented ' a! l& x/ u. D- upublic @interface RefreshScope { - Y) |: v7 z$ C5 A# ]/ S, S6 z ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS; //@1- {: q- A& Q+ K( b# O! x" R* u
}: E' z) L" e% U6 o( R% v M) B
要求标注@RefreshScope注解的类支持动态刷新@Value的配置, A9 b% h! L, t3 I6 @- x; f
0 e! ^3 ^& T# i* G. I, @1 D
@1:这个地方是个关键,使用的是ScopedProxyMode.TARGET_CLASS ( C! S3 p1 ^: L+ G* x* \- c+ U. o6 t3 f8 T4 \" Y4 ?! D
这个自定义Scope对应的解析类 * d5 f- E. H# }, {$ Z6 x' s 1 f+ r( X/ I( L R下面类中有几个无关的方法去掉了,可以忽略. S9 ?3 s% K; O0 Y4 n
+ A; D7 H) }, R( npackage com.javacode2018.lesson002.demo18.test4; & E" m& \' R, ~ 7 n' j1 I% J6 D* x* E# D. V 4 |. H9 [$ G6 W6 a- @# A5 D$ }9 i8 Vimport org.springframework.beans.factory.ObjectFactory; 9 X5 t8 ?; } }9 Q( ximport org.springframework.beans.factory.config.Scope; / M+ s* V8 f Y9 w/ \import org.springframework.lang.Nullable; / C! o' z0 G, Y% A& ]0 a! R8 P ! y# |+ u" ?* T! Qimport java.util.concurrent.ConcurrentHashMap; ( G# |% k4 }% l" p7 n' q B, ^. _- c/ j
public class BeanRefreshScope implements Scope { * y2 {' [8 X" y6 ~5 ?6 A1 c3 V1 \/ O# c" `; P! }* X
public static final String SCOPE_REFRESH = "refresh"; 1 c0 R: z2 u, p7 K- i: f! z. H p. h6 A* p& ~, k; ~# P+ t
private static final BeanRefreshScope INSTANCE = new BeanRefreshScope();) B. | b' b# k Y4 R" [
2 E6 p8 J/ P3 D; E //来个map用来缓存bean, R( r$ N: L# F* H* d8 y4 O
private ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(); //@1; b5 H: S* `- ]8 F8 t r5 ?4 s3 }
8 \) c" Y) \9 e9 W( |! I/ c8 Z, B
private BeanRefreshScope() { 9 n( a3 Y" g1 M } : w) X4 h+ q3 J7 ?) r( [+ \1 _0 |$ M, R/ [ Z
public static BeanRefreshScope getInstance() {4 i0 ]! K% j% a0 I
return INSTANCE; - X g* I! J5 X" [$ i5 w } o: G3 [4 F& e 2 F \: e R1 O: S /**6 G% I% E6 H9 l
* 清理当前 ' v: k6 b4 X% u4 }% f */ 7 j: Q$ Y9 E7 O6 [, ~, {4 _ public static void clean() { . w3 U7 H7 e: C5 J9 j6 z1 M INSTANCE.beanMap.clear(); 6 \2 s& |/ w2 v' K4 O } " f Y$ g9 S4 _: W% l3 y1 U5 { x* ?9 r( N6 m, L
@Override. Z7 ?1 R* t3 L- _8 q
public Object get(String name, ObjectFactory<?> objectFactory) { 4 S `6 m2 p! e4 d Object bean = beanMap.get(name);; Z6 S9 `% @! T: h
if (bean == null) {- C, }2 R2 Y% g+ c9 B! g: X: M& m2 @
bean = objectFactory.getObject(); % E/ Q* l* N7 P1 P% C3 C beanMap.put(name, bean); 8 d) r* Y: `0 ^ } 7 l! ?6 e( Y9 n8 u$ m9 u* ~1 y return bean;( g# m6 y2 F! M# n
} ; B6 l$ P5 }* q8 C+ ?# V5 C; T1 j& f, o
} 6 y ]3 l: X2 z9 R- P9 z/ A& {上面的get方法会先从beanMap中获取,获取不到会调用objectFactory的getObject让spring创建bean的实例,然后丢到beanMap中 " N- w% r* J/ Y. k" p4 K9 H4 r7 M8 r5 M' i6 T, Q2 g
上面的clean方法用来清理beanMap中当前已缓存的所有bean, v0 F1 n$ ]- E+ \% j; x5 W/ b
5 x! }- K! C" A3 B) G8 x; s7 G! F% N来个邮件配置类,使用@Value注解注入配置,这个bean作用域为自定义的@RefreshScope- g. n# l8 C, \: [3 A% z# }+ J, y+ N
$ {* l6 d" c; \5 S
package com.javacode2018.lesson002.demo18.test4;0 L* G5 s# }4 B) n& c( K2 x" i
* D* h) D1 U2 U! timport org.springframework.beans.factory.annotation.Value; % d! M( r3 o, y `+ uimport org.springframework.stereotype.Component;# L( A- [. M% d, W& c- d7 J
9 m* w! l+ J' w: J. E+ l/**1 T8 B6 z3 y, L; \4 Y+ u1 \
* 邮件配置信息 # ^8 s( _2 h9 z, J( T& f */ 4 ], V+ q: P$ C: M8 b@Component" Y' c) n& I9 H r) K) s: @* M
@RefreshScope //@1: p) K0 y4 T- q$ J6 H
public class MailConfig {5 }9 r7 t& G' i! q& e g
& n( `4 i2 J/ }8 A! V6 O: t
@Value("${mail.username}") //@2# ^& N. r" S- ]! Z! \# c3 q* x2 q
private String username; 7 w" B2 U% b5 E 3 z- {* ]$ R L, X public String getUsername() {0 g R: l/ U+ o! c% v, j2 A/ Z2 J
return username; 9 I8 s4 p8 ^7 D* }& p- m } # w$ B) f' W) H5 B7 J2 m4 M1 ? 2 K3 r, M1 H8 J5 j( J l* D public void setUsername(String username) {; y# Q* r# d* X9 T0 @" U# E
this.username = username;: R" D: i+ {& z$ u- N/ b# x3 M
} " ~* |" c {; K% I; n; | " w7 \+ \2 V$ `% T" M, d @Override0 V0 \6 d5 w$ r6 f; _+ [1 @
public String toString() { : p( L6 y, D+ k' D4 |3 t return "MailConfig{" ++ C9 c) s% ^$ A( Q: N- d
"username='" + username + '\'' +; w$ `) N }7 z! `/ r$ J- c
'}';- A0 C' X* m$ L& {5 E' R8 L
}( Q* c1 }9 k5 j8 l
} 7 T1 o0 ?: z4 l@1:使用了自定义的作用域@RefreshScope! ~6 a" w" v+ U2 u8 G8 _+ Z
: h4 H# X7 |" `7 ?, z2 o s1 \7 L( X
@2:通过@Value注入mail.username对一个的值 8 J8 |2 r& ^9 K$ T ) r [3 B i) o2 a' C% c重写了toString方法,一会测试时候可以看效果。 - |! k, a8 G8 V, h/ s. Q& r C
再来个普通的bean,内部会注入MailConfig; z! [$ H: H7 m3 }0 f4 ?
7 r; {; j' H) D, D: U- p$ v0 b
package com.javacode2018.lesson002.demo18.test4;7 s( w* G' A+ h# U6 h% {
% j1 T( g- ~% Z- Q1 B
import org.springframework.beans.factory.annotation.Autowired;/ f8 n- _3 Z6 w* G& L
import org.springframework.stereotype.Component; 7 A; L2 b2 l2 N : J1 m* d Y+ z# r) D@Component # e: c, z% q( tpublic class MailService {( |% h8 B3 ^" V) s# {4 @% C
@Autowired % j6 F+ c- r+ A A Q5 i, _+ J private MailConfig mailConfig; $ V# f; V/ o* U' t, ^ " M; `% U2 T. v/ ^9 r4 S5 G. F1 s @Override, D& F2 h- m0 S( E) x7 M
public String toString() {* Q: }3 C8 q% C0 h
return "MailService{" +% U* d9 c m& S& F8 a: B
"mailConfig=" + mailConfig +0 k" O: {5 }8 M
'}';7 d/ Y; P* M6 t4 N4 m
}2 [3 F2 w% B& G+ D
}# D1 K+ F$ g" W# [
代码比较简单,重写了toString方法,一会测试时候可以看效果。 4 P& i, g# I! e / E6 e0 ]8 s, i) d7 G# ^4 P( c来个类,用来从db中获取邮件配置信息: v+ l3 d% T7 b7 j
0 o, q; x4 W1 |4 Q6 c# `; }/ S
package com.javacode2018.lesson002.demo18.test4;* T% N8 G3 C. d" Q' s" Y
# A1 P6 b6 b' V% F8 kimport java.util.HashMap;; N' n1 A# h3 Q3 q( Z
import java.util.Map;; f# d! K0 V1 m& I" ~- E
import java.util.UUID; 3 m/ o5 A/ W, K, i4 P/ G, e% l : W: y; G; J2 C' r4 jpublic class DbUtil { 6 U, x4 V& z/ E* V. c /**8 ^7 ~! y# ~7 i; W: r
* 模拟从db中获取邮件配置信息 ( C: R6 Z" r/ [2 @) Q5 M- {) \1 c, m * ) X7 ?4 H9 U4 g! G * @return) A" f% O+ s% B& [! S0 C" |
*/ 5 J* v8 c- J5 ~! F0 U$ O public static Map<String, Object> getMailInfoFromDb() { 5 H* g r8 P6 t$ v9 m* B/ e Map<String, Object> result = new HashMap<>();+ A( D* i/ C i) o1 ^6 a
result.put("mail.username", UUID.randomUUID().toString()); h5 x- a/ p y8 Q( z return result; B. D$ t1 O. G
}# P5 t0 W2 B- g) l4 ?" i4 n, e
}4 U$ A' d/ s2 [
来个spring配置类,扫描加载上面的组件 6 k, }1 A- L2 A* ]3 l8 t3 J- v- `% c$ F6 T! A, g
package com.javacode2018.lesson002.demo18.test4; * h0 q& z) E5 y4 U# ~6 T2 k g$ P* G% b; ^6 y
import org.springframework.context.annotation.ComponentScan; 8 G9 O! N, D, u% m' y6 d* mimport org.springframework.context.annotation.Configuration;: S1 ~/ l" Q- ?# p- z' R7 ?
' r. `7 C R, y: U9 j1 _7 W: }# @
@Configuration 5 o+ B, ~/ C1 c! ~ G@ComponentScan - ^/ B7 Q5 v, A! r! p6 h. O4 Opublic class MainConfig4 {* S O& r# S3 D- G2 H( |, q
} 4 D6 J( [0 s: s, q M来个工具类 * { y+ ]8 M. `* _' ? 3 K9 C. T* k5 L5 o内部有2个方法,如下:0 c `3 X' j, f
2 m' |; M; T* ?3 _5 [package com.javacode2018.lesson002.demo18.test4;6 s6 y/ K* b! a4 b! x
; E x# i L- V" Q2 Dimport org.springframework.context.support.AbstractApplicationContext;( E" M' Q' N: E+ Z7 Q+ G* V" I }! i$ A
import org.springframework.core.env.MapPropertySource;4 ^! J7 o4 q& c. O/ f0 S3 \/ j