6 w5 D) @1 [0 L' M5 y2 Z@Value使用步骤 / b5 P$ t- l. H8 u/ S! h. @" I+ e, C& G% g0 C/ u! d% I) V
步骤一:使用@PropertySource注解引入配置文件 9 V% j7 d3 y5 U) V: W( Q) X! _6 Y & }9 T P; g X. s' d5 Q将@PropertySource放在类上面,如下 ' ~0 \. m% k( S' `( |: u$ x) C9 Y* R$ n: {) @& t6 h) E; m
@PropertySource({"配置文件路径1","配置文件路径2"...}) I F. ~9 E. C* h) c! {" ~% k0 ^4 {@PropertySource注解有个value属性,字符串数组类型,可以用来指定多个配置文件的路径。 0 `0 z9 H% h! y8 L% |) o: A. R) {
如:. e. W5 \& k2 H, u6 h
2 T( r t. H3 u2 A@Component ; m: F8 u3 I' y8 q W& J@PropertySource({"classpath:com/javacode2018/lesson002/demo18/db.properties"})1 z. a, [$ }! h
public class DbConfig { ) [# U9 H) ?4 Y6 m, m, D( g}) Q2 y* ]$ y; c$ C
步骤二:使用@Value注解引用配置文件的值' t. d9 d; ~/ b; b+ c
- [) z Z- b+ y6 ^: p( d8 h% d通过@Value引用上面配置文件中的值: $ z( S, x. S- ^0 m7 {. b# U3 }7 ^1 m6 N. H v
语法 * O! ]$ |* [7 P# b# S+ ~( k9 m5 o s8 d$ [8 J/ u4 a6 V# w@Value("${配置文件中的key:默认值}")0 }" n8 M% G+ M$ f. _
@Value("${配置文件中的key}") 1 G7 M# a" r6 M, {如:4 [0 O; h: L. m+ A! S0 z3 x4 W
. Q$ f* J* U+ {3 o$ ?@Value("${password:123}")& J. f' o P- R
上面如果password不存在,将123作为值* J5 P6 q- x+ A8 a
! k* |+ a# y3 ?# m: [0 W5 T
@Value("${password}") ; [+ B. P) F& N; ]上面如果password不存在,值为${password}# [" K$ m: {: M, U. Z4 Y( U
1 [ M& Y( E' ^ i, i, ?
假如配置文件如下 : j* g/ c; r+ V- v) I. ] 6 ]$ F1 d; H1 t/ Q2 sjdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8 8 Y+ ?5 ]% T2 h) x# E5 pjdbc.username=javacode ! X% c6 V+ u pjdbc.password=javacode2 ?9 k" G3 F w0 V
使用方式如下: 8 Y) s& A: R0 U5 y0 Y0 @ F- p # m8 A0 Z' {& b* O; x@Value("${jdbc.url}")1 q7 I3 h) h( S n+ n4 C% [9 R' @
private String url;( K( `5 ~& Q5 j3 E( f, F' ]
% x0 ~8 z9 z. r@Value("${jdbc.username}") {. D2 s' J4 M& k9 F0 X4 u- Bprivate String username; $ C" v) d6 T- O2 I! g) N y5 ]. y9 h: I; x+ `0 r( {
@Value("${jdbc.password}") 9 |) v* p3 g* b' c( t' p/ Rprivate String password; - }+ c3 t1 x! s& X' f. j下面来看案例 5 r% Q1 ~0 @) |' a$ O9 [, k" C! n' J8 B; r
案例 0 C3 O1 v3 {8 T! o+ T, P# r( v7 K( M7 |+ n U
来个配置文件db.properties - b& a; u' n% V5 b & ?2 x2 |& N1 c1 M" l9 b4 Z ejdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8 k7 `, Z& U1 K0 h; v$ g/ Bjdbc.username=javacode3 t% Y4 ~' Z4 a4 o3 | X
jdbc.password=javacode ) y9 ~1 b: H0 z+ H0 S2 L8 o来个配置类,使用@PropertySource引入上面的配置文件 ( M% ^+ F X1 {! c0 [$ _. ], x+ q! i! Y! m; s) }
package com.javacode2018.lesson002.demo18.test1; - b5 W* D6 V% y) g4 y8 P& R: I' |6 C2 `9 X% H
import org.springframework.beans.factory.annotation.Configurable; X/ }9 i" ~; {) G" J9 h0 R$ l
import org.springframework.context.annotation.ComponentScan;7 T9 P p3 `$ F' o# R' _0 g
import org.springframework.context.annotation.PropertySource;8 p v L' G# b" p
& c$ n& `0 A. ?2 u) \@Configurable P$ u1 P" z2 F" i% W* Q$ x2 Y/ k
@ComponentScan) w* T s4 g( R
@PropertySource({"classpath:com/javacode2018/lesson002/demo18/db.properties"})' i* i% e* T# _' e4 W7 ?
public class MainConfig1 { ; r- e8 d" e- J. k! I+ V$ P; w) F# y}# y {7 u; s+ n* o4 j+ R
来个类,使用@Value来使用配置文件中的信息4 w h9 M8 a" h6 l
" B' f7 u! k5 Zpackage com.javacode2018.lesson002.demo18.test1;% ?# r( H" B% W3 K" I3 A5 ~9 O
a# r: f% ~2 L
import org.springframework.beans.factory.annotation.Value;) o9 h* P4 P0 q. e6 V
import org.springframework.stereotype.Component; ' ]4 h4 m# p# ^% M: }. x& _ 4 V3 E# r/ D: O6 n$ Y" V@Component/ T' w" n4 t3 C
public class DbConfig { ( U" [3 Y4 R1 W6 R* S E% s. w6 i0 Y% {, A+ } E. M
@Value("${jdbc.url}") 6 R) t2 x9 y. D private String url;& S) _1 ^& m8 G! ^- N3 O. A
- h# R* a% g0 k1 \4 [
@Value("${jdbc.username}") 1 M# y# q. D2 Q, ~- J+ P private String username; / P! J, z# o7 t( ~6 z$ X" M; ^ 5 v! Q- O# K8 f& G @Value("${jdbc.password}")' V3 O( X3 H; y$ q: ~
private String password;; e1 L$ K3 F, W f0 \
/ \5 \4 x* S3 @$ _" L$ R( \ public String getUrl() {) n! ^, u" B. X, {; X3 Q
return url; 6 M3 y9 W, X# l( }5 t } , v# d6 {( F0 e% y2 R 9 i+ F6 G2 q* J- Q+ l1 ?- @ public void setUrl(String url) { , v8 _- a2 ], x$ B9 E this.url = url;( B: ~- p. P+ G
} ; u& g$ s; l9 m6 a G: y8 |9 F/ X! v. ^/ D. I5 W8 O% a public String getUsername() {/ h1 l% y @& A9 @7 D# y
return username;1 }& s* a" |! _0 E
}! U4 L: O; D5 u7 ]8 c. J' q
: O4 b! F' I8 d! {; K
public void setUsername(String username) {" z2 |7 k! @$ n- w. |# U+ P" {* Z
this.username = username;) ]2 r+ C2 ~4 m4 Q
} ~3 B& E4 a1 \+ P$ i; s+ ^4 | 9 ]( ?( @3 g" D+ d- W. }# L. f public String getPassword() {( ]! V D2 g w# E
return password;0 J' C5 v, J: i& G9 B
} % x) r/ e. L3 P* I! e, M: X/ k7 }7 L N x& k
public void setPassword(String password) { ; [) Z2 {1 n) n9 `$ y6 S! u3 Y this.password = password; / q' L; N" W9 ]/ T1 s6 Q! c } # A! \3 C3 b( D" p {/ @# ]( H2 x2 v& G/ C1 o
@Override $ Q' r. }: Y. G/ _ public String toString() {4 U9 Z5 o9 V+ ~: L& G
return "DbConfig{" +6 N9 F4 e/ R6 J9 P* M' R. e) e
"url='" + url + '\'' + ' s5 N" n( e9 W ", username='" + username + '\'' +: h$ q$ f9 ~- J! W3 m3 M, A
", password='" + password + '\'' + # @8 n* t! [, \6 J" g7 N/ N' v '}';9 U7 e' B6 Y* Z9 F: k
} 1 N! K4 @$ K* M3 R" ]* I. ]2 |1 D} 4 c! G4 H7 T) q- O9 I+ V上面重点在于注解@Value注解,注意@Value注解中的1 C9 V' z, E) I" P& ~; ]6 l- ]& L
* l: s/ b" O1 A. Epublic class DbUtil {9 t) E1 {2 ~; P% }! M( p' K
/** ( ?, W3 s' _% G# y* g6 V * 模拟从db中获取邮件配置信息* Z) I1 S! J, r% p4 v$ J {
* 8 P. r7 s- ?7 l" u0 p * @return , ^2 ^! L, K# Y1 x, o0 u */ 6 V3 l. f5 D6 l- Q" o: u" \ public static Map<String, Object> getMailInfoFromDb() { ' X( V9 }7 Y- R% n: I7 M Map<String, Object> result = new HashMap<>(); 7 c' _% G! V3 {- l: r3 J! C result.put("mail.host", "smtp.qq.com"); " t; C/ b# E% \1 j; _. } result.put("mail.username", "路人"); ! v1 H7 t `: G* P& J9 F result.put("mail.password", "123");1 ^$ p, A4 w% v8 T) s, O0 e b
return result; 8 E% r6 B" ^. E& V } 0 w3 }+ ?4 T" g: c+ n} ; |- \; v% T/ w, w来个spring配置类$ b1 J/ u0 l# X
2 p& o+ _1 P0 }package com.javacode2018.lesson002.demo18.test2;1 J6 S) |2 l; A1 J( C
# W" k# Y! p: @. S p: x( p8 \
import org.springframework.context.annotation.ComponentScan; ! F, q( x+ j; R3 \+ Z+ limport org.springframework.context.annotation.Configuration;( r% D- z% P0 M% A) I w
/ J2 R4 _" X, ]+ [0 K/ r! N9 S+ y& O@Configuration 1 h6 \4 C4 \. I" v/ I# k# _- O@ComponentScan }5 u- y/ x& A' ?9 {
public class MainConfig2 {7 d. T, q0 k* Q- c# L! e& Y
}, w( E& m J! `3 q' z
下面是重点代码 9 m2 i1 [; P, P: |% `+ _# W: K& Y) T/ O5 J$ `& {4 c% h( t
@Test , c" O9 R1 Z; c' y- D& |: E' Xpublic void test2() { / z) z3 _+ i# ?3 f5 g3 O AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();6 F+ o" S( b' W: I/ |* Y' W" g
% p" L) K' @6 ?6 i) u: B /*下面这段是关键 start*/ ' V* ^8 F; a5 C* ?8 b+ [ //模拟从db中获取配置信息 0 y$ j9 c8 I1 s" F Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();* q4 f6 x! U) ]& Y* M
//将其丢在MapPropertySource中(MapPropertySource类是spring提供的一个类,是PropertySource的子类) ! U2 u9 }) G5 q( j+ e MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb); + T$ I8 j& J" |) Y8 r0 }8 A //将mailPropertySource丢在Environment中的PropertySource列表的第一个中,让优先级最高 + N9 Q, h9 s; R2 [ context.getEnvironment().getPropertySources().addFirst(mailPropertySource); " v$ e. o: o1 V5 d /*上面这段是关键 end*/, E9 j5 b* @* v; Y
( b5 B& j) b- D" L, a! s! V( M9 C/ @( f9 }) s
context.register(MainConfig2.class); & s, d3 p( K' q# r1 B0 `& N7 O* m3 R context.refresh(); 3 o+ S7 ~/ |8 `' Y MailConfig mailConfig = context.getBean(MailConfig.class);0 a. O2 Z0 H- E; H- C. E: {' F
System.out.println(mailConfig); 0 N- n3 {3 t/ D) F} ) ~ }0 o& I" a) D! L0 @# V/ }8 @注释比较详细,就不详细解释了。: S1 I* o1 Q0 j. T4 B
! r9 A9 B2 I' f. f8 @( l6 C
直接运行,看效果- S$ c" E: K" Q' A, B5 o' i K9 X
0 j5 o& g$ {+ s8 k. Z3 c. CMailConfig{host='smtp.qq.com', username='路人', password='123'} 9 V0 K$ j* Y1 l$ O0 e. f( e9 v有没有感觉很爽,此时你们可以随意修改DbUtil.getMailInfoFromDb,具体数据是从db中来,来时从redis或者其他介质中来,任由大家发挥。 ) x2 C e4 r8 y" A" b; S( ^' u; c) p2 m4 \0 J1 G9 {
上面重点是下面这段代码,大家需要理解, w: N/ O/ Y% G& h& h# d
J$ Z4 F, l* S5 f
/*下面这段是关键 start*/ $ f- f; K* ]8 Q//模拟从db中获取配置信息 - g) |- z7 v/ i r. x9 }( S1 IMap<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();7 q7 O0 T2 ~4 u3 h/ T
//将其丢在MapPropertySource中(MapPropertySource类是spring提供的一个类,是PropertySource的子类)( c; }/ u- D+ D2 P1 U' P6 i
MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);9 p+ _6 t4 I2 Z! `
//将mailPropertySource丢在Environment中的PropertySource列表的第一个中,让优先级最高 $ V" v7 L5 L/ j- D/ O# E3 `context.getEnvironment().getPropertySources().addFirst(mailPropertySource); n/ M8 P- H o, g% z) r/ ?/*上面这段是关键 end*/ T$ H) z# _; T( L* O4 g2 m
咱们继续看下一个问题8 q" K9 [8 J* q# g) J( ]
% @7 \5 ~3 v% r% M t0 ?% m1 \! T如果我们将配置信息放在db中,可能我们会通过一个界面来修改这些配置信息,然后保存之后,希望系统在不重启的情况下,让这些值在spring容器中立即生效。+ |" b1 ~) b0 @
, i* p3 Q1 i3 j! M; D2 j. V: b
@Value动态刷新的问题的问题,springboot中使用@RefreshScope实现了。1 Z7 @3 f! Z, i
8 i4 R- J! X% G y& Z实现@Value动态刷新 1 @3 [+ z/ {* ^+ O7 E / `! G) H. E1 \1 D7 p( ?' @先了解一个知识点7 n6 N$ N5 L- p
6 m/ R; h. M- T/ N4 d! N
这块需要先讲一个知识点,用到的不是太多,所以很多人估计不太了解,但是非常重要的一个点,我们来看一下。4 b( b4 n* U' _2 f
- ^ b& ]$ V" P) |- h, ?这个知识点是自定义bean作用域,对这块不了解的先看一下这篇文章:bean作用域详解6 X; e3 u# R# i( q e* a- O4 T
8 o! ^! s) u# h$ j( {* g: G+ ~5 K! f
bean作用域中有个地方没有讲,来看一下@Scope这个注解的源码,有个参数是: * k0 S) F5 f" U% u/ X6 q% i; b! P5 C9 L " k' w& }# b6 [; O' aScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT; 6 \: M4 ^2 M4 U1 \, D @# V这个参数的值是个ScopedProxyMode类型的枚举,值有下面4中 6 o, b5 y( B! W4 V" |7 p: b 7 l& t) `$ Y* k6 x$ Ipublic enum ScopedProxyMode { 5 p1 N9 o P- {4 {6 U DEFAULT," W# Q' o1 m' z+ A7 R4 e, ~4 ^
NO,, c i: ~, G& f# }5 b
INTERFACES, 4 {$ r* w4 c( V3 ?$ b TARGET_CLASS; . C$ A- E$ `0 s) ]}- k! y0 @5 D ^; q: c
前面3个,不讲了,直接讲最后一个值是干什么的。( Z; [+ K! p" K! H5 g. e9 Q
5 e7 r0 t. Q m* O) {当@Scope中proxyMode为TARGET_CLASS的时候,会给当前创建的bean通过cglib生成一个代理对象,通过这个代理对象来访问目标bean对象。* a; q' c4 c8 p# }8 m
5 Q- B y/ W. ^0 l
理解起来比较晦涩,还是来看代码吧,容易理解一些,来个自定义的Scope案例。2 q& C1 N/ u P8 ? S" s, ]* T
+ r" Z" b, S- T# t自定义一个bean作用域的注解 e3 a: f2 J$ z; r' `. h % M8 i7 I6 p$ ^( O; Wpackage com.javacode2018.lesson002.demo18.test3;' a# I. P- G0 Z* z
4 `) N+ h) z2 n8 ?. c2 Uimport org.springframework.context.annotation.Scope;6 a6 J# R7 J% U4 N& d0 N
import org.springframework.context.annotation.ScopedProxyMode; 6 W( ^: F0 y, [" [( l , S# C& B6 Y, x4 Y9 S* z7 Wimport java.lang.annotation.*; , F+ F/ D: y! o5 K9 d: `! T5 C ( o; Y$ x* {/ q0 w+ Q4 V@Target({ElementType.TYPE, ElementType.METHOD}), O; M0 N, M3 v( ]( W
@Retention(RetentionPolicy.RUNTIME) + O/ q7 o- O$ K, S. Y@Documented 3 J: {; s1 C1 d1 [4 e2 p@Scope(BeanMyScope.SCOPE_MY) //@1. F- ?5 Z. [0 v6 z7 ~
public @interface MyScope { " t, n5 d& r" A6 k1 C8 y /** 4 Y( {! E; T! T * @see Scope#proxyMode() 0 U; t- e: J- ^ */, Y. ~- n+ T& @2 |0 c% f' B
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;//@2 5 `8 L/ J3 M3 Y}% k k4 ?, d. S& S( `6 f5 U
@1:使用了@Scope注解,value为引用了一个常量,值为my,一会下面可以看到。$ s/ _* F) i/ Y0 x4 [: Z2 l/ N
3 d; s: f' w, G8 I, U@Component9 A" I0 f: f x; V" O6 x2 p
@MyScope //@1 T* o- P3 i9 j6 G3 c" b) c
public class User {" V/ L+ o, _, |/ [2 @
3 P% S1 m S+ u# U0 }
private String username;' @: z D# m3 j/ C5 U1 U/ g
' f( K1 B* r, P( [ public User() { * d* R7 {" f; L, m2 w& q) p System.out.println("---------创建User对象" + this); //@26 m2 h) z. Q' w1 H. d
this.username = UUID.randomUUID().toString(); //@38 t$ A2 ~. @2 {2 W8 p3 h4 z; o
} , o, Z: r( {/ k5 k: X" a, h$ t. {% a( t2 e6 Y& b
public String getUsername() {" y. s# s# P0 d- y5 T; J/ _
return username;, Y. }4 ]% e. p/ Y8 S7 a7 K2 K2 v
} _% r: X1 |/ Q8 L1 r % H- F: F, N8 i) S. V8 C public void setUsername(String username) {) l m" O2 @7 z& H
this.username = username; ( _0 P: r7 J- x# h) u4 Z } 4 _3 `+ z+ I. K8 C# l3 `6 a% N0 Z0 W1 E9 B; u
}/ [ M! \ D# \) ^7 [' b
@1:使用了自定义的作用域@MyScope/ \* @& z4 ] l" R/ o; ]0 `
4 _& k* p; ]# u+ I3 U
@2:构造函数中输出一行日志 : j1 t) l: v1 h9 b" i. W; N3 c3 J4 K' E- C0 D8 j& U
@3:给username赋值,通过uuid随机生成了一个( s/ ]! R. c( T& {3 V ^; |
7 x3 w7 s2 a. f# ^. _
来个spring配置类,加载上面@Compontent标注的组件 j$ n3 ^6 r- G3 ?# K7 F1 X5 x 4 k4 \( z; B( r/ ? Tpackage com.javacode2018.lesson002.demo18.test3;* [: @" R9 W' `. V7 D" q
! n3 x1 Q! F6 P
import org.springframework.context.annotation.ComponentScan;8 f7 ~# w! H$ o, X* G! J
import org.springframework.context.annotation.Configuration; 4 [& G Q7 N6 d8 N+ g / V( C5 \' S) |* U: ^. Z) e@ComponentScan * X* Q" K+ |) Q3 x4 m _+ T% |1 G@Configuration 3 H" Z6 h/ c7 Y2 C1 }3 o9 w; W7 Kpublic class MainConfig3 { y- b8 Z/ l& p, Q: T3 M) v& T9 {, u}% W: a3 t- {; }8 O
下面重点来了,测试用例 ) n+ R q9 M7 F 5 Q0 O* @9 _* s) v' E1 }7 L@Test; Z: f1 v8 t5 K0 W! K/ T' l. [
public void test3() throws InterruptedException { 8 c. w6 V7 ~& e3 g) | j6 a9 D AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();* J7 F0 `) X* p5 k# l
//将自定义作用域注册到spring容器中 1 A( \+ L7 Q' }- U( }4 Y5 C1 L context.getBeanFactory().registerScope(BeanMyScope.SCOPE_MY, new BeanMyScope());//@1- d! f2 S( P7 N. Y- o" d& f
context.register(MainConfig3.class);0 O2 q2 T, M4 S8 w# S
context.refresh(); " S5 O, Y0 Q* B 9 B" y1 L2 {8 t: i2 r System.out.println("从容器中获取User对象");' b7 z2 O6 @4 D, _" V3 H/ R/ K
User user = context.getBean(User.class); //@2 ) y$ N* Q; H8 x System.out.println("user对象的class为:" + user.getClass()); //@3 $ z# [ c0 K7 L9 p( O; e+ F0 F3 H1 a; B, e$ r E$ j$ c7 o8 m
System.out.println("多次调用user的getUsername感受一下效果\n");; G4 c4 {) K# x4 e
for (int i = 1; i <= 3; i++) { * w/ d# g: I; O( {5 r System.out.println(String.format("********\n第%d次开始调用getUsername", i));) O0 |) Y8 a8 J% [/ C
System.out.println(user.getUsername()); % _0 C% h Y+ I- E* @7 o. V System.out.println(String.format("第%d次调用getUsername结束\n********\n", i)); ! K; B+ _' W( H7 x# g } : u5 }, P% `: f7 m1 L, B$ O}' e T9 v/ k3 E8 V+ i' z
@1:将自定义作用域注册到spring容器中8 l/ E i) Q- N; A% t5 S5 q; @
' S. F1 C! Z) X9 M; r@2:从容器中获取User对应的bean; v8 K- ^9 Q" ~; X# ~
% G- l& q6 |7 C6 K7 H@3:输出这个bean对应的class,一会认真看一下,这个类型是不是User类型的 7 Y& r& o1 W6 Z. i; d1 j+ c+ A! ~/ \8 v9 a! ~
代码后面又搞了3次循环,调用user的getUsername方法,并且方法前后分别输出了一行日志。 1 k8 E) S8 A. H) t4 e9 E j# ~8 I7 Y5 e
见证奇迹的时候到了,运行输出 2 A( \+ R% H/ s1 {0 _$ E& T" a& W; L8 ^. n
从容器中获取User对象 5 X$ H* \7 M8 ?: c: C2 iuser对象的class为:class com.javacode2018.lesson002.demo18.test3.User$$EnhancerBySpringCGLIB$$80233127: c2 l+ m+ O) ^% S5 J" ~% Y1 T6 ]
多次调用user的getUsername感受一下效果9 k( w2 q7 p% Q, }
% U& o& K, u2 c: Q" _
********( K% y3 Q" r1 e; P/ [# f
第1次开始调用getUsername: k; {* I" u2 K3 u( q9 x: ~/ e
BeanMyScope >>>>>>>>> get:scopedTarget.user - q# A, Y3 J6 Q---------创建User对象com.javacode2018.lesson002.demo18.test3.User@6a370f4 " [& ^* o9 E6 t- y* Z5 r J4 N7b41aa80-7569-4072-9d40-ec9bfb92f4389 c' \: ]! x& @$ A; ?( w1 q
第1次调用getUsername结束* Y' H4 v B9 X$ F
********% `% f, y) n$ z: F