乾貨分享:ASP.NET CORE(C#)與Spring Boot MVC(JAVA)異曲同工的編程方式總結

来源:https://www.cnblogs.com/zuowj/archive/2019/07/13/11181726.html
-Advertisement-
Play Games

我(夢在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近發表的一篇文章《 ".NET CORE與Spring Boot編寫控制台程式應有的優雅姿勢" 》看到都上48小時閱讀排行榜(當然之前發表的文章也有哦!),說明關註.NET CORE及 ...


目錄

我(夢在旅途,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)都可以:

  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語言,敬請期待,謝謝!

碼字不易,若需轉載及轉載我之前的文章請註明出處,謝謝。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • python幫組文檔 class int(x, base=10) Return an integer object constructed from a number or string x, or return 0 if no arguments are given. If x defines _ ...
  • 從資料庫中讀取數據 在 "http://sqlitebrowser.org/" 下載sqlite3可視化工具,在本main.go同目錄下創建 資料庫,創建表如下: 將數據插入資料庫 ...
  • Linked Url:https://leetcode.com/problems/single-number/ Given a non-empty array of integers, every element appears twice except for one. Find that sin ...
  • 一、創建線程 二、Future jdk8之前的實現方式,在JUC下增加了Future,從字面意思理解就是未來的意思,但使用起來卻著實有點雞肋,並不能實現真正意義上的非同步,獲取結果時需要阻塞線程,或者不斷輪詢。 三、CompletableFuture 使用原生的CompletableFuture實現異 ...
  • layui(諧音:類UI) 是一款採用自身模塊規範編寫的前端 UI 框架,遵循原生 HTML/CSS/JS 的書寫與組織形式,門檻極低,拿來即用。 layui文件上傳示例地址:https://www.layui.com/demo/upload.html 本次教程是基於springboot2.0的。 ...
  • 在騰訊雲伺服器的運維過程中,有時候運維人員可能忘記了騰訊雲的實例密碼導致無法連接上伺服器,此時就需要在騰訊雲管理後臺對伺服器密碼進行重置操作,重置騰訊雲伺服器實例密碼操作也非常的簡單方便。 (1)首先在瀏覽器上打開騰訊雲官網,登錄好相關賬號,而後點擊下圖中右上角的個人中心圖標進入個人中心頁面。 (2 ...
  • 騰訊雲伺服器和阿裡雲伺服器的Centos系統都是沒有Linux系統的一個版本,Centos系統的操作都是在沒有類似Windows圖形化操作界面的黑框框命令視窗進行操作的,需要使用到很多Linux操作命令,針對不熟悉Linux系統命令的人來說,操作是比較困難的。此時如果想簡便化操作,可以採用往伺服器中 ...
  • 在sqlserver的使用過程中,有時候可能會因為sqlserver版本過低等原因的導致無法附加以及還原資料庫,我們可以通過sql server management studio軟體的幫助菜單參看到詳細的sqlserver版本號、數據訪問組件等版本號信息。 首先打開你本地的sql server m ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...