From bf67cb3cad1e87808626477a99750a42d856ae57 Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Thu, 9 Sep 2021 17:41:22 +0800 Subject: [PATCH 1/8] project init --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 119fcc7..fb36398 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,19 @@ #### 软件架构 @Autowired 注入默认按照类型注入,name属性赋值则严格按照名称注入,没有则不注入 + @bean 类似于Spring的@Bean + @Component 配置类和需要被容器托管的类必须写 + @Note 目前仅用于注释 + @BeanScanner 包扫描注解 + ContextBean.java 容器对bean的封装 + ContextHolder.java 容器 + ContextRun.java 容器启动器,只有调用其中的run方法,容器才能够正常启动 #### 安装教程 -- Gitee From f08b45586aed11e67c1eebfb36e420c6030fbdb0 Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Thu, 9 Sep 2021 23:13:22 +0800 Subject: [PATCH 2/8] project init --- .../java/org/needcoke/ioc/ContextRun.java | 18 +++++++------- .../org/needcoke/ioc/app/MainApp/Main.java | 20 ---------------- .../org/needcoke/ioc/core/ContextBean.java | 4 +++- src/main/java/org/needcoke/ioc/test/A002.java | 19 --------------- src/main/java/org/needcoke/ioc/test/A01.java | 24 ------------------- 5 files changed, 12 insertions(+), 73 deletions(-) delete mode 100644 src/main/java/org/needcoke/ioc/app/MainApp/Main.java delete mode 100644 src/main/java/org/needcoke/ioc/test/A002.java delete mode 100644 src/main/java/org/needcoke/ioc/test/A01.java diff --git a/src/main/java/org/needcoke/ioc/ContextRun.java b/src/main/java/org/needcoke/ioc/ContextRun.java index 839fbee..f747ed5 100644 --- a/src/main/java/org/needcoke/ioc/ContextRun.java +++ b/src/main/java/org/needcoke/ioc/ContextRun.java @@ -9,6 +9,7 @@ import org.needcoke.ioc.core.ContextBean; import org.needcoke.ioc.core.ContextHolder; import org.needcoke.ioc.util.MethodUtil; import org.needcoke.ioc.util.PackageUtil; + import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Set; @@ -40,9 +41,9 @@ public class ContextRun { Object component = init(aClass); Method[] methods = aClass.getDeclaredMethods(); for (Method method : methods) { + method.setAccessible(true); Bean methodBean = method.getAnnotation(Bean.class); if (null != methodBean) { - if (holder.nameBeanContext.containsKey(methodBean.name())) { throw new RuntimeException("already has a bean name #" + methodBean.name() + "#"); } else { @@ -58,7 +59,7 @@ public class ContextRun { } ContextBean componentBean = new ContextBean(); String componentName = aClass.getAnnotation(Component.class).name(); - if(StrUtil.isBlank(componentName)){ + if (StrUtil.isBlank(componentName)) { componentName = aClass.getSimpleName(); } componentBean.name = componentName; @@ -76,24 +77,23 @@ public class ContextRun { Object component = holder.componentMap.get(componentClass); Field[] fields = componentClass.getDeclaredFields(); for (Field field : fields) { + field.setAccessible(true); Autowired autowiredAnnotation = field.getAnnotation(Autowired.class); - if(null != autowiredAnnotation){ + if (null != autowiredAnnotation) { ContextBean beanAutowired = null; /* Autowired的name属性不为空的话,就通过名称注入 */ - if(StrUtil.isNotBlank(autowiredAnnotation.name())){ + if (StrUtil.isNotBlank(autowiredAnnotation.name())) { beanAutowired = holder.nameBeanContext.get(autowiredAnnotation.name()); - if(null != beanAutowired && beanAutowired.clz.equals(field.getType())) { + if (null != beanAutowired && beanAutowired.clz.equals(field.getType())) { try { - field.setAccessible(true); field.set(component, beanAutowired.value); } catch (IllegalAccessException e) { e.printStackTrace(); } } - } - else{ + } else { /* 名称为空,则通过类型来匹配 */ - if(holder.typeBeanContext.containsKey(field.getType())){ + if (holder.typeBeanContext.containsKey(field.getType())) { beanAutowired = holder.typeBeanContext.get(field.getType()).get(0); try { field.setAccessible(true); diff --git a/src/main/java/org/needcoke/ioc/app/MainApp/Main.java b/src/main/java/org/needcoke/ioc/app/MainApp/Main.java deleted file mode 100644 index 25c1008..0000000 --- a/src/main/java/org/needcoke/ioc/app/MainApp/Main.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.needcoke.ioc.app.MainApp; - -import org.needcoke.ioc.ContextRun; -import org.needcoke.ioc.annotation.BeanScanner; -import org.needcoke.ioc.core.ContextHolder; -import org.needcoke.ioc.test.A002; -import java.util.List; - -@BeanScanner(path = "org.needcoke.ioc") -public class Main { - - public static void main(String[] args) { - ContextHolder run = ContextRun.run(Main.class, args); - List list = run.getBean(A002.class); - - for (A002 a002 : list) { - a002.print(); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/needcoke/ioc/core/ContextBean.java b/src/main/java/org/needcoke/ioc/core/ContextBean.java index 9a2e705..3b70fcd 100644 --- a/src/main/java/org/needcoke/ioc/core/ContextBean.java +++ b/src/main/java/org/needcoke/ioc/core/ContextBean.java @@ -1,6 +1,8 @@ package org.needcoke.ioc.core; - +/** +* ioc容器的封装bean +**/ public class ContextBean { public Class clz; diff --git a/src/main/java/org/needcoke/ioc/test/A002.java b/src/main/java/org/needcoke/ioc/test/A002.java deleted file mode 100644 index 9d14ad1..0000000 --- a/src/main/java/org/needcoke/ioc/test/A002.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.needcoke.ioc.test; - -import org.needcoke.ioc.annotation.Autowired; -import org.needcoke.ioc.annotation.Component; - -import java.util.Map; - -@Component -public class A002 { - - @Autowired(name = "vvsf") - private Map map; - - public void print(){ - for (String s : map.keySet()) { - System.out.println("s:"+s+"->"+map.get(s)); - } - } -} diff --git a/src/main/java/org/needcoke/ioc/test/A01.java b/src/main/java/org/needcoke/ioc/test/A01.java deleted file mode 100644 index 28beb85..0000000 --- a/src/main/java/org/needcoke/ioc/test/A01.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.needcoke.ioc.test; - -import org.needcoke.ioc.annotation.Bean; -import org.needcoke.ioc.annotation.Component; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Component(name = "DDS01") -public class A01 { - - @Bean - public Map map233() { - Map map = new ConcurrentHashMap<>(); - map.put("abc","222"); - return map; - } - - @Bean - public Map vvsf(){ - Map map = new ConcurrentHashMap<>(); - map.put("awerwrqwrbc","wefrgerghtrgh"); - return map; - } -} -- Gitee From ccab75c81ddf481737e1be117dc9328e397ea928 Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Fri, 10 Sep 2021 00:04:31 +0800 Subject: [PATCH 3/8] project init --- .gitignore | 1 + .../java/org/needcoke/ioc/ContextRun.java | 30 ++++++++++++++----- .../org/needcoke/ioc/annotation/Init.java | 10 +++++++ 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/needcoke/ioc/annotation/Init.java diff --git a/.gitignore b/.gitignore index 0107921..baf4b2b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ bin-release/ *.apk .idea/* target/* +/src/main/java/org/needcoke/ioc/test/* # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` # should NOT be excluded as they contain compiler settings and other important # information for Eclipse / Flash Builder. diff --git a/src/main/java/org/needcoke/ioc/ContextRun.java b/src/main/java/org/needcoke/ioc/ContextRun.java index f747ed5..0092342 100644 --- a/src/main/java/org/needcoke/ioc/ContextRun.java +++ b/src/main/java/org/needcoke/ioc/ContextRun.java @@ -1,17 +1,16 @@ package org.needcoke.ioc; import cn.hutool.core.util.StrUtil; -import org.needcoke.ioc.annotation.Autowired; -import org.needcoke.ioc.annotation.Bean; -import org.needcoke.ioc.annotation.BeanScanner; -import org.needcoke.ioc.annotation.Component; +import org.needcoke.ioc.annotation.*; import org.needcoke.ioc.core.ContextBean; import org.needcoke.ioc.core.ContextHolder; import org.needcoke.ioc.util.MethodUtil; import org.needcoke.ioc.util.PackageUtil; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.List; import java.util.Set; public class ContextRun { @@ -44,7 +43,7 @@ public class ContextRun { method.setAccessible(true); Bean methodBean = method.getAnnotation(Bean.class); if (null != methodBean) { - if (holder.nameBeanContext.containsKey(methodBean.name())) { + if (StrUtil.isNotBlank(methodBean.name()) && holder.nameBeanContext.containsKey(methodBean.name())) { throw new RuntimeException("already has a bean name #" + methodBean.name() + "#"); } else { /* 通过反射执行方法,并获取反射值,注解有声明名称则用注解,注解无则使用方法名,存入context */ @@ -54,7 +53,6 @@ public class ContextRun { } holder.putBean(beanFromMethod); } - } } ContextBean componentBean = new ContextBean(); @@ -96,7 +94,6 @@ public class ContextRun { if (holder.typeBeanContext.containsKey(field.getType())) { beanAutowired = holder.typeBeanContext.get(field.getType()).get(0); try { - field.setAccessible(true); field.set(component, beanAutowired.value); } catch (IllegalAccessException e) { e.printStackTrace(); @@ -107,6 +104,25 @@ public class ContextRun { } } } + for (Class componentClass : componentClasses) { + Method[] methods = componentClass.getDeclaredMethods(); + for (Method method : methods) { + method.setAccessible(true); + Init initBean = method.getAnnotation(Init.class); + /* 执行初始化方法 */ + if (null != initBean) { + try { + Object o = holder.componentMap.get(componentClass); + method.invoke(o); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } + + } } return holder; diff --git a/src/main/java/org/needcoke/ioc/annotation/Init.java b/src/main/java/org/needcoke/ioc/annotation/Init.java new file mode 100644 index 0000000..d595ae2 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/annotation/Init.java @@ -0,0 +1,10 @@ +package org.needcoke.ioc.annotation; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Note("标记Component类初始化方法,在构造方法执行后执行") +public @interface Init { +} -- Gitee From 00fc9566cfa49c8fa3018701e174551ed4eaff7a Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Fri, 10 Sep 2021 00:11:49 +0800 Subject: [PATCH 4/8] project init --- README.md | 2 ++ jar/VER-0.1/needcoke-ioc-VER-0.1-sources.jar | Bin 0 -> 11002 bytes jar/VER-0.1/needcoke-ioc-VER-0.1.jar | Bin 0 -> 17009 bytes pom.xml | 2 +- 4 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 jar/VER-0.1/needcoke-ioc-VER-0.1-sources.jar create mode 100644 jar/VER-0.1/needcoke-ioc-VER-0.1.jar diff --git a/README.md b/README.md index fb36398..7b829d9 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ @BeanScanner 包扫描注解 +@Init 被标注了@Component的方法在执行完构造函数后会调用@Init标注的方法初始化bean + ContextBean.java 容器对bean的封装 ContextHolder.java 容器 diff --git a/jar/VER-0.1/needcoke-ioc-VER-0.1-sources.jar b/jar/VER-0.1/needcoke-ioc-VER-0.1-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..49a9dbbf74f6d8fcd508cd3077894bda7049c2d1 GIT binary patch literal 11002 zcmb7K1z45I_r8Rbv~-6c-7QE7azXOap>%hn0@5MirMnxHkdW?9>6Xq50#ZuI|LX1v zy6o=n|MASjr_Y>shWDE}bLN~^K?V*U2LM1q0%*W6fCwN#Ibr|+Km^*L{|k_nP!?qd z%1N@yipl{cB|yq7vXTd~eZ7xmm|2Fi!e2}4UKgYc4#;HyZ z`q>Bi0uOCA_9m?V+ym{;9@a)ih6XkkM%TJh02qD&!}`6enT^4zT*GFRhi}^nmWKl3Z2q8Kwun%f^ z0&NV?_Wi5>)t&Ww_iN0HIyu_7nAsZ{vY6{R>&b?RnzxFeN}M3fi@F+_u;XuZ_*G#_ ziAl}E(HR)UO~&+5@cItxy!R`5?q&TvKf=N>NhC;h*i&8#ZW@o&3*ICyuXH$KUmuO* zRmQW$050I2c?@ivurJ;@;?6F!eMOQQ2DVvlV~X-`j@&2?%JX6lsNOM~^<^Yox)5*H z2_^0v)I51p08DsAnyX?yY)Ei|CBC^#fNqLR_9!|MW3&n4h^`-Pq1aOjrHjIWDA}68OQB}+pa<9qTz$dpJZsw`DNZz>-kQg)cmpnqOv|#6u`fdJtc^ft-R&C^s&W@{C^E zX#c>i+QS`x%%*A_haa#kVswkt%5DocmoBAVS_vS3lc!KsYpz=xUY?EHCOf+A~PT>mkR7d#=&&+>sfcIC9`COwy+M(R;L)(AtK5-i>TN`U5YsWts zZkBEnLG|B-J3vp*9dE6B8S8z#&}=ldLna7JG7hkt$wJ+1dG`g$+h%br7W-*ym3JKI zv**)=RCY2pEGeK@kUt$FiiW-jbiFaX*hzMAOUR}ZT*hh*O!~HO>%=}yCPVeMHk?*} zN1oH~o8Fwzr%D|IS8?;wuSfHhG3=e7$!*n(xeJM!=&JJ$;Wv^M*L2S%yK19Fe$Rk~QECwgUMWq|{Oa$~)H^5Zgny}hhJrf%r@xduHH zd-|BWSQ!+%q?u$!dx3p2k0K^UU<+-LfK1Zz%tLw3{lI?Nen#YcCaJLlC$#-Nm;5n! zX~gUpfP~i;_rjZ(=Sh7l()DQyjhOOJ)xcD-=POF!wNV_3PS(b|D&m_{-pBY59=>%r zQZQnaTZ7%nNLaOetc&nY{ITWqfJtKRmSA$((o|hcEZS{~bCcxWr%c;!0_hkn3a|-U z3JLJ#z-G!jruK@O`t}9hCQpom#JE}2m|5|E9OYM@$>{B6FB+5{qyK>-IU7f#A4^3C z+$aZi)Ey^MZqUt0lx`8Pp_z}UoI!)4r^|)UEA*`T6-%tN%9s~&kXKO9y^VqK^~Kk& zw^<~_Zpk0-o>@*6DKM2#uvO|N(BT`4J|=2buvq>8%#?4TKsD4Lge;)PcHwL-iJ0tv z9D23P_LAU1>U*2o+>^7|*AqJvtp>fvFJ6Ww65lpa!c|uix=gPa^-dc+-L7&r)sb0# zLV(IqeyiHFA2&Knr?MbW*45dAU%*>DX727eD?jPME6*ImICOVTd0ri10h;w&jn57f zX(WNA`kO~w#PY5dM1(`-7f}JCW3$f)5r$)({XG`*?cgED)_ZoVZ$@u1DRSdDQRWiz z^oZsz3zs4LTw8MA!xVG+goK7$0DwOM06+mZCvywQC zk9u6i}urgKN$luc2L$4i3*dI^v1EU0qD*(Oj25U-ZU}py_4U6BR zQ-U)yZ9shz18NkTKi8O9t}v;m?0oRH{J8JAjmN>azURvb&RbQ|U~*+EX{q*oNSm{} z$;x^4!TM~qKRMfqLhZO=!|qfVIc9CQ_F#&B^TeJoHm4EpWSml}-rDIX8Al0n^zs9v z71~-cHs%WSrh&pHLCq`5oZ!9lK4ZiUIg4|nN;nj=m{+EV1v6b>9j9NnxPv6XcaiXr=CwI* z`KDX29(`pjR~|)bc=Vn62)uvfpGCi%y$=@Uk*38wE&7K~~r93N@Z((tHK}&UEzA&g$tOn!GuvzWZ zxy=8{r|Xk&2k|2Sp8QMxp@iFY%edXC`>RzL`Lf7HJ(06kv+jNgeECey0`E!)!-wqs z1XoBS?x9t=%LyWNc9uM#d`U@x+l}MwClX74H*@%wzk+n=BtP)MeR!F`EN2rz{n9kx zV@CU>@ZzlYWz$D0F5ZqNl4RLsG($5EaHNVVh*rEwhf}zGBM{Y$_8eFAuryJ}{R{!v zHl$Rg0Kh0$Pl-(HV7GplE!Emn_jCr8g5 zzr;#gu@f^l`ZtfiTHi^|1y2u+)Q^wPZG7wK9(YoIy0?Al|r{3x=hx1BSfRZ9fG&-W3 zfPK1HZwxTK>~2H0y(L2zv8^tPF0#Io%&}>*K zx5hqQ^!XDcQ<)=YPxg288{|U6BdZA1=Khd}k(Ftp#Vn{}90+Qfrex1$t$;p;*#4!& zOtM;jBIlr4;JU~vYuThVIe@I;GxNuLFzLza>yCEctauQ(#Q zAyKd}={!x*Y4bxo_0#x9o+I>Oj0b7_yhzkircXwq1UJ8gEw9~?oV0~NTM=Jd#LITw zjA>3>#{F4L2glkIQAl2GH1Pq4)?@6r%uUE6GSTjk>h%6qe*7I_Yvx@Na!V5OI)Sgt zxFMA;@7>a8X4J-1aDaKi3Mb&wC0s{V{xe1$k9TK`8*Z+%gfW|$dL#J0EHcCkh@r9# z7W%B^`n&S1JkqhlNfx$P?L+3-&j6&Me9b-Fsuv%hzv_BH&Z2#}^U5|q26IW%KOa?} ztf3IY=t~O?%d@)iX%D=&WYXX|;t?0m-9j;RT= z=6qCaQH!H&>QwS-D3&S}vvTBeYJPQhX^zbLg+S>GgVYymhnuWcN>5d9KVWG*qd9|s zZ@bnASC0#je_9?a+B?Iz3OByr92fcpJa0kIQ4&}Hfc7Te|KsNP=K=6D%y$RZo42xI zzI5@KU)-abp&x)-DZcAEps+D25rdxD5$;VDpcI+@;laaN=S}6BW3R_&1kp=th`gUD zKiJK8COb0r;Bh@t5Rku1Wn%6Xq`;&nz1(#2qL{q10pI8C*lz04!OTRj!NvaG#c32} zbWEH3yjHXZB|(XI(F7Z4cp*7n6EzwF;PD8J=k$*(RoO*k<{7jOC!G9{(Z*BDq1i=p zSI3#Mc}6!bjM9&!zXC!asUogp%9S01nKPu9#AtIu%Cb&8s%b08+nq;?O#NksgYT<5 zuE;LkHg(BUa*ZWb+nA?ZHq-pIZ(8kp39wA6`<|@$gVELYI&jX(Ut4zDVI7}e3rfzQ zSI9H8_*Ggs0DuFUa-jP!BBg9B4gb6(WUKaD-(kmWpgtmflfq+Gk*yYiP!XOM2piy! zXzQFjy3_H*ExFuFaw|O+g%UT2Oauv;g#tYr30`8=E+OZ2^W@o`U3qMe;}Xam;owRC zI$^Q<_VxmgsCK!PWFTaZR3G$d5*8g^c_sqx9!@RxZK8h(_bkOhUOnS}^`bAE9KabtDx`4o>^DF>|(997(@XvKLi>Z?H3tWX#>AC1jE* z-%3U%OzzHi#{0tm{=?9W*O%GN8@=s&J7w{L9ffJqHRi+llv5{{3Gz6#(|LYai&L}$ zX}J2_Embrsd2}`U@`P=;_`Ip`g7NW)988j_ft2sP2car*fz zgp-p_%!P^^7u#gVHS{6Pu5^LhCY zUZxO>E4pAS3}X<%p69?G@;T{|yse-#yJJm{G%coDelX4D+O?VxFcp@4Zka44=PZt^ zzS342ah&m(3gviJdT~2##(YSqWN^@g#QA;RBXBgX>RYxPrYiYURrX@Knt_L4^xbe- zw2V1?>NX`BVW7#o67P)`u?_*LM%wCvfv()7Y}C_7lbT0wL?~WViuc{+j$0O@mn+YE zA3<2d9L$Co=n6w09BZ>?JlZ}|2>`|EQki2t&6e%s9lh5T-VfnsQO=ZB8Qk6_MvDMp z38F}Zgn;iJyBB)~+}>!5V0Kf_s#Y!w!EU=tytRh^9Big|z{)P@p+|x!$=dkD^P8QU zhppSUpqfZ@#8Y)$Y?ev(2#}9vl*-yizt8F{{gX^v`@){5VR~d}DC6vnmmOzd`j9oi z`}r%URa{38GFY**8;0(&BB zu+oU~6MBZff+b>nk8ZqF6U+XL)qV%NwasOS{7Vo+@Xi>z``N?sFeg;X`s|mx4nD1C zU9s(-@4aVwvHh%2^n?!;yvJ*?5Jf3f)~C{W(32hb^@+F9h^0oCf;zytCM8LdX^4jhw%Hc#{TY|na44}|!N8F?r(Dh)}t$52MppywO)KQf~XO?8Ud|Cz%I zeJu)2+h9tZgxe!B7OGM_rFbc(;nZB9t(z3W6lrM5e}`Jx&Lt^}J0CzF!#n=`+d-%3 zoVTQ2eT?tqXr+XiB{Lx+%K_8i2VKXlu9iKrr!ZcX@vJ-ozHKvy z)bWV}yO<(jmI7Z?eG z+_H%SzTH{Y(YJ>Da6OOSX^G++VTfV11tZp$ED4X4*MDW@i_DT8uE`LWGG24veSXkz z!hxR7tPi`?(uSHQ>2|xMo&Gq>2)``evcvqoc9#lZ6?Tu-1iNxog5u2=n-7e$MP5=I zyubk6M7BG(^JYY7KI)Uo1(ov^V(NLID9To zizt3Ml{ht|%kx5Dj{M7W;n9KZBbUaB)Qj1Z`T7&`)IK1dAwCI}6mf-5oawM|%8UEz zJfF=$#p2VL9PYIcPyQ5lA-$m-(`PxO)Ym2tdSv?_tq3q1O-EZ9-< zUf%u^!Waa0kns>ol7OX8R8D=k8osAa7l-AMp*;Sf-y9SzN$!D`y+xkPo;xNnB*me6 z1R>Giqo}r831-(8X9MxmX%k=+s#d4-sOpV+#EH!zY$B^}3Ycx@6uED{`1;WuhYlIr z7onPdU5g$REW!(-e_TVNRHcabii{~5MCPP&+kgxo*d92*a!WoaD?sIuWFGlAx@Nr8 z-J@J~Ak*3tzoK-jYTWg-h~{p#{4u#@MKk|z&2PXmWE!uNUE|}Vhjh^KyXGk?W9cHZ zJ$CU3MAaTX3KzuelDai5x`myFW@tIZ6W8`J4M*F>j*vnfh+aa?M7t~HRHy=yx&58*0-~4FZED;!L3J|PvijOP{wWo?u>gODo?uZh08zFb zUWq+hm5KWyhCe*pC2Cnyw`cQ7&3dMZb4-(HEuC}DS-nT22YI8%NXfQYp=3~}1#M=z z{_~MhN5L6Olupu(Pg|etjfu$cR}#1_n!WkAsvOg-j(uJ~TC<$S4C~~06@qdg{z_hl zkocG&(-{#lUNv^kbHeZjB89u`cJbqfnM@6RR zJ(&8AOdzASaHwuefTvWa_oTb(^EwVv1gmXy6Wz&CKH~5rO~%Gb9v<(C;;_^mc+Yvk z*^8Wtccasq{53A1PakT^UR0f^-6bomA1SHg7AG31FboC_tuQ@fEeya6Wo=^R@uEgY zU#|4A!9WWq!fpN3LU&@^k!#W6yMSJ}7$e~;-4d=E9+xN2f-PAb86|@l5V9oV_%NT) z#JOP#LCn`XHMUJmg&p}gb zoQY^KfkaZ3n0s-z)fO=_`W!_V#3_)J-S%mj^;dGYq_8SSe~v}sc6~ey^d>JhfS9gc0vZdL+^u|bk9szV>A1;7$OfsRY+9{RrwD;*3F`~Cs5;aD9K|>P0v{4b*CVKRN zl~e^~rBO_tA24`q*5CBhwT>mPH@~uB4?nKlcY~Of)JH|XrlWgohZd;~vlDst-n+lJ z>|s~3hRlaRWD}v>h{#Z66UwEC4m}1ThbcDPTk#6G0RcFKt9%8J^d_cHFzk^7{0}~( zR@?b>A$KAscxxYgd4YbGmuF;Jwde|ZDR;cum|P1t zYpT?^OlqRWz^_>%Zm&Cw6Y!c5!I(vGyeN5>*00hm677yijs0U zrTG-*oFV1zq_*(%Q)!F#Z~lVAP3Vp>!Kt#1Zey{EoGel0X3@96#uqE0j-FU8sH)^- z32M6euh3)VAw0uyT@pYjnkB{$ zTV_Q0Pwo7-4}Ht3d9}F)+TEK^$-txvv)#wkbiPPRLGbde-;Td5yltYgPU)?V8jFmntMB?o`*Itpikk>Le{${qJ0Z_VWSszyVWIU zMUzx=;Qf=yNV#$T3Wl7y6%zZW_4$MR8?CpLB~Ce6OY;(k6MgdBVw7Tq*tfVfvO(l; zlLgv*$Q~0JemTRx3Q$1Ms`A>(R~sNS+9icvxf!8x-EURpzY4{F6>)#md~I#4SX`|v z{gkw9S||wIdH036+*wDhXgeq#SaoCf$LRDuLB3F((xiAfesRVgtOMa%_OoTNZMf6* zSwXFvECg4YWf-|<1pcm?A9$6@-MzZkcrzmG(L>@Rm|2=9EIU+l0=er16P6g*?EPQx zz#KFlFVvTbqs}ZkGFPg+O*~364!C7~5+DmzJqO4hTE!_BxZJ(I1SuAJ^h(oarVX%gZn=BtXu(VKkxEfG**yU9>x_3?=DEAKiCny2i>$=kEhXEn zV<9Wp9wEmoBrT9(WtY0wRJ-@sb5Xi_p2o30^*Qm%Zma-1a}=2~Gq80PBrl4Xz^C40 zxlhVTQ^h$T)rRAWN$q{h6zq4&ac@!Jyf6SgVHw-XMJ|7io)ooZMD&%ntzf6AKQT9#w)b!@ur~JK_)$)RfyIIQqo8z? z9tAr4A5WIo_g_Dk)-`pCg3Nb>e^!_LVUG#e5p}$daNX=CwaJxPBJ}kS?a9?Zzgv7) zpZxKBAE+Ms`b*{J`d~MyQLaokemDI?A#pYGP3n{@bA0GN{AJ|d6cg7ca+6x+3Q6%M zq(8MQSF^cEy>f-&cN4~+T9zw}o75~<7}Ymn{1Hd~%ZLB2Zn?tPfnxkzI)7=HPq2jlHBjIOt{5s@KCE+W`*HH1?-ynZ05MM{TsmO4J z_6oXZ{)TpA$>BQYP5JgK%u&q$z`T}yzm9rSX7LJD1iC-|Hq+npjMw3A%FA8BDdPSU z++SI`>sU8svaYZ!pz^(&r}j%;>pI{~Ii4%P45;+(Z-77C-)|Y8>yS6y#jhY`iT(*v z;kTRlb&{ihp#HzxcO5KUM=H0X~ibmN6l IegD`00h|3ncmMzZ literal 0 HcmV?d00001 diff --git a/jar/VER-0.1/needcoke-ioc-VER-0.1.jar b/jar/VER-0.1/needcoke-ioc-VER-0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..81f3241d8dab470e9074d5945c94840399c9e10d GIT binary patch literal 17009 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 A?*IS* literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index fe4513d..7dba268 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ groupId needcoke-ioc - 1.0-SNAPSHOT + VER-0.1 8 -- Gitee From 45425a512a323d726b526a3a1107956bb3f58e16 Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Fri, 10 Sep 2021 00:15:14 +0800 Subject: [PATCH 5/8] project init --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7b829d9..008bb2b 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,9 @@ ContextHolder.java 容器 ContextRun.java 容器启动器,只有调用其中的run方法,容器才能够正常启动 #### 安装教程 +下载jar包: https://gitee.com/needcoke/needcoke-ioc/tree/master/jar/VER-0.1 -1. xxxx -2. xxxx -3. xxxx - +下载完成后在IDEA - ProjectSettings - modules中点击+号导入java选择jar包就可以 #### 使用说明 ```java /* 扫描bean的必备注解 */ -- Gitee From fb7bf53b473f653cc0b1dda70f9fd06fa7a8be0d Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Tue, 14 Sep 2021 18:01:43 +0800 Subject: [PATCH 6/8] project init --- README.md | 2 + pom.xml | 23 +- .../java/org/needcoke/ioc/ContextRun.java | 175 +- .../org/needcoke/ioc/annotation/Value.java | 15 + .../org/needcoke/ioc/core/ConfigBean.java | 23 + .../org/needcoke/ioc/core/ContextBean.java | 5 + .../org/needcoke/ioc/core/ContextHolder.java | 40 + .../needcoke/ioc/handle/BeanInjectHandle.java | 35 + .../needcoke/ioc/handle/BeanScanHandle.java | 119 ++ .../ioc/handle/ComponentInitHandle.java | 20 + .../ioc/handle/ConfigInjectHandle.java | 64 + .../ioc/handle/ConfigReaderHandle.java | 78 + .../java/org/needcoke/ioc/handle/Handle.java | 15 + .../ioc/util/ConfigValueTypeEnum.java | 58 + src/main/java/org/needcoke/ioc/util/Pair.java | 20 + .../org/needcoke/ioc/util/ReflectUtil.java | 76 + .../java/org/needcoke/ioc/util/YamlUtil.java | 1521 +++++++++++++++++ 17 files changed, 2190 insertions(+), 99 deletions(-) create mode 100644 src/main/java/org/needcoke/ioc/annotation/Value.java create mode 100644 src/main/java/org/needcoke/ioc/core/ConfigBean.java create mode 100644 src/main/java/org/needcoke/ioc/handle/BeanInjectHandle.java create mode 100644 src/main/java/org/needcoke/ioc/handle/BeanScanHandle.java create mode 100644 src/main/java/org/needcoke/ioc/handle/ComponentInitHandle.java create mode 100644 src/main/java/org/needcoke/ioc/handle/ConfigInjectHandle.java create mode 100644 src/main/java/org/needcoke/ioc/handle/ConfigReaderHandle.java create mode 100644 src/main/java/org/needcoke/ioc/handle/Handle.java create mode 100644 src/main/java/org/needcoke/ioc/util/ConfigValueTypeEnum.java create mode 100644 src/main/java/org/needcoke/ioc/util/Pair.java create mode 100644 src/main/java/org/needcoke/ioc/util/ReflectUtil.java create mode 100644 src/main/java/org/needcoke/ioc/util/YamlUtil.java diff --git a/README.md b/README.md index 008bb2b..405b79a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # needcoke-ioc +#### VER-0.2 + #### 介绍 实现类似于Spring的容器管理工具,因为项目中不需要使用很重的Spring,而右希望能够拥有它的IOC,DI diff --git a/pom.xml b/pom.xml index 7dba268..24712b0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ groupId needcoke-ioc - VER-0.1 + VER-0.2 8 @@ -19,5 +19,26 @@ hutool-all 5.5.1 + + org.yaml + snakeyaml + 1.27 + + + + com.amihaiemil.web + eo-yaml + 5.2.3 + + + org.projectlombok + lombok + 1.18.20 + + + com.alibaba + fastjson + 1.2.57 + \ No newline at end of file diff --git a/src/main/java/org/needcoke/ioc/ContextRun.java b/src/main/java/org/needcoke/ioc/ContextRun.java index 0092342..22f763f 100644 --- a/src/main/java/org/needcoke/ioc/ContextRun.java +++ b/src/main/java/org/needcoke/ioc/ContextRun.java @@ -2,129 +2,108 @@ package org.needcoke.ioc; import cn.hutool.core.util.StrUtil; import org.needcoke.ioc.annotation.*; -import org.needcoke.ioc.core.ContextBean; import org.needcoke.ioc.core.ContextHolder; -import org.needcoke.ioc.util.MethodUtil; +import org.needcoke.ioc.handle.*; import org.needcoke.ioc.util.PackageUtil; - +import org.needcoke.ioc.util.ReflectUtil; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.List; -import java.util.Set; +import java.lang.reflect.Type; +import java.util.*; public class ContextRun { - private static Object init(Class clz) { - try { - return clz.newInstance(); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } - public static ContextHolder run(Class t, String[] args) { ContextHolder holder = new ContextHolder(); BeanScanner annotation = t.getAnnotation(BeanScanner.class); if (null != annotation) { - String[] paths = annotation.path(); - boolean recursion = annotation.recursion();/* 是否递归加载类 */ - for (String path : paths) { - /* 获取包路径下的所有类 */ - Set> clzFromPkg = PackageUtil.getClzFromPkg(path); - for (Class aClass : clzFromPkg) { - if (aClass.getAnnotation(Component.class) != null) { - Object component = init(aClass); - Method[] methods = aClass.getDeclaredMethods(); - for (Method method : methods) { - method.setAccessible(true); - Bean methodBean = method.getAnnotation(Bean.class); - if (null != methodBean) { - if (StrUtil.isNotBlank(methodBean.name()) && holder.nameBeanContext.containsKey(methodBean.name())) { - throw new RuntimeException("already has a bean name #" + methodBean.name() + "#"); - } else { - /* 通过反射执行方法,并获取反射值,注解有声明名称则用注解,注解无则使用方法名,存入context */ - ContextBean beanFromMethod = MethodUtil.runMethod(method, component, holder); - if (StrUtil.isBlank(methodBean.name())) { - beanFromMethod.name = method.getName(); - } - holder.putBean(beanFromMethod); - } - } - } - ContextBean componentBean = new ContextBean(); - String componentName = aClass.getAnnotation(Component.class).name(); - if (StrUtil.isBlank(componentName)) { - componentName = aClass.getSimpleName(); + Set> clzFromPkg = new HashSet<>(); + String[] packages = annotation.path(); + for (String packName : packages) { + clzFromPkg = PackageUtil.getClzFromPkg(packName); + } + /* 配置文件读取 */ + Handle configReaderHandle = null; + /* 配置文件注入 */ + Handle configInjectHandle = null; + /* bean扫描 */ + Handle beanScanHandle = null; + /* bean注入 */ + Handle beanInjectHandle = null; + /* component调用初始化方法 */ + Handle componentInitHandle = null; + for (Class aClass : clzFromPkg) { + for (Type genericInterface : aClass.getGenericInterfaces()) { + if (genericInterface.getTypeName().equals(Handle.class.getTypeName())) { + Handle handle = ReflectUtil.getBean(aClass); + if (handle instanceof ConfigReaderHandle) { + configReaderHandle = handle; + } else if (handle instanceof ConfigInjectHandle) { + configInjectHandle = handle; + } else if (handle instanceof BeanScanHandle) { + beanScanHandle = handle; + } else if (handle instanceof BeanInjectHandle) { + beanInjectHandle = handle; + } else if (handle instanceof ComponentInitHandle) { + componentInitHandle = handle; } - componentBean.name = componentName; - componentBean.clz = aClass; - componentBean.value = component; - holder.putComponent(componentBean); } } + Map configMap = new HashMap<>(); + Object component = null; + Component annotationComponent = aClass.getAnnotation(Component.class); + if (null != annotationComponent) { + component = ReflectUtil.getBean(aClass); + holder.componentBuffer.put(aClass, component); + holder.componentNameBuffer.put(aClass, StrUtil.isNotBlank(annotationComponent.name()) ? + annotationComponent.name() : aClass.getSimpleName()); + Field[] fields = aClass.getDeclaredFields(); + for (Field field : fields) { + /* 需要被注入bean的缓冲区 */ + if (null != field.getAnnotation(Autowired.class)) { + if(!holder.needInjectBeanField.containsKey(aClass)){ + holder.needInjectBeanField.put(aClass,new ArrayList<>()); + } + holder.needInjectBeanField.get(aClass).add(field); + } - } + /* 需要被注入配置的缓冲区 */ + if (null != field.getAnnotation(Value.class)) { + Value annotationValue = field.getAnnotation(Value.class); + configMap.put(field, annotationValue.name()); + } + } + holder.needInjectConfigField.put(aClass, configMap); - Set> componentClasses = holder.componentMap.keySet(); - for (Class componentClass : componentClasses) { - Object component = holder.componentMap.get(componentClass); - Field[] fields = componentClass.getDeclaredFields(); - for (Field field : fields) { - field.setAccessible(true); - Autowired autowiredAnnotation = field.getAnnotation(Autowired.class); - if (null != autowiredAnnotation) { - ContextBean beanAutowired = null; - /* Autowired的name属性不为空的话,就通过名称注入 */ - if (StrUtil.isNotBlank(autowiredAnnotation.name())) { - beanAutowired = holder.nameBeanContext.get(autowiredAnnotation.name()); - if (null != beanAutowired && beanAutowired.clz.equals(field.getType())) { - try { - field.set(component, beanAutowired.value); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } else { - /* 名称为空,则通过类型来匹配 */ - if (holder.typeBeanContext.containsKey(field.getType())) { - beanAutowired = holder.typeBeanContext.get(field.getType()).get(0); - try { - field.set(component, beanAutowired.value); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } + Method[] methods = aClass.getDeclaredMethods(); + for (Method method : methods) { + /* 将自定义bean注入到缓冲区 */ + if (null != method.getAnnotation(Bean.class)) { + Bean annotationMethod = method.getAnnotation(Bean.class); + if(!holder.beanMethodBuffer.containsKey(aClass)){ + holder.beanMethodBuffer.put(aClass,new HashMap<>()); } + holder.beanMethodBuffer.get(aClass).put(StrUtil.isNotBlank(annotationMethod.name()) ? + annotationMethod.name() : method.getName(), + method); + } + /* 需要调用的初始化方法缓冲区 */ + if (null != method.getAnnotation(Init.class)) { + holder.initMethodBuffer.put(aClass, method); } } + } } - for (Class componentClass : componentClasses) { - Method[] methods = componentClass.getDeclaredMethods(); - for (Method method : methods) { - method.setAccessible(true); - Init initBean = method.getAnnotation(Init.class); - /* 执行初始化方法 */ - if (null != initBean) { - try { - Object o = holder.componentMap.get(componentClass); - method.invoke(o); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - } - } + configReaderHandle.handle(holder); + configInjectHandle.handle(holder); + beanScanHandle.handle(holder); + beanInjectHandle.handle(holder); + componentInitHandle.handle(holder); } return holder; - } } diff --git a/src/main/java/org/needcoke/ioc/annotation/Value.java b/src/main/java/org/needcoke/ioc/annotation/Value.java new file mode 100644 index 0000000..6eaa70e --- /dev/null +++ b/src/main/java/org/needcoke/ioc/annotation/Value.java @@ -0,0 +1,15 @@ +package org.needcoke.ioc.annotation; + +import java.lang.annotation.*; + +@Target({ElementType.FIELD,ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Value { + + @Note("指定配置文件的映射路径") + String target() default ""; + + @Note("指定映射配置文件地址") + String name() default ""; +} diff --git a/src/main/java/org/needcoke/ioc/core/ConfigBean.java b/src/main/java/org/needcoke/ioc/core/ConfigBean.java new file mode 100644 index 0000000..bd94791 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/core/ConfigBean.java @@ -0,0 +1,23 @@ +package org.needcoke.ioc.core; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** +* 配置文件的Bean +**/ +@Data +@Accessors(chain = true) +public class ConfigBean { + + private String beanKey; + + private Object beanValue; + + /** + * 该配置源自哪个配置文件 + **/ + private String readByFile; + + +} diff --git a/src/main/java/org/needcoke/ioc/core/ContextBean.java b/src/main/java/org/needcoke/ioc/core/ContextBean.java index 3b70fcd..22e580f 100644 --- a/src/main/java/org/needcoke/ioc/core/ContextBean.java +++ b/src/main/java/org/needcoke/ioc/core/ContextBean.java @@ -1,8 +1,13 @@ package org.needcoke.ioc.core; +import lombok.Data; +import lombok.experimental.Accessors; + /** * ioc容器的封装bean **/ +@Data +@Accessors(chain = true) public class ContextBean { public Class clz; diff --git a/src/main/java/org/needcoke/ioc/core/ContextHolder.java b/src/main/java/org/needcoke/ioc/core/ContextHolder.java index 189d1e6..133a452 100644 --- a/src/main/java/org/needcoke/ioc/core/ContextHolder.java +++ b/src/main/java/org/needcoke/ioc/core/ContextHolder.java @@ -1,12 +1,22 @@ package org.needcoke.ioc.core; import cn.hutool.core.util.StrUtil; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; +/** + * ioc容器 + * + * @author Gilgamesh + * @since VER-0.1 + **/ public class ContextHolder { /** @@ -33,6 +43,36 @@ public class ContextHolder { **/ public Map, List> typeBeanContext = new ConcurrentHashMap<>(); + /** + * properties配置文件缓冲区 + **/ + public Map propertiesBuffer = new HashMap<>(); + + /** + * component缓冲区 + **/ + public Map,Object> componentBuffer = new HashMap<>(); + + public Map,String> componentNameBuffer = new HashMap<>(); + + /** + * 函数缓存@Bean + **/ + public Map, Map> beanMethodBuffer = new HashMap<>(); + + /** + * 函数缓冲@init + **/ + public Map,Method> initMethodBuffer = new HashMap<>(); + + /** + * Autowired 标记的字段 + **/ + public Map, List> needInjectBeanField = new HashMap<>(); + + public Map,Map> needInjectConfigField = new HashMap<>(); + + public List getBean(Class type) { List ts = new ArrayList<>(); List contextBeans = typeBeanContext.get(type); diff --git a/src/main/java/org/needcoke/ioc/handle/BeanInjectHandle.java b/src/main/java/org/needcoke/ioc/handle/BeanInjectHandle.java new file mode 100644 index 0000000..c1b7243 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/BeanInjectHandle.java @@ -0,0 +1,35 @@ +package org.needcoke.ioc.handle; + +import cn.hutool.core.collection.CollUtil; +import org.needcoke.ioc.annotation.Autowired; +import org.needcoke.ioc.core.ContextBean; +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.ReflectUtil; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Set; + +public class BeanInjectHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... params) { + Set> clzSet = holder.needInjectBeanField.keySet(); + for (Class aClass : clzSet) { + Object component = holder.componentMap.get(aClass); + List fields = holder.needInjectBeanField.get(aClass); + for (Field field : fields) { + Autowired annotationAutoWired = field.getAnnotation(Autowired.class); + String name = annotationAutoWired.name(); + ContextBean contextBean = holder.nameBeanContext.get(name); + if (null != contextBean && contextBean.getClz().equals(field.getType())) { + ReflectUtil.setField(component, field, contextBean.value); + } else { + List bean = holder.getBean(field.getType()); + if (CollUtil.isNotEmpty(bean)) { + ReflectUtil.setField(component, field, bean.get(0)); + } + } + } + } + return 1; + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/BeanScanHandle.java b/src/main/java/org/needcoke/ioc/handle/BeanScanHandle.java new file mode 100644 index 0000000..8cf038a --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/BeanScanHandle.java @@ -0,0 +1,119 @@ +package org.needcoke.ioc.handle; + +import cn.hutool.core.collection.CollUtil; +import org.needcoke.ioc.annotation.Value; +import org.needcoke.ioc.core.ConfigBean; +import org.needcoke.ioc.core.ContextBean; +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.ReflectUtil; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; + +/** + * Bean创建处理器 + * + * @author Gilgamesh + * @since VER-0.2 + **/ +public class BeanScanHandle implements Handle { + + private ContextHolder holder; + + @Override + public int handle(ContextHolder holder, Object... params) { + this.holder = holder; + initBeanMethodWithoutParameter(); + initComponent(); + initBeanMethodHasParameter(); + return 1; + } + + public void initBeanMethodWithoutParameter() { + Set> clzSet = holder.beanMethodBuffer.keySet(); + Map, Map> waitBuffer = new HashMap<>(); + for (Class aClass : clzSet) { + Map stringMethodMap = holder.beanMethodBuffer.get(aClass); + for (String beanName : stringMethodMap.keySet()) { + Method method = stringMethodMap.get(beanName); + Object component = holder.componentBuffer.get(aClass); + Parameter[] parameters = method.getParameters(); + /* 如果是有参构造函数直接启动,如果有参就先加入等待区 */ + if (parameters.length > 0) { + if (!waitBuffer.containsKey(aClass)) { + waitBuffer.put(aClass, new HashMap<>()); + } + waitBuffer.get(aClass).put(beanName, method); + } else { + Object o = ReflectUtil.invokeMethod(component, method, null); + ContextBean contextBean = new ContextBean() + .setName(beanName) + .setClz(method.getReturnType()) + .setValue(o); + holder.putBean(contextBean); + } + } + } + holder.beanMethodBuffer = waitBuffer; + } + + public void initBeanMethodHasParameter() { + Set> clzSet = holder.beanMethodBuffer.keySet(); + Map, Map> waitBuffer = new HashMap<>(); + for (Class aClass : clzSet) { + Map stringMethodMap = holder.beanMethodBuffer.get(aClass); + for (String beanName : stringMethodMap.keySet()) { + Method method = stringMethodMap.get(beanName); + + Object component = holder.componentBuffer.get(aClass); + Parameter[] parameters = method.getParameters(); + Object[] params = new Object[parameters.length]; + int i = 0; + /* 执行有参函数 */ + for (Parameter parameter : parameters) { + /* 参数来自配置文件 */ + if (null != parameter.getAnnotation(Value.class)) { + Value annotationValue = parameter.getAnnotation(Value.class); + ConfigBean configBean = holder.propertiesBuffer.get(annotationValue.name()); + if (null != configBean) { + params[i] = configBean.getBeanValue(); + } + } else { + List bean = holder.getBean(parameter.getType()); + if (CollUtil.isNotEmpty(bean)) { + params[i] = bean.get(0); + } else { + params[i] = null; + } + } + i++; + } + Object o = ReflectUtil.invokeMethod(component, method, params); + ContextBean contextBean = new ContextBean() + .setName(beanName) + .setClz(method.getReturnType()) + .setValue(o); + holder.putBean(contextBean); + holder.beanMethodBuffer = null; + } + } + } + + public void initComponent() { + for (Class aClass : holder.componentBuffer.keySet()) { + String componentName = holder.componentNameBuffer.get(aClass); + componentName = componentName.substring(0, 1).toLowerCase() + componentName.substring(1, componentName.length()); + + ContextBean bean = new ContextBean() + .setName(componentName) + .setValue(holder.componentBuffer.get(aClass)) + .setClz(aClass); + holder.putComponent(bean); +// /* 清空缓冲区 */ +// holder.componentNameBuffer = null; +// holder.componentBuffer = null; + } + } + +} diff --git a/src/main/java/org/needcoke/ioc/handle/ComponentInitHandle.java b/src/main/java/org/needcoke/ioc/handle/ComponentInitHandle.java new file mode 100644 index 0000000..86b146b --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/ComponentInitHandle.java @@ -0,0 +1,20 @@ +package org.needcoke.ioc.handle; + +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.ReflectUtil; +import java.lang.reflect.Method; +import java.util.Set; + +public class ComponentInitHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... params) { + Set> classes = holder.initMethodBuffer.keySet(); + for (Class aClass : classes) { + Method method = holder.initMethodBuffer.get(aClass); + ReflectUtil.invokeMethod(holder.componentMap.get(aClass), + method,null); + + } + return 1; + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/ConfigInjectHandle.java b/src/main/java/org/needcoke/ioc/handle/ConfigInjectHandle.java new file mode 100644 index 0000000..5b275ab --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/ConfigInjectHandle.java @@ -0,0 +1,64 @@ +package org.needcoke.ioc.handle; + +import cn.hutool.core.util.StrUtil; +import org.needcoke.ioc.core.ConfigBean; +import org.needcoke.ioc.core.ContextHolder; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Set; + +/** + * 该处理类负责将配置文件的内容注入到@Component中 + * + * @author Gilgamesh + * @since VER-0.2 + **/ +public class ConfigInjectHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... params) { + Map, Map> needInjectConfigField = holder.needInjectConfigField; + Set> keySet = needInjectConfigField.keySet(); + for (Class aClass : keySet) { + Object component = holder.componentBuffer.get(aClass); + Map fieldMap = needInjectConfigField.get(aClass); + for (Field field : fieldMap.keySet()) { + ConfigBean configBean = holder.propertiesBuffer.get(fieldMap.get(field)); + if (null == configBean) { + continue; + } + try { + field.setAccessible(true); + String value = configBean.getBeanValue().toString(); + if (field.getType().getTypeName().equals(Integer.class.getTypeName())) { + field.set(component, Integer.parseInt(value)); + } else if (field.getType().getTypeName().equals(Double.class.getTypeName())) { + field.set(component, Double.parseDouble(value)); + } else if (field.getType().getTypeName().equals(Long.class.getTypeName())) { + field.set(component, Long.parseLong(value)); + } else if (field.getType().getTypeName().equals(Boolean.class.getTypeName())) { + field.set(component, Boolean.parseBoolean(value)); + } else if (field.getType().getTypeName().equals(Short.class.getTypeName())) { + field.set(component, Short.parseShort(value)); + } else if (field.getType().getTypeName().equals(Byte.class.getTypeName())) { + field.set(component, Byte.parseByte(value)); + } else if (field.getType().getTypeName().equals(Character.class.getTypeName())) { + field.set(component, value); + } else if (field.getType().getTypeName().equals(String.class.getTypeName())) { + field.set(component, value); + } else if (field.getType().getTypeName().equals("int")) { + field.set(component, Integer.parseInt(value)); + } else if (field.getType().getTypeName().equals("double")) { + field.set(component, Double.parseDouble(value)); + } else if (field.getType().getTypeName().equals("boolean")) { + field.set(component, Boolean.parseBoolean(value)); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + } + return 1; + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/ConfigReaderHandle.java b/src/main/java/org/needcoke/ioc/handle/ConfigReaderHandle.java new file mode 100644 index 0000000..1211441 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/ConfigReaderHandle.java @@ -0,0 +1,78 @@ +package org.needcoke.ioc.handle; + +import org.needcoke.ioc.core.ConfigBean; +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.YamlUtil; +import org.yaml.snakeyaml.Yaml; +import java.io.*; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +/** + * 配置文件阅读处理 + * + * @author Gilgamesh + * @since VER-0.2 + **/ +public class ConfigReaderHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... o) { + readYaml(holder, "/application.yaml"); + readYaml(holder, "/application.yml"); + readProperties(holder, "/application.properties"); + return 0; + } + + private void readYaml(ContextHolder holder, String fileName) { + InputStream inputStream = this.getClass().getResourceAsStream(fileName); + if (inputStream == null) { + return; + } + Yaml yaml = new Yaml(); + Map map = null; + try { + map = yaml.loadAs(new InputStreamReader(inputStream, "UTF-8"), Map.class); + } catch (Exception e) { + e.printStackTrace(); + } + String yamlContent = YamlUtil.mapToYaml(map); + String propertiesContent = YamlUtil.yamlToProperties(yamlContent); + String[] lines = propertiesContent.split("\n"); + for (String line : lines) { + try { + String[] ss = line.split("="); + if (ss.length == 2) { + ConfigBean configBean = new ConfigBean() + .setBeanKey(ss[0]) + .setBeanValue(ss[1]) + .setReadByFile(fileName); + holder.propertiesBuffer.put(ss[0], configBean); + } + } catch (Throwable throwable) { + throw new RuntimeException("配置文件解析失败:application.yml"); + } + } + } + + private void readProperties(ContextHolder holder, String fileName) { + InputStream inputStream = this.getClass().getResourceAsStream(fileName); + if (null == inputStream) { + return; + } + Properties properties = new Properties(); + try { + properties.load(new InputStreamReader(inputStream,"UTF-8")); + Set set = properties.stringPropertyNames(); + for (String s : set) { + ConfigBean configBean = new ConfigBean() + .setBeanKey(s) + .setBeanValue(properties.getProperty(s)) + .setReadByFile(fileName); + holder.propertiesBuffer.put(s, configBean); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/Handle.java b/src/main/java/org/needcoke/ioc/handle/Handle.java new file mode 100644 index 0000000..37c7d6e --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/Handle.java @@ -0,0 +1,15 @@ +package org.needcoke.ioc.handle; + +import org.needcoke.ioc.core.ContextHolder; + +/** +* 处理接口 +**/ +public interface Handle { + /** + * 处理函数接口 + * @param holder ioc容器 + * @param params 参数 + **/ + int handle(ContextHolder holder,Object... params); +} diff --git a/src/main/java/org/needcoke/ioc/util/ConfigValueTypeEnum.java b/src/main/java/org/needcoke/ioc/util/ConfigValueTypeEnum.java new file mode 100644 index 0000000..50cc5eb --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/ConfigValueTypeEnum.java @@ -0,0 +1,58 @@ +package org.needcoke.ioc.util; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author shizi + * @since 2020/9/14 9:58 上午 + */ +@AllArgsConstructor +public enum ConfigValueTypeEnum { + + /** + * yml配置 + */ + YAML("yml配置"), + /** + * properties配置 + */ + PROPERTIES("properties配置"), + /** + * 打包中 + */ + JSON("json配置"), + /** + * string字符配置 + */ + STRING("string字符配置"); + + @Getter + private final String desc; + + private static final Map indexEnumMap; + private static final Map nameEnumMap; + + static { + indexEnumMap = Arrays.stream(ConfigValueTypeEnum.values()).collect(Collectors.toMap(ConfigValueTypeEnum::ordinal, e -> e)); + nameEnumMap = Arrays.stream(ConfigValueTypeEnum.values()).collect(Collectors.toMap(ConfigValueTypeEnum::name, e -> e)); + } + + public static ConfigValueTypeEnum parse(Integer index) { + if (!indexEnumMap.containsKey(index)) { + throw new RuntimeException("不支持下标: " + index); + } + return indexEnumMap.get(index); + } + + public static ConfigValueTypeEnum parse(String name) { + if (!nameEnumMap.containsKey(name)) { + throw new RuntimeException("不支持name: " + name); + } + return nameEnumMap.get(name); + } +} diff --git a/src/main/java/org/needcoke/ioc/util/Pair.java b/src/main/java/org/needcoke/ioc/util/Pair.java new file mode 100644 index 0000000..f81ef31 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/Pair.java @@ -0,0 +1,20 @@ +package org.needcoke.ioc.util; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author shizi + * @since 2020/3/30 7:35 PM + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public final class Pair implements Serializable { + + private K key; + private V value; +} diff --git a/src/main/java/org/needcoke/ioc/util/ReflectUtil.java b/src/main/java/org/needcoke/ioc/util/ReflectUtil.java new file mode 100644 index 0000000..7eafb58 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/ReflectUtil.java @@ -0,0 +1,76 @@ +package org.needcoke.ioc.util; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +public class ReflectUtil { + + public static T getBean(Class clz) { + try { + return (T) clz.newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 调用函数 + **/ + public static Object invokeMethod(Object component, Method method, Object... params) { + Parameter[] parameters = method.getParameters(); + if (null != params && params.length > 0) { + for (int i = 0; i < params.length; i++) { + if (params[i] != null) { + String value = (String) params[i]; + if (parameters[i].getType().getTypeName().equals(Integer.class.getTypeName())) { + params[i] = Integer.parseInt(value); + } else if (parameters[i].getType().getTypeName().equals(Double.class.getTypeName())) { + params[i] = Double.parseDouble(value); + } else if (parameters[i].getType().getTypeName().equals(Long.class.getTypeName())) { + params[i] = Long.parseLong(value); + } else if (parameters[i].getType().getTypeName().equals(Boolean.class.getTypeName())) { + params[i] = Boolean.parseBoolean(value); + } else if (parameters[i].getType().getTypeName().equals(Short.class.getTypeName())) { + params[i] = Short.parseShort(value); + } else if (parameters[i].getType().getTypeName().equals(Byte.class.getTypeName())) { + params[i] = Byte.parseByte(value); + } else if (parameters[i].getType().getTypeName().equals(Character.class.getTypeName())) { + params[i] = value; + } else if (parameters[i].getType().getTypeName().equals(String.class.getTypeName())) { + params[i] = value; + } else if (parameters[i].getType().getTypeName().equals("int")) { + params[i] = Integer.parseInt(value); + } else if (parameters[i].getType().getTypeName().equals("double")) { + params[i] = Double.parseDouble(value); + } else if (parameters[i].getType().getTypeName().equals("boolean")) { + params[i] = Boolean.parseBoolean(value); + } + } + } + } + try { + if (params == null) + return method.invoke(component); + return method.invoke(component, params); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static void setField(Object component, Field field, Object o) { + try { + field.setAccessible(true); + field.set(component, o); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/needcoke/ioc/util/YamlUtil.java b/src/main/java/org/needcoke/ioc/util/YamlUtil.java new file mode 100644 index 0000000..0616d9f --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/YamlUtil.java @@ -0,0 +1,1521 @@ +package org.needcoke.ioc.util; + +import com.alibaba.fastjson.JSON; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import lombok.Data; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.yaml.snakeyaml.DumperOptions; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * yaml与其他各种格式之间互转 + *

+ *

    + *
  • 1.yaml <---> properties
  • + *
  • 2.yaml <---> json
  • + *
  • 3.yaml <---> map
  • + *
  • 4.yaml <---> list
  • + *
  • 5.yaml <---> kvList
  • + *
+ * + * @author shizi + * @since 2020/9/14 3:17 下午 + */ +@SuppressWarnings("all") +@UtilityClass +public class YamlUtil { + + /** + * 换行符 + */ + private final String NEW_LINE = "\n"; + /** + * 注释标识 + */ + private final String REMARK_PRE = "# "; + /** + * 缩进空格 + */ + private final String INDENT_BLANKS = " "; + /** + * 分号连接符 + */ + private final String SIGN_SEMICOLON = ":"; + /** + * 等号连接符 + */ + private final String SIGN_EQUAL = "="; + /** + * 点 + */ + private final String DOT = "."; + /** + * 数组缩进 + */ + private final String ARRAY_BLANKS = "- "; + /** + * yaml的value换行符 + */ + private final String yaml_NEW_LINE_DOM = "|\n"; + private final Pattern rangePattern = Pattern.compile("^(.*)\\[(\\d*)\\]$"); + /** + * 格式转换的缓存 + */ + private final Map typeContentMap = new ConcurrentHashMap<>(); + + /** + * 判断类型是否是yaml类型 + * + * @param content yaml 内容 + * @return true:是yaml,false:不是 + */ + public boolean isYaml(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isYaml", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return false; + } + + try { + checkYaml(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是yaml类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkYaml(String content) { + if (isEmpty(content)) { + throw new RuntimeException("yaml内容为空"); + } + if (!content.contains(":") && !content.contains("-")) { + throw new RuntimeException("yaml内容不包含\":\"也不包含\"-\""); + } + if (content.contains("---\n")) { + throw new RuntimeException("yaml内容不支持 --- 的导入"); + } + + try { + yamlToProperties(content); + } catch (RuntimeException e) { + throw new RuntimeException("内容不是严格yaml类型;" + e.getMessage()); + } + } + + /** + * 判断是否是properties类型 + * + * @param content 内容 + * @return true:是properties类型,false:不是properties类型 + */ + public boolean isProperties(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isProperties", content, () -> { + if (!content.contains("=")) { + return false; + } + + try { + checkProperties(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是properties类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkProperties(String content) { + if (isEmpty(content)) { + throw new RuntimeException("properties内容为空"); + } + if (!content.contains("=")) { + throw new RuntimeException("properties内容不包含\"=\""); + } + try { + checkYaml(propertiesToYaml(content)); + } catch (Throwable e) { + throw new RuntimeException("内容不是严格properties类型;" + e.getMessage()); + } + } + + /** + * 判断是否是json类型 + * + * @param content 内容 + * @return true:是json类型,false:不是json类型 + */ + public boolean isJson(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isJson", content, () -> { + if (!content.startsWith("{") && !content.startsWith("[")) { + return false; + } + + try { + checkJson(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是json对象类型 + * + * @param content 内容 + * @return true:是json对象类型,false:不是json对象类型 + */ + public boolean isJsonObject(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isJsonObject", content, () -> { + try { + checkJsonObject(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是json数组类型 + * + * @param content 内容 + * @return true:是json数组类型,false:不是json数组类型 + */ + public boolean isJsonArray(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isJsonArray", content, () -> { + try { + checkJsonArray(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是json类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkJson(String content) { + if (isEmpty(content)) { + throw new RuntimeException("json内容不是严格json类型,因为内容为空"); + } + + // 先核查是否是object + if (content.startsWith("{")) { + try { + JSON.parseObject(content); + } catch (Throwable e) { + throw new RuntimeException("json内容不是严格json对象类型;" + e.getMessage()); + } + } else if (content.startsWith("[")) { + try { + JSON.parseArray(content); + } catch (Throwable e) { + throw new RuntimeException("json内容不是严格json数组类型;" + e.getMessage()); + } + } else { + throw new RuntimeException("json内容不是json类型,因为没有\"{\"也没有\"[\"开头"); + } + } + + /** + * 判断是否是json对象类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkJsonObject(String content) { + if (isEmpty(content)) { + throw new RuntimeException("json内容不是严格json对象类型,因为内容为空"); + } + try { + JSON.parseObject(content); + } catch (Throwable e) { + throw new RuntimeException("内容不是严格json对象类型;" + e.getMessage()); + } + } + + /** + * 判断是否是json数组类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkJsonArray(String content) { + if (isEmpty(content)) { + throw new RuntimeException("json内容不是严格json数组类型,因为内容为空"); + } + try { + JSON.parseArray(content); + } catch (Throwable e) { + throw new RuntimeException("内容不是严格json对象类型;" + e.getMessage()); + } + } + + /** + * yaml格式转properties + * + * @param key key + * @param content 对应的yaml内容 + * @return properties内容 + * @throws RuntimeException 转换异常 + */ + public String yamlToProperties(String key, String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToProperties", key, content, () -> { + try { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + Map dataMap = new HashMap<>(); + dataMap.put(key, yamlToList(content)); + return yamlToProperties(mapToYaml(dataMap)); + } + + return propertiesAppendPrefixKey(key, yamlToProperties(content)); + } catch (Throwable e) { + throw new RuntimeException("yaml 转换到 properties异常:" + e.getMessage()); + } + }); + } + + /** + * yaml格式转换到properties + * + * @param content yaml内容 + * @return properties内容 + * @throws RuntimeException 转换异常 + */ + public String yamlToProperties(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToProperties", content, () -> { + try { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + throw new RuntimeException("不支持数组的yaml转properties"); + } + + List propertiesList = new ArrayList<>(); + Map remarkMap = new LinkedHashMap<>(); + Map valueMap = yamlToMap(content); + + // 读取yaml的注释 + yamlToRemarkMap(remarkMap, Yaml.createYamlInput(content).readYamlMapping(), ""); + formatYamlToProperties(propertiesList, remarkMap, valueMap, ""); + return propertiesList.stream().filter(e -> null != e && !"".equals(e)).reduce((a, b) -> a + NEW_LINE + b).orElse(""); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + } + + public Properties yamlToPropertiesValue(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToProperties", content, () -> { + try { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + return null; + } + + Properties properties = new Properties(); + Map remarkMap = new LinkedHashMap<>(); + Map valueMap = yamlToMap(content); + + // 读取yaml的注释 + yamlToRemarkMap(remarkMap, Yaml.createYamlInput(content).readYamlMapping(), ""); + formatYamlToPropertiesValue(properties, remarkMap, valueMap, ""); + return properties; + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + } + + /** + * properties 转换到 yaml + * + * @param properties properties内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToYaml(Properties properties) { + if (properties.isEmpty()) { + return null; + } + return cacheCompute("propertiesToYaml", properties, () -> { + StringBuilder stringBuilder = new StringBuilder(); + properties.forEach((k, v) -> stringBuilder.append(k).append("=").append(v).append(NEW_LINE)); + return propertiesToYaml(stringBuilder.toString()); + }); + } + + /** + * properties类型转换到json + * + * @param properties properties 内容 + * @return json内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToJson(Properties properties) { + return yamlToJson(propertiesToYaml(properties)); + } + + /** + * properties内容转换到json + * + * @param content properties内容 + * @return json内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToJson(String content) { + return yamlToJson(propertiesToYaml(content)); + } + + /** + * properties内容转换到map + * + * @param content properties内容 + * @return map内容 + * @throws RuntimeException 转换异常 + */ + public Map propertiesToMap(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("propertiesToMap", content, () -> { + if (!content.contains("=")) { + return null; + } + + Map resultMap = new HashMap<>(); + List propertiesLineWordList = getPropertiesItemLineList(content); + + for (String line : propertiesLineWordList) { + String lineTem = line.trim(); + if (!"".equals(lineTem)) { + int index = lineTem.indexOf("="); + if (index > -1) { + String key = lineTem.substring(0, index); + String value = lineTem.substring(index + 1); + + // 对于yaml中换行的添加|用于保留换行 + if (value.contains("\n")) { + value = yaml_NEW_LINE_DOM + value; + } + resultMap.put(key, value); + } + } + } + return resultMap; + }); + } + + /** + * properties 转换到 yaml + * + * @param content properties内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToYaml(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("propertiesToYaml", content, () -> { + if (!content.contains("=")) { + return null; + } + try { + List yamlLineList = new ArrayList<>(); + List propertiesLineWordList = getPropertiesItemLineList(content); + + List YamlNodes = new ArrayList<>(); + StringBuilder projectRemark = new StringBuilder(); + StringBuilder remark = new StringBuilder(); + for (String line : propertiesLineWordList) { + String lineTem = line.trim(); + if (!"".equals(lineTem)) { + if (lineTem.startsWith("#")) { + if (0 != remark.length()) { + projectRemark.append(remark.toString()); + remark.delete(0, remark.length()); + } + remark.append(lineTem); + continue; + } + int index = lineTem.indexOf("="); + if (index > -1) { + String key = lineTem.substring(0, index); + String value = lineTem.substring(index + 1); + // 对于yaml中换行的添加|用于保留换行 + if (value.contains("\n")) { + value = yaml_NEW_LINE_DOM + value; + } + + final List lineWordList = new ArrayList<>(Arrays.asList(key.split("\\."))); + wordToNode(lineWordList, YamlNodes, null, false, null, appendSpaceForArrayValue(value), projectRemark.toString(), remark.toString()); + } + // 删除本地保留 + remark.delete(0, remark.length()); + projectRemark.delete(0, projectRemark.length()); + } + } + formatPropertiesToYaml(yamlLineList, YamlNodes, false, ""); + return yamlLineList.stream().reduce((a, b) -> a + "\n" + b).orElse("") + "\n"; + } catch (Throwable e) { + throw new RuntimeException("properties 转换到 yaml异常:" + e.getMessage()); + } + }); + } + + /** + * yaml 转换到 对象 + * + * @param content properties内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public Object yamlToObject(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToObject", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + return yamlToList(content); + } + + return yamlToMap(content); + }); + } + + /** + * yaml 转 map + * + * 由于eo-yaml对map转换支持会默认将一些key添加字符,这里就用snakeyaml工具做 + * @return map 对象 + * @throws RuntimeException 转换异常 + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public Map yamlToMap(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToMap", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + try { + Map resultMap = new HashMap<>(); + org.yaml.snakeyaml.Yaml yml = new org.yaml.snakeyaml.Yaml(); + Map result = yml.loadAs(content, Map.class); + Set> entrySet = result.entrySet(); + for (Map.Entry entry : entrySet) { + resultMap.put(String.valueOf(entry.getKey()), entry.getValue()); + } + return resultMap; + } catch (Throwable e) { + throw new RuntimeException("yml 转换到 map 异常:" + e.getMessage()); + } + }); + } + + /** + * yaml 转 map + * + * @param content yaml内容 + * @return 集合内容 + * @throws RuntimeException 转换异常 + */ + public List yamlToList(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToList", content, () -> { + if (!content.trim().startsWith("-")) { + return null; + } + + try { + org.yaml.snakeyaml.Yaml yml = new org.yaml.snakeyaml.Yaml(); + return yml.load(content); + } catch (Throwable e) { + throw new RuntimeException("yml 转换到 map 异常:" + e.getMessage()); + } + }); + } + + /** + * map 转 yaml + * + * @param contentMap map内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String mapToYaml(Map contentMap) { + if (isEmpty(contentMap)) { + return null; + } + return cacheCompute("yamlToList", contentMap, () -> { + try { + org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml(); + String originalYaml = yaml.dumpAsMap(contentMap); + // 由于snakeyaml对数组缩进支持不够好,这里做一层缩进 + return yamlFormatForMap(originalYaml); + } catch (Throwable e) { + throw new RuntimeException("map 转换到 yml 异常:" + e.getMessage()); + } + }); + } + + /** + * 集合内容转yaml + * + * @param contentList 集合内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String listToYaml(List contentList) { + if (isEmpty(contentList)) { + return null; + } + return cacheCompute("listToYaml", contentList, () -> { + try { + org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml(); + return yaml.dumpAs(contentList, null, DumperOptions.FlowStyle.BLOCK); + } catch (Throwable e) { + throw new RuntimeException("map 转换到 yml 异常:" + e.getMessage()); + } + }); + } + + /** + * yaml 转 json + * + * @param content yaml内容 + * @return json内容 + * @throws RuntimeException 转换异常 + */ + public String yamlToJson(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToJson", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + try { + return JSON.toJSONString(yamlToObject(content)); + } catch (Throwable e) { + throw new RuntimeException("yaml 转换到 json 异常:" + e.getMessage()); + } + }); + } + + /** + * json 转 对象 + * + * @param content json内容 + * @return object内容 + * @throws RuntimeException 转换异常 + */ + public Object jsonToObject(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("jsonToObject", content, () -> { + if (!content.startsWith("{") && !content.startsWith("[")) { + return null; + } + + try { + if (isJsonObject(content)) { + return JSON.parseObject(content); + } else if (isJsonArray(content)) { + return JSON.parseArray(content); + } + throw new RuntimeException("content 不是json类型"); + } catch (Throwable e) { + throw new RuntimeException("json 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + /** + * json 转 yaml + * + * @param content json内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String jsonToYaml(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("jsonToYaml", content, () -> { + if (!content.startsWith("{") && !content.startsWith("[")) { + return null; + } + + try { + if (isJsonObject(content)) { + return mapToYaml(JSON.parseObject(content)); + } else if (isJsonArray(content)) { + return listToYaml(JSON.parseArray(content)); + } + throw new RuntimeException("content 不是json类型"); + } catch (Throwable e) { + throw new RuntimeException("json 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + /** + * yaml类型转换为 k-v的集合 + * + * @param content yaml内容 + * @return kv集合 + * @throws RuntimeException 转换异常 + */ + public List> yamlToKVList(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToKVList", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + try { + String propertiesContent = yamlToProperties(content); + return getPropertiesItemLineList(propertiesContent).stream().map(e -> { + int index = e.indexOf("="); + String key = e.substring(0, index); + String value = e.substring(index + 1); + return new AbstractMap.SimpleEntry<>(key, value); + }).collect(Collectors.toList()); + } catch (Throwable e) { + throw new RuntimeException("yaml 转换到 kv-list 异常:" + e.getMessage()); + } + }); + } + + /** + * k-v的集合类型转yaml + * + * @param kvStringList kv集合 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String kvListToYaml(List> kvStringList) { + if (isEmpty(kvStringList)) { + return null; + } + return cacheCompute("kvListToYaml", kvStringList, () -> { + try { + String propertiesContent = kvStringList.stream().map(e -> e.getKey() + "=" + e.getValue()).reduce((a, b) -> a + "\n" + b).orElse(""); + return propertiesToYaml(propertiesContent); + } catch (Throwable e) { + throw new RuntimeException("kv-list 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + public String kvToYaml(String key, String value, ConfigValueTypeEnum valueTypeEnum) { + if (isEmpty(key)) { + return null; + } + return cacheCompute("kvToYaml", key, value, () -> { + try { + return propertiesToYaml(kvToProperties(key, value, valueTypeEnum)); + } catch (Throwable e) { + throw new RuntimeException("kv 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + public Map kvToMap(String key, String value, ConfigValueTypeEnum valueTypeEnum) { + if (isEmpty(key)) { + return null; + } + return cacheCompute("kvToMap", key, value, () -> propertiesToMap(kvToProperties(key, value, valueTypeEnum))); + } + + public String kvToProperties(String key, String value, ConfigValueTypeEnum valueTypeEnum) { + return cacheCompute("kvToProperties", key, value, () -> kvToProperties(key, value, null, valueTypeEnum)); + } + + /** + * k-v的String类型转properties + * + *

其中key可能是a.b.c这种,而value可能是各种各样的类型,我们这里通过valueType进行区分 + * + * @param key 主键 + * @param value 待转换的值 + * @param desc 注释 + * @param valueTypeEnum 值的类型,0:yaml,1:properties,2:json,3:string + * @return 转换之后的yaml类型 + * @throws RuntimeException 转换异常 + */ + public String kvToProperties(String key, String value, String desc, ConfigValueTypeEnum valueTypeEnum) { + if (isEmpty(key)) { + return null; + } + return cacheCompute("kvToProperties", key, value, () -> { + try { + // 将value对应的值先转换为properties类型,然后对key进行拼接,最后再统一转化为yaml格式 + StringBuilder propertiesResult = new StringBuilder(); + if (null != desc && !"".equals(desc)) { + propertiesResult.append("# ").append(desc).append("\n"); + } + + String propertiesContent; + switch (valueTypeEnum) { + case YAML: + propertiesContent = yamlToProperties(key, value); + if (!isEmpty(propertiesContent)) { + propertiesResult.append(propertiesContent); + } + return propertiesResult.toString(); + case JSON: + propertiesContent = yamlToProperties(key, jsonToYaml(value)); + if (!isEmpty(propertiesContent)) { + propertiesResult.append(propertiesContent); + } + return propertiesResult.toString(); + case PROPERTIES: + propertiesContent = propertiesAppendPrefixKey(key, value); + if (!isEmpty(propertiesContent)) { + propertiesResult.append(propertiesContent); + } + return propertiesResult.toString(); + case STRING: + propertiesResult.append(key).append("=").append(appendSpaceForArrayValue(value)); + return propertiesResult.toString(); + default: + break; + } + + return propertiesResult.toString(); + } catch (Throwable e) { + throw new RuntimeException("kv 转换到 properties 异常: " + e.getMessage()); + } + }); + } + + public List getPropertiesItemLineList(String content) { + if (isEmpty(content)) { + return Collections.emptyList(); + } + return cacheCompute("getPropertiesItemLineList", content, () -> { + if (!content.contains("=")) { + return Collections.emptyList(); + } + + String[] lineList = content.split(NEW_LINE); + List itemLineList = new ArrayList<>(); + StringBuilder stringBuilder = new StringBuilder(); + for (String line : lineList) { + // 处理多行数据 + if (line.endsWith("\\")) { + stringBuilder.append(line).append("\n"); + } else { + stringBuilder.append(line); + itemLineList.add(stringBuilder.toString()); + stringBuilder.delete(0, stringBuilder.length()); + } + } + return itemLineList; + }); + } + + private String propertiesAppendPrefixKey(String key, String propertiesContent) { + return getPropertiesItemLineList(propertiesContent).stream().filter(e -> e.contains("=")).map(e -> { + int index = e.indexOf("="); + if (index > -1) { + String keyTem = e.substring(0, index).trim(); + String valueTem = e.substring(index + 1).trim(); + return key + DOT + keyTem + "=" + valueTem; + } + return null; + }).filter(Objects::nonNull).reduce((a, b) -> a + NEW_LINE + b).orElse(null); + } + + /** + * 针对有些yaml格式不严格,这里做不严格向严格的eo-yaml解析的转换 + *

+ * 对{@code + * test: + * - k1: 12 + * - k2: 22 + * } + * 这种做一层缩进,由于snake的map转yaml后有缩进问题 + */ + private String yamlFormatForMap(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlFormatForMap", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + StringBuilder stringBuilder = new StringBuilder(); + String[] items = content.split("\n"); + Integer blankSize = null; + // 判断是否在数组中 + boolean inArray = false; + for (String item : items) { + int innerBlankSize = item.substring(0, item.indexOf(item.trim())).length(); + // 数组 + if (item.trim().startsWith("- ")) { + if (inArray) { + // 多重数组,则多层嵌套 + if (innerBlankSize > blankSize) { + stringBuilder.append(INDENT_BLANKS).append(INDENT_BLANKS).append(item).append("\n"); + continue; + } + } + inArray = true; + blankSize = innerBlankSize; + } else { + // 其他的字符 + if (null != blankSize) { + if (innerBlankSize <= blankSize) { + inArray = false; + } + } + } + + if (inArray) { + stringBuilder.append(INDENT_BLANKS).append(item).append("\n"); + } else { + stringBuilder.append(item).append("\n"); + } + } + return stringBuilder.toString(); + }); + } + + /** + * 将key转换为yaml节点 + * + * @param lineWordList 待转换的key,比如{@code k1.k2.k3=123} + * @param nodeList 已经保存的节点数据 + * @param lastNodeArrayFlag 上一个节点是否数组类型 + * @param index 索引下标 + * @param value 解析的值 + * @param remark 当前value对应的注释 + */ + private void wordToNode(List lineWordList, List nodeList, YamlNode parentNode, Boolean lastNodeArrayFlag, Integer index, String value, String projectRemark, + String remark) { + if (lineWordList.isEmpty()) { + if (lastNodeArrayFlag) { + YamlNode node = new YamlNode(); + node.setValue(value); + node.setRemark(remark); + nodeList.add(node); + } + } else { + String nodeName = lineWordList.get(0); + + Pair nameAndIndex = peelArray(nodeName); + nodeName = nameAndIndex.getKey(); + Integer nextIndex = nameAndIndex.getValue(); + + YamlNode node = new YamlNode(); + node.setName(nodeName); + node.setProjectRemark(projectRemark); + node.setParent(parentNode); + node.setRemark(remark); + node.setLastNodeIndex(index); + lineWordList.remove(0); + + //如果节点下面的子节点数量为0,则为终端节点,也就是赋值节点 + if (lineWordList.size() == 0) { + if (null == nextIndex) { + node.setRemark(remark); + node.setValue(value); + } + } + + // nextIndex 不空,表示当前节点为数组,则之后的数据为他的节点数据 + if (null != nextIndex) { + node.setArrayFlag(true); + boolean hasEqualsName = false; + //遍历查询节点是否存在 + for (YamlNode YamlNode : nodeList) { + //如果节点名称已存在,则递归添加剩下的数据节点 + if (nodeName.equals(YamlNode.getName()) && YamlNode.getArrayFlag()) { + Integer yamlNodeIndex = YamlNode.getLastNodeIndex(); + if (null == yamlNodeIndex || index.equals(yamlNodeIndex)) { + hasEqualsName = true; + wordToNode(lineWordList, YamlNode.getValueList(), node.getParent(), true, nextIndex, appendSpaceForArrayValue(value), null, remark); + } + } + } + //如果遍历结果为节点名称不存在,则递归添加剩下的数据节点,并把新节点添加到上级yamlTree的子节点中 + if (!hasEqualsName) { + wordToNode(lineWordList, node.getValueList(), node.getParent(), true, nextIndex, appendSpaceForArrayValue(value), null, remark); + nodeList.add(node); + } + } else { + boolean hasEqualsName = false; + //遍历查询节点是否存在 + for (YamlNode YamlNode : nodeList) { + if (!lastNodeArrayFlag) { + //如果节点名称已存在,则递归添加剩下的数据节点 + if (nodeName.equals(YamlNode.getName())) { + hasEqualsName = true; + wordToNode(lineWordList, YamlNode.getChildren(), YamlNode, false, nextIndex, appendSpaceForArrayValue(value), null, remark); + } + } else { + //如果节点名称已存在,则递归添加剩下的数据节点 + if (nodeName.equals(YamlNode.getName())) { + Integer yamlNodeIndex = YamlNode.getLastNodeIndex(); + if (null == yamlNodeIndex || index.equals(yamlNodeIndex)) { + hasEqualsName = true; + wordToNode(lineWordList, YamlNode.getChildren(), YamlNode, true, nextIndex, appendSpaceForArrayValue(value), null, remark); + } + } + } + } + //如果遍历结果为节点名称不存在,则递归添加剩下的数据节点,并把新节点添加到上级yamlTree的子节点中 + if (!hasEqualsName) { + wordToNode(lineWordList, node.getChildren(), node, false, nextIndex, appendSpaceForArrayValue(value), null, remark); + nodeList.add(node); + } + } + } + } + + /** + * 获取yaml中的注释 + * + * @param remarkMap 解析后填充的注释map:key为a.b.c.d,value为对应的注释,去除掉前缀#后的数据 + * @param mapping yaml解析后数据 + * @param prefix 前缀 + */ + private void yamlToRemarkMap(Map remarkMap, YamlMapping mapping, String prefix) { + if (null == mapping) { + return; + } + for (com.amihaiemil.eoyaml.YamlNode node : mapping.keys()) { + String nodeName = node.asScalar().value(); + String remark = mapping.value(node).comment().value(); + + if (null != remark && !"".equals(remark)) { + remarkMap.put(wrapKey(prefix, nodeName), remark); + } + + yamlToRemarkMap(remarkMap, mapping.yamlMapping(node), wrapKey(prefix, nodeName)); + } + } + + private String wrapKey(String prefix, String value) { + if (isEmpty(prefix)) { + return prefix + "." + value; + } + return value; + } + + /** + * 解析节点名字,为数组则返回数组名和节点下标 + *

+ * name.test[0] 将test和0进行返回 + * + * @param nodeName 界面的名字 + * @return 如果是数组,则将数组名和解析后的下标返回 + */ + private Pair peelArray(String nodeName) { + String name = nodeName; + Integer index = null; + Matcher matcher = rangePattern.matcher(nodeName); + if (matcher.find()) { + String indexStr = matcher.group(2); + if (null != indexStr) { + index = Integer.valueOf(indexStr); + } + name = matcher.group(1); + } + + return new Pair<>(name, index); + } + + /** + * 将yaml对应的这种value进行添加前缀空格,其中value为key1对应的value + * {@code + * test: + * key1: | + * value1 + * value2 + * value3 + * } + * 对应的值 + * {@code + * | + * value1 + * value2 + * value3 + * } + * + * @param value 待转换的值比如{@code + * test: + * key1: | + * value1 + * value2 + * value3 + * } + * @return 添加前缀空格之后的处理 + * {@code + * | + * value1 + * value2 + * value3 + * } + */ + private String appendSpaceForArrayValue(String value) { + if (!value.startsWith(yaml_NEW_LINE_DOM)) { + return value; + } + String valueTem = value.substring(yaml_NEW_LINE_DOM.length()); + return yaml_NEW_LINE_DOM + Arrays.stream(valueTem.split("\\n")).map(e -> { + String tem = e; + if (e.endsWith("\\")) { + tem = e.substring(0, e.length() - 1); + } + return INDENT_BLANKS + tem; + }).reduce((a, b) -> a + "\n" + b).orElse(valueTem); + } + + private void formatPropertiesToYaml(List yamlLineList, List YamlNodes, Boolean lastNodeArrayFlag, String blanks) { + Integer beforeNodeIndex = null; + String equalSign; + for (YamlNode YamlNode : YamlNodes) { + String value = YamlNode.getValue(); + String remark = YamlNode.getRemark(); + + equalSign = SIGN_SEMICOLON; + if (null == value || "".equals(value)) { + value = ""; + } else { + equalSign = SIGN_SEMICOLON + " "; + } + YamlNode.resortValue(); + + String name = YamlNode.getName(); + if (lastNodeArrayFlag) { + if (null == name) { + yamlLineList.add(blanks + ARRAY_BLANKS + stringValueWrap(value)); + } else { + if (null != beforeNodeIndex && beforeNodeIndex.equals(YamlNode.getLastNodeIndex())) { + yamlLineList.add(blanks + INDENT_BLANKS + name + equalSign + stringValueWrap(value)); + } else { + yamlLineList.add(blanks + ARRAY_BLANKS + name + equalSign + stringValueWrap(value)); + } + } + beforeNodeIndex = YamlNode.getLastNodeIndex(); + } else { + // 父节点为空,表示,当前为顶层 + if (null == YamlNode.getParent()) { + String remarkTem = getRemarkProject(YamlNode.getProjectRemark()); + if (!"".equals(remarkTem)) { + yamlLineList.add(blanks + getRemarkProject(YamlNode.getProjectRemark())); + } + } + + // 自己节点为数组,则添加对应的注释 + if (YamlNode.getArrayFlag()) { + if (null != remark && !"".equals(remark)) { + yamlLineList.add(blanks + remark); + } + } + yamlLineList.add(blanks + name + equalSign + stringValueWrap(value, remark)); + } + + if (YamlNode.getArrayFlag()) { + if (lastNodeArrayFlag) { + formatPropertiesToYaml(yamlLineList, YamlNode.getValueList(), true, INDENT_BLANKS + INDENT_BLANKS + blanks); + } else { + formatPropertiesToYaml(yamlLineList, YamlNode.getValueList(), true, INDENT_BLANKS + blanks); + } + } else { + if (lastNodeArrayFlag) { + formatPropertiesToYaml(yamlLineList, YamlNode.getChildren(), false, INDENT_BLANKS + INDENT_BLANKS + blanks); + } else { + formatPropertiesToYaml(yamlLineList, YamlNode.getChildren(), false, INDENT_BLANKS + blanks); + } + } + } + } + + @SuppressWarnings("rawtypes") + private void formatYamlToPropertiesValue(Properties properties, Map remarkMap, Object object, String prefix) { + if (null == object) { + return; + } + if (object instanceof Map) { + Map map = (Map) object; + Set set = map.keySet(); + for (Object key : set) { + Object value = map.get(key); + if (null == value) { + value = ""; + } + if (value instanceof Map) { + formatYamlToPropertiesValue(properties, remarkMap, value, prefixWithDOT(prefix) + key); + } else if (value instanceof Collection) { + Collection collection = (Collection) value; + if (!collection.isEmpty()) { + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToPropertiesValue(properties, remarkMap, valueObject, prefixWithDOT(prefix) + key + "[" + index + "]"); + index = index + 1; + } + } + } else if (value instanceof String) { + String valueStr = (String) value; + valueStr = valueStr.trim(); + valueStr = valueStr.replace("\n", "\\\n"); + properties.put(prefixWithDOT(prefix) + key, valueStr); + } else { + properties.put(prefixWithDOT(prefix) + key, value); + } + } + } else if (object instanceof Collection) { + Collection collection = (Collection) object; + if (!collection.isEmpty()) { + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToPropertiesValue(properties, remarkMap, valueObject, prefix + "[" + index + "]"); + index = index + 1; + } + } + } else if (object.getClass().isArray()) { + Object[] array = (Object[]) object; + for (int index = 0; index < array.length; index++) { + formatYamlToPropertiesValue(properties, remarkMap, array[index], prefix + "[" + index + "]"); + } + } else if (object instanceof String) { + String valueObject = (String) object; + valueObject = valueObject.replace("\n", "\\\n"); + properties.put(prefix, valueObject); + } else { + properties.put(prefix, object); + } + } + + @SuppressWarnings("rawtypes") + private void formatYamlToProperties(List propertiesLineList, Map remarkMap, Object object, String prefix) { + if (null == object) { + return; + } + if (object instanceof Map) { + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + Map map = (Map) object; + Set set = map.keySet(); + for (Object key : set) { + Object value = map.get(key); + if (null == value) { + value = ""; + } + if (value instanceof Map) { + formatYamlToProperties(propertiesLineList, remarkMap, value, prefixWithDOT(prefix) + key); + } else if (value instanceof Collection) { + Collection collection = (Collection) value; + if (!collection.isEmpty()) { + // 填充注释 + if (remarkMap.containsKey(prefixWithDOT(prefix) + key)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefixWithDOT(prefix) + key)); + } + + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToProperties(propertiesLineList, remarkMap, valueObject, prefixWithDOT(prefix) + key + "[" + index + "]"); + index = index + 1; + } + } + } else if (value instanceof String) { + String valueStr = (String) value; + valueStr = valueStr.trim(); + valueStr = valueStr.replace("\n", "\\\n"); + // 填充注释 + if (remarkMap.containsKey(prefixWithDOT(prefix) + key)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefixWithDOT(prefix) + key)); + } + + propertiesLineList.add(prefixWithDOT(prefix) + key + SIGN_EQUAL + valueStr); + } else { + // 填充注释 + if (remarkMap.containsKey(prefixWithDOT(prefix) + key)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefixWithDOT(prefix) + key)); + } + + propertiesLineList.add(prefixWithDOT(prefix) + key + SIGN_EQUAL + value); + } + } + } else if (object instanceof Collection) { + Collection collection = (Collection) object; + if (!collection.isEmpty()) { + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToProperties(propertiesLineList, remarkMap, valueObject, prefix + "[" + index + "]"); + index = index + 1; + } + } + } else if (object.getClass().isArray()) { + Object[] array = (Object[]) object; + for (int index = 0; index < array.length; index++) { + formatYamlToProperties(propertiesLineList, remarkMap, array[index], prefix + "[" + index + "]"); + } + } else if (object instanceof String) { + String valueObject = (String) object; + valueObject = valueObject.replace("\n", "\\\n"); + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + propertiesLineList.add(prefix + SIGN_EQUAL + valueObject); + } else { + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + propertiesLineList.add(prefix + SIGN_EQUAL + object); + } + } + + private String prefixWithDOT(String prefix) { + if ("".equals(prefix)) { + return prefix; + } + return prefix + DOT; + } + + private String stringValueWrap(String value) { + if (isEmpty(value)) { + return ""; + } + // 对数组的数据进行特殊处理 + if (value.startsWith("[") && value.endsWith("]")) { + return "'" + value + "'"; + } + return value; + } + + private String stringValueWrap(String value, String remark) { + if (isEmpty(value)) { + return ""; + } + // 对数组的数据进行特殊处理 + if (value.startsWith("[") && value.endsWith("]")) { + return "'" + value + "'" + getRemark(remark); + } + + return value + getRemark(remark); + } + + private String getRemark(String remark) { + if (null != remark && !"".endsWith(remark) && remark.startsWith("#")) { + return " # " + remark.substring(1).trim(); + } else { + return ""; + } + } + + private String getRemarkProject(String remark) { + if (null != remark && !"".endsWith(remark) && remark.startsWith("#")) { + return " # " + remark.substring(1).trim(); + } else { + return ""; + } + } + + /** + * 数据存在则返回,不存在则计算后添加到缓存中 + */ + @SuppressWarnings("unchecked") + private T cacheCompute(String funName, Object key, Supplier biFunction) { + String cacheKey = buildCacheKey(funName, key); + if (typeContentMap.containsKey(cacheKey)) { + return (T) typeContentMap.get(cacheKey); + } + T result = biFunction.get(); + if (null != result) { + typeContentMap.put(cacheKey, result); + } + return result; + } + + @SuppressWarnings("unchecked") + private T cacheCompute(String funName, Object key, Object value, Supplier biFunction) { + String cacheKey = buildCacheKey(funName, key, value); + if (typeContentMap.containsKey(cacheKey)) { + return (T) typeContentMap.get(cacheKey); + } + T result = biFunction.get(); + if (null != result) { + typeContentMap.put(cacheKey, result); + } + return result; + } + + private String buildCacheKey(String funName, Object... parameters) { + StringBuilder stringBuilder = new StringBuilder(funName); + for (Object parameter : parameters) { + if (null != parameter) { + stringBuilder.append(":").append(parameter.toString()); + } + } + return stringBuilder.toString(); + } + + private boolean isEmpty(String string) { + return null == string || "".endsWith(string); + } + + private boolean isEmpty(Collection collection) { + return null == collection || collection.isEmpty(); + } + + private boolean isEmpty(Map map) { + return null == map || map.isEmpty(); + } + + @Data + class YamlNode { + + private YamlNode parent; + /** + * 只有parent为null时候,该值才可能有值 + */ + private String projectRemark; + /** + * name + */ + private String name; + /** + * value + */ + private String value; + /** + * 注释 + */ + private String remark; + + /** + * 子节点 + */ + private List children = new ArrayList<>(); + + /** + * 数组标示: + */ + private Boolean arrayFlag = false; + /** + * 存储的数组中的前一个节点的下标 + */ + private Integer lastNodeIndex; + /** + * 只有数组标示为true,下面的value才有值 + */ + private List valueList = new ArrayList<>(); + + /** + * 将其中的value按照index下标顺序进行重拍 + */ + public void resortValue() { + if (!arrayFlag || valueList.isEmpty()) { + return; + } + + // 升序 + valueList.sort((a, b) -> { + if (null == a.getLastNodeIndex() || null == b.getLastNodeIndex()) { + return 0; + } + + return a.getLastNodeIndex() - b.getLastNodeIndex(); + }); + + // 是数组的节点也循环下 + valueList.forEach(YamlNode::resortValue); + } + } +} -- Gitee From 8342900c340563c396f7fdee6e18d8037574d86e Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Tue, 14 Sep 2021 18:04:10 +0800 Subject: [PATCH 7/8] project init --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 405b79a..85ff658 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # needcoke-ioc #### VER-0.2 +1、支持配置文件,.yaml,.yml,.properties后缀的配置文件 +2、并通过@Value注入 +3、优化了ioc容器的加载流程 #### 介绍 实现类似于Spring的容器管理工具,因为项目中不需要使用很重的Spring,而右希望能够拥有它的IOC,DI @@ -18,6 +21,8 @@ @Init 被标注了@Component的方法在执行完构造函数后会调用@Init标注的方法初始化bean +@Value 配置文件注入 + ContextBean.java 容器对bean的封装 ContextHolder.java 容器 -- Gitee From 9e07f4f049a68d765f908a29725c2277c3c8d60c Mon Sep 17 00:00:00 2001 From: Gilgamesh <2410818122@qq.com> Date: Tue, 14 Sep 2021 18:09:23 +0800 Subject: [PATCH 8/8] project init --- jar/VER-0.2/needcoke-ioc-VER-0.2-sources.jar | Bin 0 -> 27738 bytes jar/VER-0.2/needcoke-ioc-VER-0.2.jar | Bin 0 -> 53315 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 jar/VER-0.2/needcoke-ioc-VER-0.2-sources.jar create mode 100644 jar/VER-0.2/needcoke-ioc-VER-0.2.jar diff --git a/jar/VER-0.2/needcoke-ioc-VER-0.2-sources.jar b/jar/VER-0.2/needcoke-ioc-VER-0.2-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..11ea1c6110e4f7aa433a0656abedff6801368414 GIT binary patch literal 27738 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 U-7CPr(1N~7K?1QPgYQ5650!ezj{pDw literal 0 HcmV?d00001 diff --git a/jar/VER-0.2/needcoke-ioc-VER-0.2.jar b/jar/VER-0.2/needcoke-ioc-VER-0.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..507860a004784b38601034d980247b2371841f55 GIT binary patch literal 53315 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 literal 0 HcmV?d00001 -- Gitee