干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结
目录
我(梦在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近发表的一篇文章《.net core与spring boot编写控制台程序应有的优雅姿势》看到都上48小时阅读排行榜(当然之前发表的文章也有哦!),说明关注.net core及spring boot的人很多,也是目前的主流方向,于是我便决定系统性的总结一下c# 与java 、asp.net core 与 spring boot mvc,让更多的人了解它们,消除之前可能存在的对.net或java的误解。
本文目的是通过全面简述c# 与java 在基础语法以及asp.net core 与 spring boot mvc的在框架规范、部署、运行的异曲同工的实现方式,让大家更多的了解c#与java,本文不会刻意说哪门语言好,我认为这是没有意义的,更多的是了解每种语言的特点、优点以及不同语言的共性,掌握编程内功(如:面向对象、di、aop、设计模式、算法),这样才能让自己更能适应社会及未来的变化。
本文主要以示例代码为主,辅以简单文字说明,不会细讲每个语法点,只会体现不同的实现方式而矣,全文无废话,全是干货,慢慢欣赏吧。
(注:本文内容是使用markdown编辑器进行编辑完成!)
c# vs java 基础语法类比篇:
一、匿名类
c#(直接new{},在{}中直接定义只读公开属性或委托方法,无需预先定义任何接口或类)
#region 1.匿名类 var helloword = new { codeby = "c#匿名类", output = new action<string, string>((name, codeby) => { system.console.writeline($"welcome:{name},hello word! by {codeby}"); }) }; helloword.output("梦在旅途", helloword.codeby); #endregion
java(需要先定义接口或类,然后 new 接口或类的构造函数{},{}内实现接口方法或重写父类接口)
//1.匿名类 ihelloword helloword=new ihelloword() { @override public void output(string name) { system.out.printf("welcome:%s,hello word! by %s\n",name,getcodeby()); } @override public string getcodeby() { return "java匿名类"; } }; helloword.output("梦在旅途"); public interface ihelloword { void output(string name); string getcodeby(); }
二、类型初始化
c#(ilist类型(dictionary、list)直接在new 类型{},在{}内直接使用{key,value}或{value}方式添加集合元素,其实是隐式调用了add方法)
#region 2.类型初始化 dictionary<string, string> map = new dictionary<string, string> { { "key1","value1" },//(隐式自动调用add方法) { "key2", "value2" }, { "key3", "value3" } }; foreach (var item in map) { system.console.writeline($"key:{item.key},value:{item.value}"); } list<string> list = new list<string> { "list-item1",//(隐式自动调用add方法) "list-item2", "list-item3" }; foreach (string item in list) { system.console.writeline(item); } string[] strarr = { "arr1", "arr2", "arr3" }; foreach (string item in strarr) { system.console.writeline(item); } person person = new person { name = "梦在旅途", age = 23, sex = "男" }; string json = jsonconvert.serializeobject(person); system.console.writeline("person json:" + json); #endregion
java(new集合类型{},并在{}内再次使用{},即{{赋值 }},在双大括号内进行赋值操作,省略类名,这个特点有点类似vb及vb.net的with语句,大家有兴趣可以了解一下,数组的初始化与c#相同,都可以直接在定义数组的时候在{}中给定元素)
//2.类型初始化 map<string,string> map=new hashmap(){ { put("key1","value1"); put("key2","value2"); put("key3","value3"); } }; for (map.entry<string, string> item:map.entryset()) { system.out.printf("key:%1$s,value:%2$s\n",item.getkey(),item.getvalue()); } list<string> list=new arraylist(){ { add("list-item1"); add("list-item2"); add("list-item3"); } }; for (string item :list) { system.out.printf("%s\n",item); } string[] strarr={"arr1","arr2","arr3"}; for (string item :strarr) { system.out.printf("%s\n",item); } person person=new person(){ { setname("zwj"); setage(32); setsex("男"); } }; objectmapper jsonmapper=new objectmapper(); string json= jsonmapper.writevalueasstring(person); system.out.println("person json:" + json);
三、委托(方法引用)
c#(委托定义使用delegate关键字,后面就跟方法答名定义【不含方法体】,可委托普通方法,静态方法,有很多的现成的预定义委托类型,如:action<t0...t16>,func<t0...t16,tout>各有16个重载)
#region 3.委托 delegate void hellodelegate(string name);//定义委托类型(重点是方法签名) //常规普通自定义委托类型及委托相应的方法 helloword hellowordobj = new helloword(); hellodelegate hellodelegate = hellowordobj.output; //委托实例方法 hellodelegate.invoke("梦在旅途");// or hellodelegate("梦在旅途"); hellodelegate hellodelegate2 = helloword.outputforstatic; //委托类的静态方法 hellodelegate2.invoke("zuowenjun"); // or hellodelegate2("zuowenjun"); //使用通用的已封装好的委托类型(如:func、action)并实例化 func<int, int, int> multiplyfunc = new func<int, int, int>(delegate (int a, int b) { return a * b; }); int x = 12, y = 25; int multiplyresult = multiplyfunc.invoke(x, y); //or multiplyfunc(x,y); system.console.writeline($"{x}乘以{y}等于:{multiplyresult}"); action<string> helloaction = new action<string>(delegate (string name) { system.console.writeline($"hello,{name},how are you!"); system.console.writeline("learning keep moving!"); }); helloaction.invoke("www.zuowenjun.cn"); #endregion
java(定义委托需要先定义委托类型【即:函数式接口,规则:接口+@functionalinterface+一个方法定义】,然后就可以普通方法,静态方法,有很多的现成的预定义委托类型【即:函数式接口】,如:bifunction,consumer等)
//3.委托 helloword hellowordobj = new helloword(); helloworddelegate helloworddelegate = hellowordobj::output; helloworddelegate.invoke("梦在旅途"); helloworddelegate helloworddelegate2 = helloword::outputforstatic; helloworddelegate2.invoke("zuowenjun"); //使用已封装好的委托方法(java这边称:函数式接口,有很多详见:https://www.runoob.com/java/java8-functional-interfaces.html) bifunction<integer, integer, integer> multiplyfunc = new bifunction<integer, integer, integer>() { @override public integer apply(integer i, integer i2) { return i * i2; } }; int x = 12, y = 25; int multiplyresult = multiplyfunc.apply(x, y); system.out.printf("%d乘以%d等于:%d%n", x, y, multiplyresult); consumer<string> helloaction=new consumer<string>() { @override public void accept(string s) { system.out.printf("hello,%s,how are you!%n",s); system.out.printf("learning keep moving!%n"); } }; helloaction.accept("www.zuowenjun.cn"); @functionalinterface public interface helloworddelegate { void invoke(string name); } public class helloword implements ihelloword { @override public void output(string name) { system.out.printf("welcome:%s,hello word! by %s\n",name,getcodeby()); } public static void outputforstatic(string name){ system.out.printf("welcome:%s,hello word! by java static\n",name); } @override public string getcodeby() { return "java"; } }
四、lambda表达式
c#(使用(入参)=>{方法处理体},与要传入或要实例化的委托方法签名相同即可)
#region 4.lambda func<int, int, int> multiplyfunc2 = new func<int, int, int>((a, b) => a * b); int x2 = 12, y2 = 25; int multiplyresult2 = multiplyfunc2.invoke(x2, y2); //or multiplyfunc(x,y); system.console.writeline($"{x2}乘以{y2}等于:{multiplyresult2}"); action<string> helloaction2 = new action<string>(name => { system.console.writeline($"hello,{name},how are you!"); system.console.writeline("learning keep moving!"); }); helloaction2.invoke("www.zuowenjun.cn"); int[] intarr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; intarr = intarr.where(i => i >= 5).toarray(); foreach (int i in intarr) { system.console.writeline($"int-{i}"); } string msg = "测试外部变量被lambda引用"; action testmsgaction = () => { msg += "--改变内容"; system.console.writeline("lambda方法体中的值:" + msg); }; testmsgaction(); system.console.writeline("原始值:" + msg); #endregion
java(使用(入参)->{方法处理体},与要传入或要实例化的方法签名相同,且传入或实例化的类型必需是函数式接口【可以理解为自定义的委托类型】,注意与c#不同,lambda方法体内不能引用外部非final的变量,与c# lambda有本质不同)
//4.lambda bifunction<integer, integer, integer> multiplyfunc = (i1, i2) -> i1 * i2; int x = 12, y = 25; int multiplyresult = multiplyfunc.apply(x, y); system.out.printf("%d乘以%d等于:%d%n", x, y, multiplyresult); consumer<string> helloaction= s -> { system.out.printf("hello,%s,how are you!%n",s); system.out.printf("learning keep moving!%n"); }; helloaction.accept("www.zuowenjun.cn"); int[] intarr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; intarr= arrays.stream(intarr).filter(value -> value>=5).toarray(); for (int n : intarr) { system.out.printf("int-%d%n",n); }
五、泛型
c#(真泛型,不同的泛型类型参数视为不同的类型,有泛型接口,泛型类,泛型方法,泛型委托,泛型约束:in表示逆变【泛型参数父类型转子类型,属于消费者,一般用于入参】,out 表示协变【泛型参数子类型转父类型】,只有委托、接口才支持可变性)
#region 5.泛型 //常用泛型集合类型 list<int> intlist = new list<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; list<long> longlist = new list<long> { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l }; dictionary<string, string> dic = new dictionary<string, string> { { "k1","v1"},{ "k2","v2"},{ "k3","v3"} }; //泛型方法 var demo = new demogenericclass(); //demo.displayresult("学习永无止境"); 错误,因为约束是值类型 demo.displayresult(consolecolor.darkgreen); list<yellowperson> yellowpersonlist = new list<yellowperson> { new yellowperson(){ name="zzz",age=11,sex="g"}, new yellowperson(){ name="xxx",age=22,sex="b"} }; //协变(泛型参数子类转父类) //public interface ienumerable<out t> ienumerable<yellowperson> yellowpersons = yellowpersonlist; ienumerable<person> persons = yellowpersons;//协变(子类到父类的转变) ,泛型参数 out标记,一般用于出参,这个正确的 // list<person> personlist = yellowpersonlist; 因为list是类,而且泛型参数并没有标记out,不适用协变,故这样转换是错误的 foreach (var p in persons) { system.console.writeline($"item :【name={p.name},age={p.age},sex={p.sex},color={p.color}】"); } //逆变(泛型参数父类转子类) action<object, object> showplusresultaction = (d1, d2) => console.writeline($"{d1}+{d2}={d1.tostring() + d2.tostring()}"); action<string, string> showstrplusresultaction = showplusresultaction;//逆变(父类到子类的转变),泛型参数 in标记,一般用于入参 showplusresultaction(55, 66); showstrplusresultaction("你好", "中国"); showmsg<person> showmsg = new showmsg<person>((p) => { system.console.writeline($"showmsg :【name={p.name},age={p.age},sex={p.sex},color={p.color}】"); }); //showmsg<helloword> showmsg2 = new showmsg<helloword>(...); 这样是不行的,因为泛型约束为需继承自person showmsg.invoke(new person() { name = "zuowenjun", age = 33, sex = "b" }); showmsg.invoke(new yellowperson() { name = "zuowenjun2", age = 33, sex = "b" }); //综合演示:入参逆变,出参协变 func<person, person, string> getdatafunc = (x, y) => x.name + y.name; func<yellowperson, yellowperson, object> getdatafunc2 = getdatafunc; object dataresult = getdatafunc2(new yellowperson() { name = "张三", age = 33, sex = "g" }, new yellowperson() { name = "赵六", age = 33, sex = "b" }); system.console.writeline($"getdatafunc2:{dataresult}"); list<int> a = new list<int>(); list<string> b = new list<string>(); bool isequal = (a.gettype() == b.gettype()); system.console.writeline($"list<int> 与 list<string> {(isequal ? "is" : "not")} equal ");//结果是不相等 #endregion //以上示例需要用到的类 public class baseclass { /// <summary> /// 必需是用virtual标记的方法(即:虚方法)或abstract标记的方法(即:抽象方法)子类才能使用override进行重写 /// </summary> /// <param name="name"></param> public virtual void sayhello(string name) { system.console.writeline($"{nameof(baseclass)} say:{name},hello!"); } } public class demogenericclass : baseclass, idisposable { public void displayresult<t>(t arg) where t : struct { system.console.writeline($"demogenericclass.displayresult:{arg}"); } public void dispose() { system.console.writeline("demogenericclass disposed"); } public override void sayhello(string name) { base.sayhello(name); system.console.writeline($"{nameof(demogenericclass)} say:{name},hello again!"); } } public class person { public virtual color color { get; } public string name { get; set; } public int age { get; set; } public string sex { get; set; } } public class blackperson : person { public override color color => color.black; } public class yellowperson : person { public override color color => color.yellow; } public class whiteperson : person { public override color color => color.white; }
java(伪泛型,编译后类型参数擦除,同一个泛型类型不同的泛型参数类型相同,有泛型接口,泛型类,泛型方法,泛型约束:super限定下边界,逆变,用于入参,属于消费者,extends限定上边界,协变,用于出参,属于生产者,还有?通匹符)
//常用泛型集合 list<integer> intlist = new arraylist(){ { add(1); add(2); add(3); add(4); add(5); } }; map<string,string> map=new hashmap(){ { put("k1","v1"); put("k2","v2"); put("k3","v3"); } }; //泛型方法 demogenericclass demo=new demogenericclass(); demo.displayresult(new yellowperson(){{ setname("zwj");setsex("b");setage(33); }}); list<integer> a=new arraylist<>(); list<string> b=new arraylist<>(); boolean isequal =(a.getclass()==b.getclass()); system.out.printf("list<integer>与list<string> %s equal %n",isequal?"is":"not"); //结果是相等,都是同一个list类型,不能使用instanceof判断泛型类型实例 //协变、逆变(详见说明:https://www.jianshu.com/p/2bf15c5265c5 ,意义与c#相同) list<? super person> persons=new arraylist<>(); //super:限定下边界,逆变,用于入参 persons.add(new person(){ { setname("张三"); setage(25); setsex("b"); } }); persons.add(new yellowperson(){ { setname("赵六"); setage(18); setsex("g"); } }); list<? extends person> result= (list<? extends person>) persons;//extends:限定上边界,协变,用于出参 for (person p:result){ system.out.printf("person list item:%s %n",p.tostring()); } //以上示例需要用到的类 public class demogenericclass implements autocloseable { @override public void close() throws exception { system.out.println("demogenericclass closed"); } public <t extends person> void displayresult(t arg) //泛型约束(泛型参数上边界,协变) { system.out.printf("demogenericclass.displayresult:%s %n",arg.tostring()); } } public class person { private string name; private int age; private string sex; public string getname() { return name; } public void setname(string name) { this.name = name; } public int getage() { return age; } public void setage(int age) { this.age = age; } public string getsex() { return sex; } public void setsex(string sex) { this.sex = sex; } @override public string tostring() { return string.format("person=[name:%s,age:%d,sex:%s] %n", name, age, sex); } } class yellowperson extends person { @override public string tostring() { return "yellowperson#tostring-"+ super.tostring(); } }
六、自动释放
c#(采用using包裹,要实现自动释放必需实现autocloseable接口)
using (var demo2 = new demogenericclass()) //demogenericclass实现idisposable接口 { demo2.displayresult(123456); }
java(采用try包裹,要实现自动释放必需实现idisposable接口)
try(demogenericclass demo=new demogenericclass()) { demo.displayresult(new yellowperson(){ { setname("zuowenjun"); setage(33); setsex("b"); } }); }
七、重写(override)
c#(必需是用virtual标记的方法(即:虚方法)或abstract标记的方法(即:抽象方法)子类才能使用override进行重写,重写后父类的方法将被子类取代,若需在子类中执行父类被重写的方法,应使用base关键字,若父类方法非虚方法或抽象方法但又想“重写”怎么办?则只能使用new覆盖方法,覆盖方法与重写方法的不同之处在于,在父类中仍可以正常执行父类的方法而不会执行子类的覆盖方法,覆盖方法的方法签名、访问修饰符均没有严格限制,即使不相同仍不会报错,但ide会有提示,如需真正覆盖父类方法,则应按照重写的规范来,只是使用new来修饰覆盖方法,但覆盖方法与重写方法有本质不同,一般情况下更建议使用重写方法)
c#所有类的普通方法默认是密封方法(类似java的final方法),是不允许被重写,可以理解为默认是不开放的,需要开放重写的方法必需使用virtual标记为虚方法(虚方法至少是protected及以上的访问权限),若重写后不想被后续的子类再次重写,则可以标记为sealed,即:密封方法
public class baseclass { /// <summary> /// 必需是用virtual标记的方法(即:虚方法)或abstract标记的方法(即:抽象方法)子类才能使用override进行重写 /// </summary> /// <param name="name"></param> public virtual void sayhello(string name) { system.console.writeline($"{nameof(baseclass)} say:{name},hello!"); } } public class demogenericclass : baseclass { public override void sayhello(string name) { base.sayhello(name); system.console.writeline($"{nameof(demogenericclass)} say:{name},hello again!"); } }
java(非private 且非 final 修饰的普通方法默认均可在子类中进行重写,重写要求基本与c#相同,只是无需强制override关键字,但建议仍使用@override注解,以便ide进行重写规范检查,重写后父类的方法将被子类取代,若需在子类中执行父类被重写的方法,应使用super关键字)
java所有类的普通方法默认是虚方法,都是可以被重写,可以理解为默认是开放重写的,若不想被重写则应标记为final ,即:最终方法(c#中称密封方法)
public class baseclass{ public void testoutput(string msg){ system.out.println("output msg:" + msg); } } public class demogenericclass extends baseclass { @override public void testoutput(string msg){ super.testoutput(msg); system.out.println("output again msg:" + msg); } }
asp.net core vs spring boot 框架部署类比篇:
一、引用依赖(包)
c#(编辑csproj文件,可以通过packagereference引用包、projectreference引用同一个解决方案下的其它项目,reference引用本地dll组件,csproj除了引用包以外,还可以通过在propertygroup元素下配置相关的属性,比如targetframework指定sdk框架版本等)
.net项目的包是nuget包,可以从nuget.org上查找浏览所需的包,项目中引用依赖包,除了在csproj文件中使用packagereference添加编辑外(具体用法参见:项目文件中的包引用 (packagereference))还可以使用package manager控制台使用包管理命令,如:
install-package exceleasyutil -version 1.0.0
,或者直接使用.net cli命令行工具,如:dotnet add package exceleasyutil --version 1.0.0
.net有包、元包、框架 之分,详细了解:
<!--包引用--> <itemgroup> <packagereference include="autofac.extras.dynamicproxy" version="4.5.0" /> <packagereference include="autofac" version="4.9.2" /> <packagereference include="microsoft.aspnetcore.app" /> <packagereference include="microsoft.visualstudio.web.codegeneration.design" version="2.1.1" /> <packagereference include="autofac.extensions.dependencyinjection" version="4.4.0" /> </itemgroup> <!--同一解方案下的项目引用--> <itemgroup> <projectreference include="..\standardclasslib2019\standardclasslib2019.csproj" /> </itemgroup> <!--本地组件直接引用--> <itemgroup> <reference include="kyexpress.common"> <hintpath>xxxx\xxxx.dll</hintpath> <private>true</private> </reference> </itemgroup>
java(编辑pom 文件,通过dependencies.dependency来声明引入多个依赖,根据scope可以指定依赖的有效作用范围)
<dependencies> <!--maven包依赖--> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter</artifactid> </dependency> <!--本地jar包依赖(scope=system,systempath=jar包存放目录)--> <dependency> <groupid>cn.zuowenjun.boot.mybatis.plugin</groupid> <artifactid>cn.zuowenjun.boot.mybatis.plugin</artifactid> <version>1.0</version> <scope>system</scope> <systempath>${basedir}/src/main/libs/xxxxx.jar</systempath> </dependency> <!--同一父项目module之间依赖,注意这个必需先创建基于pom的父项目,然后各子moudle 的pom 的parent指向父项目--> <dependency> <groupid>cn.zuowenjun.springboot</groupid> <artifactid>springboot-demo1</artifactid> <version>1.0-snapshot</version> </dependency> </dependencies>
java pom 依赖继承两种方式
通过parent继承,如下所示:(如下是非常典型的spring boot的parent继承),项目将继承spring-boot-starter-parent pom中的所有设置及依赖(如:properties、dependencies等)
<parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactid> <version>2.1.6.release</version> </parent>
通过dependencymanagement继承,如下所示:(这是依赖管理,dependencymanagement里只是声明依赖,并不实现引入,因此子项目可按需显式的声明所需的依赖项。如果不在子项目中声明依赖,则不会从父项目中继承依赖,只有在子项目中声明了依赖项,且没有指定具体版本,才会从父项目中继承依赖项,(写了版本号相当于覆盖),version和scope都读取自父pom)
<dependencymanagement> <dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-dependencies</artifactid> <version>greenwich.sr2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencymanagement>
二、依赖注入 di (ioc容器)
c#(一般在startup文件中configureservices方法中按需注册依赖,注册依赖可以指定生命周期如:addtransient【瞬时,即:每次都创建新实例】、addscoped【作用域范围内】、addsingleton【单例,仅实例化一次】,具体效果可以参见:在 asp.net core 依赖注入)
//1.使用asp.net core默认的di框架,在startup文件中configureservices方法中按需注册依赖 public void configureservices(iservicecollection services) { //采用asp.net core默认的ioc容器注册 services.addtransient<ioperationtransient, operation>(); services.addscoped<ioperationscoped, operation>(); services.addsingleton<ioperationsingleton, operation>(); services.addsingleton<ioperationsingletoninstance>(new operation(guid.empty)); } //2.在controller中就可以直接采用构造函数注入或指明从ioc容器中获得实例[fromservices] [apicontroller] [route("api/[controller]")] public class democontroller : controller { private readonly operationservice operationservice; public democontroller(operationservice operationservice) { this.operationservice = operationservice; } [route("optid")] public object operation([fromservices]operationservice optsrv){ //todo:方法体中直接使用operationservice 或 入参optsrv均可 } } //如上所需接口及类定义 public interface ioperation { guid operationid { get; } } public interface ioperationtransient : ioperation { } public interface ioperationscoped : ioperation { } public interface ioperationsingleton : ioperation { } public interface ioperationsingletoninstance : ioperation { } public class operation : ioperationtransient, ioperationscoped, ioperationsingleton, ioperationsingletoninstance { public operation() : this(guid.newguid()) { } public operation(guid id) { operationid = id; } public guid operationid { get; private set; } } public class operationservice { public operationservice( ioperationtransient transientoperation, ioperationscoped scopedoperation, ioperationsingleton singletonoperation, ioperationsingletoninstance instanceoperation) { transientoperation = transientoperation; scopedoperation = scopedoperation; singletonoperation = singletonoperation; singletoninstanceoperation = instanceoperation; } public ioperationtransient transientoperation { get; } public ioperationscoped scopedoperation { get; } public ioperationsingleton singletonoperation { get; } public ioperationsingletoninstance singletoninstanceoperation { get; } }
c#使用第三方ioc容器,如:autofac,由第三方ioc容器接管并实现di,示例如下:(autofac支持更多、更灵活的依赖注入场景)
public iserviceprovider configureservices(iservicecollection services) { //采用asp.net core默认的ioc容器注册 services.addtransient<ioperationtransient, operation>(); services.addscoped<ioperationscoped, operation>(); services.addsingleton<ioperationsingleton, operation>(); services.addsingleton<ioperationsingletoninstance>(new operation(guid.empty)); services.addtransient<operationservice, operationservice>(); var containerbuilder = new containerbuilder(); containerbuilder.populate(services); //交由autofac ioc容器管理 var container = containerbuilder.build(); return new autofacserviceprovider(container);//使用utofac ioc容器 }
java(可以使用xml来进行bean的依赖注册,也可使用注解方式来进行依赖注册,目前在di方面更多的是流行注解注册及注入,故这里也以注解依赖注册及注入为简要说明,更多有关注解依赖注册及注入以及xml的依赖注册及注入详细说明,可查阅我之前的文章:java web快速入门之通过一个简单的spring项目了解spring的核心(aop、ioc))
注解依赖注册一般可以通过自定义一个spring统一注册配置类,如代码中所示beansconfig,这种一般对于集中注册bean或bean之间有先后依赖,先后顺序时比较有效果;另一种是直接在bean上使用@component注解(或其它专用含义的注解,如:@repository、@service,这些注解本身也标记了@component注解)
//1. 在自定义的spring统一注册配置类中注册相关bean @configuration public class beansconfig { @bean @scope("prototype") //singleton,request,session @order(1) //注册顺序 public demobean demobean(){ return new demobean(); } @bean("demo") //定义名称 @order(2) public demointerface demointerface(){ return new demoimplbean(demobean()); //构造函数注入 } } //2.在controller中就可以直接通过属性注入或构造函数注入获得实例,并在action中使用这些实例对象 @restcontroller public class democontroller { @autowired private demobean demobean; @autowired @qualifier("demo")//指定从ioc中解析的bean注册名 private demointerface demointerface; @autowired private demobean2 demobean2; @requestmapping(path = "/demo/msg",method = requestmethod.get,produces = "application/json;charset=utf-8") public object testmsg(@requestparam(value = "m",required = false) string m){ //todo:可直接使用:demobean、demointerface、demobean2这些私有字段,它们通过属性注入 return "test msg:" + m; } } //以下是如上所需的类及接口定义 public class demobean { } public interface demointerface { void showmsg(string msg); } public class demoimplbean implements demointerface { private demobean demobean; public demoimplbean(demobean demobean){ this.demobean=demobean; } @override public void showmsg(string msg) { system.out.println("show msg:" + msg); } } //通过标记component,交由spring ioc自动扫描注册 @component public class demobean2 { }
三、过滤器、拦截器 aop
c#(在asp.net core中实现aop常见有三种方式:第一种:添加action过滤器(仅适用于mvc);第二种:使用第三方的aop切面拦截器(如下文的aopinterceptor,可拦截指定的任意位置的虚方法),第三种:在请求管道中添加中间件(仅适用mvc))
public iserviceprovider configureservices(iservicecollection services) { services.addmvc(opt => opt.filters.add<aopfilter>() //第一种:添加过滤器,实现action执行前后记录耗时 ).setcompatibilityversion(compatibilityversion.version_2_1); var containerbuilder = new containerbuilder(); containerbuilder.populate(services); containerbuilder.registertype<aopinterceptor>(); containerbuilder.registertype<operationservice>().interceptedby(typeof(aopinterceptor)).enableclassinterceptors(); //第二种:启用autofac的aop拦截 var container = containerbuilder.build(); return new autofacserviceprovider(container); } public void configure(iapplicationbuilder app, ihostingenvironment env) { if (env.isdevelopment()) { app.usedeveloperexceptionpage(); } //第三种:使用一个自定义的中间件,实现aop的效果 app.use(async (ctx, next) => { //如果为示例逻辑 if (!ctx.request.query.trygetvalue("token", out var tokenval) || tokenval != "zuowenjun") { await ctx.response.writeasync("验证token失败,禁止访问!"); return; } ctx.request.enablebuffering();//启动用buffer,以便可以重置position var requestreader = new streamreader(ctx.request.body); var requestcontent = requestreader.readtoend(); ctx.request.body.position = 0; //需要重置为流开头,否则将导致后续的model binding失效等各种问题 var originalresponsestream = ctx.response.body;//记录原始请求 using (var ms = new memorystream()) { ctx.response.body = ms;//因原始请求为只写流,故此处用自定义的内存流来接收响应流数据 var watch = stopwatch.startnew(); await next.invoke(); watch.stop(); ms.position = 0; var responsereader = new streamreader(ms); var responsecontent = responsereader.readtoend(); string logmsg = $"execedtime:{ watch.elapsedmilliseconds.tostring() }ms,request,{requestcontent},response: { responsecontent}"; logger.loginformation(logmsg); ms.position = 0;//恢复流位置为开头 await ms.copytoasync(originalresponsestream); //将当前的流合并到原始流中 ctx.response.body = originalresponsestream; //恢复原始响应流 }; }); app.usemvc(); } /// <summary> /// filter仅针对接入层(mvc)有效,底层服务若需使用aop,则必需使用特定的aop框架 /// </summary> public class aopfilter : iactionfilter { private readonly stopwatch stopwatch = new stopwatch(); public void onactionexecuting(actionexecutingcontext context) { //执行前逻辑 stopwatch.start(); } public void onactionexecuted(actionexecutedcontext context) { //执行后逻辑 stopwatch.stop(); var returnresult = context.result; if (returnresult is objectresult) { var objresult = (returnresult as objectresult); objresult.value = new { original = objresult.value, elapsedtime = stopwatch.elapsedmilliseconds.tostring() + "ms" }; } else if (returnresult is jsonresult) { var jsonresult = (returnresult as jsonresult); jsonresult.value = new { original = jsonresult.value, elapsedtime = stopwatch.elapsedmilliseconds.tostring() + "ms" }; } } }
java(可以通过自定义filter、handlerinterceptor、methodinterceptor 、around aop增强等方式实现aop拦截处理)
//最先执行,由servlet拦截请求(适用web) @webfilter(filtername = "demofilter",urlpatterns = "/*") class demofilter implements filter { @override public void init(filterconfig filterconfig) throws servletexception { //初始化 } @override public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception { //过滤处理 } @override public void destroy() { //销毁之前执行 } } //其次执行,由spring mvc拦截请求(适用spring mvc) @component public class demohandlerinterceptor implements handlerinterceptor { //也可继承自handlerinterceptoradapter @override public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception { //执行前 return false; } @override public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception { //执行后,生成视图之前执行 } @override public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception { //在dispatcherservlet完全处理完请求之后被调用,可用于清理资源 } } //最后执行,拦截方法 @component class demomethodinterceptor implements methodinterceptor{ @override public object invoke(methodinvocation methodinvocation) throws throwable { return null; } } //方法拦截的另一种形式 @component @aspect class autoaspectjinterceptor { @around("execution (*..controller.*.*(..))") public object around(proceedingjoinpoint point) throws throwable{ //执行前 object object = point.proceed(); //执行后 return object; } }
特别说明:asp.net core中的fitler 与spring mvc中的methodinterceptor类似,都是控制方法,而asp.net core中的请求管道中间件与spring mvc中的filter、handlerinterceptor类似,都是控制请求过程。这点要搞清楚。
四、配置读取
c#(支持多种配置数据提供程序,支持多种获取配置信息的方式,详见:asp.net core 中的配置)
//configuration为iconfiguration实例对象 configuration.getvalue("key");//适用单个key-value configuration.get<tconfig>();//适用整个config文件映射为一个tconfig类型的对象 configuration.getsection("key").getchildren();//获取子项集合
java(支持多种配置数据源格式(yml,properties),可通过@value、@configurationproperties、environment等常见方法来获取配置信息)
//1.通过@value方式获取配置信息 @value("${zuowenjun.site}") public string zwjsite; //2.通过创建一个映射配置信息的bean(configproperties) 方式获取配置信息 @component @configurationproperties()//如果有前缀,则可以设置prefix=xxx public static class zuowenjun { private string site; private string skills; private string motto; public string getsite() { return site; } public void setsite(string site) { this.site = site; } public string getskills() { return skills; } public void setskills(string skills) { this.skills = skills; } public string getmotto() { return motto; } public void setmotto(string motto) { this.motto = motto; } } //3.通过environment来直接获取配置信息 environment.getproperty("zuowenjun.site");
五、发布、部署、运行
c#(asp.net core:除了如下使用.net cli命今进行发布打包,也可以使用vs或vs code可视化操作进行发布操作)
dotnet publish --configuration release
java(spring mvc:除了如下使用maven命令进行清理打包,还可以使用idea来进行打包,具体方法可参见:springboot项目打包成jar运行2种方式)
mvn clean package;
c#(asp.net core)、java(spring mvc)都可以:
都支持windows服务器、linux服务器等多种平台服务器 部署运行
-
都支持使用命令行启动运行asp.net core 或spring mvc应用,例如:
dotnet aspnetcoreapp.dll --urls="http://*:5001"
java -jar springmvcapp.jar --server.port=5001
都支持jenkins ci&cd ,docker、k8s虚拟化部署
-
都支持在linux服务器中以守护进程方式运行,例如:
nohup dotnet aspnetcoreapp.dll > aspnetcoreapp.out 2>&1 &
nohup java -jar springmvcapp.jar > springmvcapp.out 2>&1 &
//或者都使用supervisor来构建守护进程,还提供管理ui,具体请参见网上相关资源
好了,总结到此结束,愿能帮助到那些处于.net 转java 或java 转.net或者想多了解一门编程语言的朋友们,祝大家事业有成。今后将分享更多关于分布式、算法等方面的知识,不局限.net或java语言,敬请期待,谢谢!
码字不易,若需转载及转载我之前的文章请注明出处,谢谢。