我(梦在旅途,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((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 map = new Dictionary

{

{ "key1","value1" },//(隐式自动调用add方法)

{ "key2", "value2" },

{ "key3", "value3" }

};

foreach (var item in map)

{

System.Console.WriteLine($"key:{item.Key},value:{item.Value}");

}

List list = new List

{

"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 map=new HashMap(){

{

put("key1","value1");

put("key2","value2");

put("key3","value3");

}

};

for (Map.Entry item:map.entrySet()) {

System.out.printf("key:%1$s,value:%2$s\n",item.getKey(),item.getValue());

}

List 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,Func各有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 multiplyFunc = new Func(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 helloAction = new Action(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 multiplyFunc = new BiFunction() {

@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 helloAction=new Consumer() {

@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 multiplyFunc2 = new Func((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 helloAction2 = new Action(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 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 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 intList = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

List longList = new List { 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L };

Dictionary dic = new Dictionary {

{ "k1","v1"},{ "k2","v2"},{ "k3","v3"}

};

//泛型方法

var demo = new DemoGenericClass();

//demo.DisplayResult("学习永无止境"); 错误,因为约束是值类型

demo.DisplayResult(ConsoleColor.DarkGreen);

List yellowPersonList = new List {

new YellowPerson(){ Name="zzz",Age=11,Sex="G"},

new YellowPerson(){ Name="xxx",Age=22,Sex="B"}

};

//协变(泛型参数子类转父类)

//public interface IEnumerable

IEnumerable yellowPersons = yellowPersonList;

IEnumerable persons = yellowPersons;//协变(子类到父类的转变) ,泛型参数 out标记,一般用于出参,这个正确的

// List 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 showPlusResultAction = (d1, d2) => Console.WriteLine($"{d1}+{d2}={d1.ToString() + d2.ToString()}");

Action showStrPlusResultAction = showPlusResultAction;//逆变(父类到子类的转变),泛型参数 in标记,一般用于入参

showPlusResultAction(55, 66);

showStrPlusResultAction("你好", "中国");

ShowMsg showMsg = new ShowMsg((p) =>

{

System.Console.WriteLine($"ShowMsg :【Name={p.Name},Age={p.Age},Sex={p.Sex},Color={p.Color}】");

});

//ShowMsg showMsg2 = new ShowMsg(...); 这样是不行的,因为泛型约束为需继承自Person

showMsg.Invoke(new Person() { Name = "zuowenjun", Age = 33, Sex = "B" });

showMsg.Invoke(new YellowPerson() { Name = "zuowenjun2", Age = 33, Sex = "B" });

//综合演示:入参逆变,出参协变

Func getDataFunc = (x, y) => x.Name + y.Name;

Func 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 a = new List();

List b = new List();

bool isEqual = (a.GetType() == b.GetType());

System.Console.WriteLine($"List 与 List {(isEqual ? "is" : "not")} Equal ");//结果是不相等

#endregion

//以上示例需要用到的类

public class BaseClass

{

///

/// 必需是用virtual标记的方法(即:虚方法)或abstract标记的方法(即:抽象方法)子类才能使用override进行重写

///

///

public virtual void SayHello(string name)

{

System.Console.WriteLine($"{nameof(BaseClass)} Say:{name},hello!");

}

}

public class DemoGenericClass : BaseClass, IDisposable

{

public void DisplayResult(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 intList = new ArrayList(){

{

add(1);

add(2);

add(3);

add(4);

add(5);

}

};

Map 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 a=new ArrayList<>();

List b=new ArrayList<>();

boolean isEqual =(a.getClass()==b.getClass());

System.out.printf("List与List %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 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

{

///

/// 必需是用virtual标记的方法(即:虚方法)或abstract标记的方法(即:抽象方法)子类才能使用override进行重写

///

///

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有包、元包、框架 之分,详细了解:包、元包和框架

xxxx\xxxx.dll

true

JAVA(编辑POM 文件,通过dependencies.dependency来声明引入多个依赖,根据scope可以指定依赖的有效作用范围)

org.springframework.boot

spring-boot-starter

cn.zuowenjun.boot.mybatis.plugin

cn.zuowenjun.boot.mybatis.plugin

1.0

system

${basedir}/src/main/libs/xxxxx.jar

cn.zuowenjun.springboot

springboot-demo1

1.0-SNAPSHOT

JAVA POM 依赖继承两种方式

通过parent继承,如下所示:(如下是非常典型的spring boot的parent继承),项目将继承spring-boot-starter-parent POM中的所有设置及依赖(如:properties、dependencies等)

org.springframework.boot

spring-boot-starter-parent

2.1.6.RELEASE

通过dependencyManagement继承,如下所示:(这是依赖管理,dependencyManagement里只是声明依赖,并不实现引入,因此子项目可按需显式的声明所需的依赖项。如果不在子项目中声明依赖,则不会从父项目中继承依赖,只有在子项目中声明了依赖项,且没有指定具体版本,才会从父项目中继承依赖项,(写了版本号相当于覆盖),version和scope都读取自父pom)

org.springframework.cloud

spring-cloud-dependencies

Greenwich.SR2

pom

import

二、依赖注入 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();

services.AddScoped();

services.AddSingleton();

services.AddSingleton(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();

services.AddScoped();

services.AddSingleton();

services.AddSingleton(new Operation(Guid.Empty));

services.AddTransient();

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() //第一种:添加过滤器,实现ACTION执行前后记录耗时

).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

var containerBuilder = new ContainerBuilder();

containerBuilder.Populate(services);

containerBuilder.RegisterType();

containerBuilder.RegisterType().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();

}

///

/// Filter仅针对接入层(MVC)有效,底层服务若需使用AOP,则必需使用特定的AOP框架

///

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();//适用整个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语言,敬请期待,谢谢!

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

java按需读取word文件_干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结...相关推荐

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

    目录 我(梦在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近发表的一篇文章<.NET CORE与Spring Boot编写控制台程 ...

  2. python怎么读取word文件_使用python编辑和读取word文档

    python调用word接口主要用到的模板为python-docx,基本操作官方文档有说明. 使用python新建一个word文档,操作就像文档里介绍的那样: 1 from docx importDo ...

  3. java 微信企业号上传文件_微信企业号上传下载多媒体文件接口详解演示-java

    讲完这篇博客,微信企业号的接口就说完了,下载了我源码的童鞋都知道,里面的备注很详细,但凡看过几遍就都会自己开发了,我说的这些接口至此我已经全部开发完了,剩下的就是你们自己写功能了,都是轻而易举的事情了 ...

  4. Java 读取word文件的页数

    实现思路: 1,由于现有的插件读取word的页数不是很准确,于是将word文件转换为PDF文件 2,读取PDF文件的页数以获取word文件的页数 需要的插件:链接:https://pan.baidu. ...

  5. Java读取word文件,字体,颜色

    在Android读取Word文件时,在网上查看时可以用tm-extractors,但好像没有提到怎么读取Word文档中字体的颜色,字体,上下标等相关的属性.但由于需要,要把doc文档中的内容(字体,下 ...

  6. java准确读取word文件页数

    转自:https://blog.csdn.net/tiandixuanwuliang/article/details/71298406 由于本人在做一个网上打印网站,遇到了一个需求是"准确读 ...

  7. java读取word文件并设置其字体样式_Java读取word文件,字体,颜色(示例代码)

    在Android读取Word文件时,在网上查看时可以用tm-extractors,但好像没有提到怎么读取Word文档中字体的颜色,字体,上下标等相关的属性.但由于需要,要把doc文档中的内容(字体,下 ...

  8. java写入word文件_使用JAVA写入word文件

    我读了一个word文档,想用 Java写入另一个word文件.我希望将读取文档中的内容的样式(字体,粗体,斜体,标题等)写入,因为它是创建的新文档. 我能够复制内容而不是格式样式. import ja ...

  9. Java POI 读取word文件

    Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 1.读取word 2003及word 2007需要 ...

最新文章

  1. 1024,千家公司程序员幸福指数大比拼!最“幸福”的程序员是你吗?
  2. linux path环境变量检索目录,Linux下动态链接库加载路径及搜索路径问题
  3. 08年美国最值得信赖20大公司排行 谷歌落榜
  4. 水瓶座和什么座最配:天秤座,双子座,狮子座
  5. [initandlisten] connection refused because too many open connections: 819 of 819
  6. 校内集训(20170903)
  7. SAP UI5 Gateway后台ETAG校验逻辑
  8. python查找第二次输入字符串在第一次字符串中出现的次数
  9. android imei *#06#,[Android]Hot key IMEI *#06# and *#07#
  10. 一个简单的Python调度器
  11. 刀片存储助力发挥融合基础架构优势
  12. 网络安全实效性衡量指南:如何作出准确评估
  13. matlab如何获得一个二值掩码,1.3矩阵的掩码操作 - osc_uarhdl2n的个人空间 - OSCHINA - 中文开源技术交流社区...
  14. Layui 文档(含镜像站资源)
  15. 终于注册csdn博客了!
  16. 数据库系统概念第6版第三章答案
  17. smart原则_一百天目标达成:SMART原则
  18. 广和通L610_ADP对腾讯云通信笔记——02(STM32F411控制L610)
  19. Verilog语言学习
  20. 64位windows无法正常安装dnw的解决方案

热门文章

  1. Python使用matplotlib可视化人口金字塔图、人口金字塔显示不同性别不同年龄段的人口数,是了解人口组成的最优可视化方法、人口金字塔图可以用来表示按体积排序的群体的分布、形成漏斗结构
  2. R语言ggplot2可视化:使用R原生plot函数为指定曲线下面的区域着色、ggplot2可视化在曲线的特定下方添加分割线、ggplot2为指定曲线下面的区域着色
  3. R语言ggplot2可视化:应用pivot_longer函数将数据从宽格式转换为长格式、为dataframe的每一列绘制密度图和直方图(堆叠)
  4. R语言deplyr包complete.cases函数查看数据缺失情况实战
  5. python模拟退火(Simulated Annealing)参数寻优实战
  6. 作为候选人,你需要问些什么?你需要查些什么?这些关乎你的利益和未来!
  7. 期望最大EM(Expectation Maximum)算法是什么?为什么有时候不用梯度下降而是用EM算法
  8. Evaluation and Validation of AssemblingCorrected PacBio Long Reads for MicrobialGenome Completion
  9. java 爬虫 异步_Java 爬虫遇上数据异步加载,试试这两种办法!
  10. python字典按value逆序排序_python 对字典按照value进行排序的方法