干货分享: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包裹,要实现自动释放必需实现IDisposable接口)
            using (var demo2 = new DemoGenericClass()) //DemoGenericClass实现IDisposable接口
{
demo2.DisplayResult(123456);
}
JAVA(采用try包裹,要实现自动释放必需实现AutoCloseable接口)
        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)都可以:

  1. 都支持WINDOWS服务器、Linux服务器等多种平台服务器 部署运行

  2. 都支持使用命令行启动运行ASP.NET CORE 或Spring MVC应用,例如:

    dotnet aspnetcoreApp.dll --urls="http://*:5001"

    java -jar springmvcApp.jar --server.port=5001

  3. 都支持Jenkins CI&CD ,Docker、k8s虚拟化部署

  4. 都支持在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语言,敬请期待,谢谢!

码字不易,若需转载及转载我之前的文章请注明出处,谢谢。

ASP.NET CORE(C#)与Spring Boot MVC(JAVA)的更多相关文章

  1. 干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结

    目录 C# VS JAVA 基础语法类比篇: 一.匿名类 二.类型初始化 三.委托(方法引用) 四.Lambda表达式 五.泛型 六.自动释放 七.重写(override) ASP.NET CORE ...

  2. 用Spring Boot颠覆Java应用开发

    Java开发概述: 使用Java做Web应用开发已经有近20年的历史了,从最初的Servlet1.0一步步演化到现在如此多的框架,库以及整个生态系统.经过这么长时间的发展,Java作为一个成熟的语言, ...

  3. 16.用Spring Boot颠覆Java应用开发

    转自:https://www.cnblogs.com/aishangJava/p/5971288.html Java开发概述: 使用Java做Web应用开发已经有近20年的历史了,从最初的Servle ...

  4. 利用spring boot创建java app

    利用spring boot创建java app 背景 在使用spring框架开发的过程中,随着功能以及业务逻辑的日益复杂,应用伴随着大量的XML配置和复杂的bean依赖关系,特别是在使用mvc的时候各 ...

  5. Spring Boot 获取 java resources 下文件

    Spring Boot 获取 java resources 下文件 Spring Boot 获取 resources 目录下的目录(例:获取 resources 目录下的 template 目录): ...

  6. 玩转spring boot——MVC应用

    如何快速搭建一个MCV程序? 参照spring官方例子:https://spring.io/guides/gs/serving-web-content/ 一.spring mvc结合thymeleaf ...

  7. asp.net core系列 41 Web 应用 MVC视图

    一.MVC视图 在Web开发的MVC和Razor中,都有使用视图,在Razor中称为"页"..cshtml视图是嵌入了Razor标记的HTML模板. Razor 标记使用C#代码, ...

  8. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

  9. Spring Boot MVC 使用 JSP 作为模板

    Spring Boot 默认使用 Thymeleaf 作为模板引擎,直接在 template 目录中存放 JSP 文件并不能正常访问,需要在 main 目录下新建一个文件夹来存放 JSP 文件,而且需 ...

随机推荐

  1. 使用apache 的 ab命令压力测试nginx服务器

    nginx压力测试方法: #ab命令 #安装ab #Centos系统 yum install apr-util #Ubuntu系统 sudo apt-get install apache2-utils ...

  2. 【数论】P1029 最大公约数和最小公倍数问题

    题目链接 P1029 最大公约数和最小公倍数问题 思路 如果有两个数a和b,他们的gcd(a,b)和lcm(a,b)的乘积就等于ab. 也就是: ab=gcd(a,b)*lcm(a,b) 那么,接下来 ...

  3. GO语言strconv包的使用

    Go语言中strconv包实现了基本数据类型和其字符串表示的相互转换. strconv包 strconv包实现了基本数据类型与其字符串表示的转换,官方文档中文版. string与int类型转换 Ato ...

  4. 数据结构Java版之红黑树(八)

    红黑树是一种自动平衡的二叉查找树,因为存在红黑规则,所以有效的防止了二叉树退化成了链表,且查找和删除的速度都很快,时间复杂度为log(n). 什么是红黑规则? 1.根节点必须是黑色的. 2.节点颜色要 ...

  5. 常用的maven仓库地址

    maven 仓库地址: 共有的仓库http://repo1.maven.org/maven2/http://repository.jboss.com/maven2/http://repository. ...

  6. C格式字符串转为二叉树

    最近在LeetCode做题,二叉树出现错误时不好排查,于是自己写了一个函数,将前序遍历格式字串转换成二叉树. 形如 "AB#D##C##" 的字符串,"#"表示 ...

  7. 夺灵者哈卡(Hakkar, the Soulflayer)

    Hakkar, the Soulflayer夺灵者哈卡Deathrattle: Shuffle a Corrupted Blood into each player's deck.亡语:将一张“堕落之 ...

  8. js的prototype理解

    转载:https://www.cnblogs.com/douyage/p/8630529.html 在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实 ...

  9. Mybatis-Generator开发教程

    转载:https://blog.csdn.net/qqyb2000/article/details/80031559 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyB ...

  10. Js 实现返回上一页

    Js 实现返回上一页 <a href="javascript:history.go(-1)">返回上一页</a> <a href="java ...