月份: 2020 年 5 月

  • 北京公車採購案 八成為電動車

    北京公交集團持續落實首都空氣清淨規劃,2016年計畫採購2700輛新的公車,其中有81%將採購電動車款,以減少大眾運輸系統的碳排放量。

    北京霧霾問題嚴重,改用電動車來取代汽油車是減輕空氣汙染的方法之一。2015年間,北京公交集團共置換了2306輛汽油公車為新式環保公車,其中有一半以上是新能源電動車。同時,2015年改造超過8000輛柴油公車,減少60%的氮氧化合物排放量。

    在純電動車基礎設施方面,北京公交集團陸續興建吳癸電動車線路網、變電站、充電站網絡等,共有21個公車站可供純電動車充電。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 北汽豪擲80億元佈局新能源汽車

    2015年12月28日,北京汽車集團有限公司就“北汽新能源汽車動力電池”、“北汽集團常州產業基地”兩個專案,與常州市政府簽約。

    此次北汽集團擬在常州建設的兩個項目總投資80億元,其中,北汽新能源汽車動力電池專案總投資約30億元,規劃動力電池產能達到5G瓦時,同時將以滆湖低碳濕地公園培訓中心為主體,打造北汽新能源綠色商學院,其主要目的是加強新體系電池基礎研究和關鍵技術開發,推進新一代鋰離子電池的工程化和產業化,實現對動力電池產業鏈核心環節資源掌控,以支撐北汽新能源業務需求。

    而北汽集團常州產業基地專案總投資50億元,總規劃年產30萬輛整車及配套零部件、物流專案,其中一期年產15萬輛SUV、MPV和輕型客車,二期重點生產新能源汽車,打造產業生態鏈。

    此前,北汽集團總投資100億元的新能源汽車和總投資50億元的通用航空兩個項目已經於今年4月和10月相繼落戶常州。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • Deepin 下 使用 Rider 開發 .NET Core

    Deepin 下 使用 Rider 開發 .NET Core

    目錄

    Deepin 下 使用 Rider 開發 .NET Core

    國產的 Deepin 不錯,安利一下。

    Deepin 用了也有一兩年,也只是玩玩,沒用在開發上面。後來 Win10 不太清真了,就想着能不能到 Deepin下擼碼。要搞開發,首先少不了 IDE,VS2019 用不來,Vs Code 太複雜、麻煩,後來發現了 Rider 這個神器,可是 Rider 是英文界面,筆者的英文是渣渣的。結果在 Windows 下 使用 Rider 開發一段時間后, 已經熟悉了 Rider ,於是計劃後面遷移到 Deepin 下開發 .NET Core 。筆者裝了雙系統 Windows10 + Deepin 15。

    安裝 Rider

    Rider 的Linux 下載地址

    下載壓縮包后,將壓縮包解壓,打開 bin 目錄,在目錄下打開終端,運行

    sh rider.sh

    或者直接點擊 rider.sh 文件,選擇執行即可。

    之後會彈出安裝界面。

    根據提示一步步安裝。

    最後會要求輸入賬號密碼或者激活碼激活 Rider 。

    我這個是高材生的福利~你們沒有的話就用 Github 開源項目免費申請使用,或者其他手段激活。

    安裝完畢后,點擊 New Solution ,發現只能創建 .NET Frameork 的項目(Mono)。

    先關閉 Rider ,接下來安裝 .NET Core

    安裝 .NET Core SDK

    有兩種安裝方法

    1. 自己下載二進制的 安裝包

    2. 使用軟件包形式安裝

      無論哪種方法,如果不把 SDK/Runtime 放到 /usr/share/dotnet 下,Rider 是無法識別的(默認路徑,可以進入Rider修改設置),下面兩種方法都是在 Linux 簡單二進制安裝 .NET Core SDK的方法。

    sudo ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet -f
    export DOTNET_ROOT=/usr/share/dotnet export 
    PATH=$PATH:/usr/share/dotnet

    推薦第一種方法,第二種方法的或,可以按照微軟的文檔自己試試。。。

    安裝完畢后就是這個樣子。

    如果要通過軟件包形式安裝,參考一下這裏 

    體驗開發

    想不到在 Deepin 下,Rider 竟然支持 Desktop Application(WPF)。

    不過這不是重點,我又不會 WPF,先試一下 ASP.NET Core ,晚一點再看看 WPF。

    運行的時候,報這個錯,是 Https 證書的問題,只需要任意位置打開終端,輸入下面的命令即可。

    dotnet dev-certs https

    不知道為什麼,瀏覽器打開 Blazor 應用一片空白。。。

    換成 MVC 試試。

    不知道為什麼 Blazor 打開會空白。不管了,試試 Desktop Application。

    創建 Wpf 項目后,提示要安裝插件,然後退出重新打開。

    不過最後重新打開項目還是報錯

    Project 'WpfApp1' load finished with warnings
                The imported project "/usr/share/dotnet/sdk/3.0.100/Sdks/Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.WinFX.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.  /usr/share/dotnet/sdk/3.0.100/Sdks/Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.NET.Sdk.WindowsDesktop.targets at (26:3)
                Windows is required to build Windows desktop applications. at (59:5)

    算了~就這樣好了,反正我又不會 WPF ~

    本文是使用 Typora 寫的,很清真。

    好好學習唄~

    最後錄了個視頻玩,不知道說啥,看看內容界面就好~

    打不開的話,請點擊 

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

  • .NET Core 3.0 單元測試與 Asp.Net Core 3.0 集成測試

    .NET Core 3.0 單元測試與 Asp.Net Core 3.0 集成測試

    單元測試與集成測試

    測試必要性說明

    相信大家在看到單元測試與集成測試這個標題時,會有很多感慨,我們無數次的在實踐中提到要做單元測試、集成測試,但是大多數項目都沒有做或者僅建了項目文件。這裡有客觀原因,已經接近交付日期了,我們沒時間做白盒測試了。也有主觀原因,面對業務複雜的代碼我們不知道如何入手做單元測試,不如就留給黑盒測試吧。但是,當我們的代碼無法進行單元測試的時候,往往就是代碼開始散發出壞味道的時候。長此以往,將欠下技術債務。在實踐過程中,技術債務常常會存在,關鍵在於何時償還,如何償還。

    上圖說明了隨着時間的推移開發/維護難度的變化。

    測試框架選擇

    在 .NET Core 中,提供了 xUnit 、NUnit 、 MSTest 三種單元測試框架。

    MSTest UNnit xUnit 說明 提示
    [TestMethod] [Test] [Fact] 標記一個測試方法
    [TestClass] [TestFixture] n/a 標記一個 Class 為測試類,xUnit 不需要標記特性,它將查找程序集下所有 Public 的類
    [ExpectedException] [ExpectedException] Assert.Throws 或者 Record.Exception xUnit 去掉了 ExpectedException 特性,支持 Assert.Throws
    [TestInitialize] [SetUp] Constructor 我們認為使用 [SetUp] 通常來說不好。但是,你可以實現一個無參構造器直接替換 [SetUp]。 有時我們會在多個測試方法中用到相同的變量,熟悉重構的我們會提取公共變量,並在構造器中初始化。但是,這裏我要強調的是:在測試中,不要提取公共變量,這會破壞每個測試用例的隔離性以及單一職責原則。
    [TestCleanup] [TearDown] IDisposable.Dispose 我們認為使用 [TearDown] 通常來說不好。但是你可以實現 IDisposable.Dispose 以替換。 [TearDown] 和 [SetUp] 通常成對出現,在 [SetUp] 中初始化一些變量,則在 [TearDown] 中銷毀這些變量。
    [ClassInitialize] [TestFixtureSetUp] IClassFixture< T > 共用前置類 這裏 IClassFixture< T > 替換了 IUseFixture< T > ,
    [ClassCleanup] [TestFixtureTearDown] IClassFixture< T > 共用後置類 同上
    [Ignore] [Ignore] [Fact(Skip=”reason”)] 在 [Fact] 特性中設置 Skip 參數以臨時跳過測試
    [Timeout] [Timeout] [Fact(Timeout=n)] 在 [Fact] 特性中設置一個 Timeout 參數,當允許時間太長時引起測試失敗。注意,xUnit 的單位時毫秒。
    [DataSource] n/a [Theory], [XxxData] Theory(數據驅動測試),表示執行相同代碼,但具有不同輸入參數的測試套件 這個特性可以幫助我們少寫很多代碼。

    以上寫了 MSTest 、UNnit 、 xUnit 的特性以及比較,可以看出 xUnit 在使用上相對其它兩個框架來說提供更多的便利性。但是這裏最終實現還是看個人習慣以選擇。

    單元測試

    1. 新建單元測試項目

    2. 新建 Class

    3. 添加測試方法

              /// <summary>
              /// 添加地址
              /// </summary>
              /// <returns></returns>
              [Fact]
              public async Task Add_Address_ReturnZero()
              {
                  DbContextOptions<AddressContext> options = new DbContextOptionsBuilder<AddressContext>().UseInMemoryDatabase("Add_Address_Database").Options;
                  var addressContext = new AddressContext(options);
      
                  var createAddress = new AddressCreateDto
                  {
                      City = "昆明",
                      County = "五華區",
                      Province = "雲南省"
                  };
                  var stubAddressRepository = new Mock<IRepository<Domain.Address>>();
                  var stubProvinceRepository = new Mock<IRepository<Province>>();
                  var addressUnitOfWork = new AddressUnitOfWork<AddressContext>(addressContext);
      
                  var stubAddressService = new AddressServiceImpl.AddressServiceImpl(stubAddressRepository.Object, stubProvinceRepository.Object, addressUnitOfWork);
                  await stubAddressService.CreateAddressAsync(createAddress);
                  int addressAmountActual = await addressContext.Addresses.CountAsync();
                  Assert.Equal(1, addressAmountActual);
              }
      • 測試方法的名字包含了測試目的、測試場景以及預期行為。
      • UseInMemoryDatabase 指明使用內存數據庫。
      • 創建 createAddress 對象。
      • 創建 Stub 。在單元測試中常常會提到幾個概念 Stub , Mock 和 Fake ,那麼在應用中我們該如何選擇呢?
        • Fake – Fake 通常被用於描述 Mock 或 Stub ,如何判斷它是 Stub 還是 Mock 依賴於使用上下文,換句話說,Fake 即是 Stub 也是 Mock 。
        • Stub – Stub 是系統中現有依賴項的可控替代品。通過使用 Stub ,你可以不用處理依賴直接測試你的代碼。默認情況下, 偽造對象以stub 開頭。
        • Mock – Mock 對象是系統中的偽造對象,它決定單元測試是否通過或失敗。Mock 會以 Fake 開頭,直到被斷言為止。
      • Moq4 ,使用 Moq4 模擬我們在項目中依賴對象。
    4. 打開視圖 -> 測試資源管理器。

    5. 點擊運行,得到測試結果。

    6. 至此,一個單元測試結束。

    集成測試

    集成測試確保應用的組件功能在包含應用的基礎支持下是正確的,例如:數據庫、文件系統、網絡等。

    1. 新建集成測試項目。

    2. 添加工具類 Utilities 。

      using System.Collections.Generic;
      using AddressEFRepository;
      
      namespace Address.IntegrationTest
      {
          public static class Utilities
          {
              public static void InitializeDbForTests(AddressContext db)
              {
                  List<Domain.Address> addresses = GetSeedingAddresses();
                  db.Addresses.AddRange(addresses);
                  db.SaveChanges();
              }
      
              public static void ReinitializeDbForTests(AddressContext db)
              {
                  db.Addresses.RemoveRange(db.Addresses);
                  InitializeDbForTests(db);
              }
      
              public static List<Domain.Address> GetSeedingAddresses()
              {
                  return new List<Domain.Address>
                  {
                      new Domain.Address
                      {
                          City = "貴陽",
                          County = "測試縣",
                          Province = "貴州省"
                      },
                      new Domain.Address
                      {
                          City = "昆明市",
                          County = "武定縣",
                          Province = "雲南省"
                      },
                      new Domain.Address
                      {
                          City = "昆明市",
                          County = "五華區",
                          Province = "雲南省"
                      }
                  };
              }
          }
      }
    3. 添加 CustomWebApplicationFactory 類,

       using System;
       using System.IO;
       using System.Linq;
       using AddressEFRepository;
       using Microsoft.AspNetCore.Hosting;
       using Microsoft.AspNetCore.Mvc.Testing;
       using Microsoft.EntityFrameworkCore;
       using Microsoft.Extensions.Configuration;
       using Microsoft.Extensions.DependencyInjection;
       using Microsoft.Extensions.Logging;
      
       namespace Address.IntegrationTest
       {
           public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
           {
               protected override void ConfigureWebHost(IWebHostBuilder builder)
               {
                   string projectDir = Directory.GetCurrentDirectory();
                   string configPath = Path.Combine(projectDir, "appsettings.json");
                   builder.ConfigureAppConfiguration((context, conf) =>
                   {
                       conf.AddJsonFile(configPath);
                   });
      
                   builder.ConfigureServices(services =>
                   {
                       ServiceDescriptor descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AddressContext>));
      
                       if (descriptor != null)
                       {
                           services.Remove(descriptor);
                       }
      
                       services.AddDbContextPool<AddressContext>((options, context) =>
                       {
                           //var configuration = options.GetRequiredService<IConfiguration>();
                           //string connectionString = configuration.GetConnectionString("TestAddressDb");
                           //context.UseMySql(connectionString);
                           context.UseInMemoryDatabase("InMemoryDbForTesting");
      
                       });
      
                       // Build the service provider.
                       ServiceProvider sp = services.BuildServiceProvider();
                       // Create a scope to obtain a reference to the database
                       // context (ApplicationDbContext).
                       using IServiceScope scope = sp.CreateScope();
                       IServiceProvider scopedServices = scope.ServiceProvider;
                       var db = scopedServices.GetRequiredService<AddressContext>();
                       var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
      
                       // Ensure the database is created.
                       db.Database.EnsureCreated();
      
                       try
                       {
                           // Seed the database with test data.
                           Utilities.ReinitializeDbForTests(db);
                       }
                       catch (Exception ex)
                       {
                           logger.LogError(ex, "An error occurred seeding the " + "database with test messages. Error: {Message}", ex.Message);
                       }
                   });
               }
           }
       }
      • 這裏為什麼要添加 CustomWebApplicationFactory 呢?
        WebApplicationFactory 是用於在內存中引導應用程序進行端到端功能測試的工廠。通過引入自定義 CustomWebApplicationFactory 類重寫 ConfigureWebHost 方法,我們可以重寫我們在 StartUp 中定義的內容,換句話說我們可以在測試環境中使用正式環境的配置,同時可以重寫,例如:數據庫配置,數據初始化等等。
      • 如何準備測試數據?
        我們可以使用數據種子的方式加入數據,數據種子可以針對每個集成測試做數據準備。
      • 除了內存數據庫,還可以使用其他數據庫進行測試嗎?
        可以。
    4. 添加集成測試 AddressControllerIntegrationTest 類。

       using System.Collections.Generic;
       using System.Linq;
       using System.Net.Http;
       using System.Threading.Tasks;
       using Address.Api;
       using Microsoft.AspNetCore.Mvc.Testing;
       using Newtonsoft.Json;
       using Xunit;
      
       namespace Address.IntegrationTest
       {
           public class AddressControllerIntegrationTest : IClassFixture<CustomWebApplicationFactory<Startup>>
           {
               public AddressControllerIntegrationTest(CustomWebApplicationFactory<Startup> factory)
               {
                   _client = factory.CreateClient(new WebApplicationFactoryClientOptions
                   {
                       AllowAutoRedirect = false
                   });
               }
      
               private readonly HttpClient _client;
      
               [Fact]
               public async Task Get_AllAddressAndRetrieveAddress()
               {
                   const string allAddressUri = "/api/Address/GetAll";
                   HttpResponseMessage allAddressesHttpResponse = await _client.GetAsync(allAddressUri);
      
                   allAddressesHttpResponse.EnsureSuccessStatusCode();
      
                   string allAddressStringResponse = await allAddressesHttpResponse.Content.ReadAsStringAsync();
                   var addresses = JsonConvert.DeserializeObject<IList<AddressDto.AddressDto>>(allAddressStringResponse);
                   Assert.Equal(3, addresses.Count);
      
                   AddressDto.AddressDto address = addresses.First();
                   string retrieveUri = $"/api/Address/Retrieve?id={address.ID}";
                   HttpResponseMessage addressHttpResponse = await _client.GetAsync(retrieveUri);
      
                   // Must be successful.
                   addressHttpResponse.EnsureSuccessStatusCode();
      
                   // Deserialize and examine results.
                   string addressStringResponse = await addressHttpResponse.Content.ReadAsStringAsync();
                   var addressResult = JsonConvert.DeserializeObject<AddressDto.AddressDto>(addressStringResponse);
                   Assert.Equal(address.ID, addressResult.ID);
                   Assert.Equal(address.Province, addressResult.Province);
                   Assert.Equal(address.City, addressResult.City);
                   Assert.Equal(address.County, addressResult.County);
               }
           }
       }
    5. 在測試資源管理器中運行集成測試方法。

    6. 結果。

    7. 至此,集成測試完成。需要注意的是,集成測試往往耗時比較多,所以建議能使用單元測試時就不要使用集成測試。

    總結:當我們寫單元測試時,一般不會同時存在 Stub 和 Mock 兩種模擬對象,當同時出現這兩種對象時,表明單元測試寫的不合理,或者業務寫的太過龐大,同時,我們可以通過單元測試驅動業務代碼重構。當需要重構時,我們應盡量完成重構,不要留下欠下過多技術債務。集成測試有自身的複雜度存在,我們不要節約時間而打破單一職責原則,否則會引發不可預期後果。為了應對業務修改,我們應該在業務修改以後,進行回歸測試,回歸測試主要關注被修改的業務部分,同時測試用例如果有沒要可以重寫,運行整個和修改業務有關的測試用例集。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 源碼學習系列之SpringBoot自動配置(篇二)

    源碼學習系列之SpringBoot自動配置(篇二)

    源碼學習系列之SpringBoot自動配置(篇二)之HttpEncodingAutoConfiguration 源碼分析

    繼上一篇博客之後,本博客繼續跟一下SpringBoot的自動配置源碼

    ok,先複習一下上一篇的內容,從前面的學習,我們知道了SpringBoot的自動配置主要是由一個選擇器AutoConfigurationImportSelector,先通過選擇器將自動配置的類加載到Spring容器

    注意點:

    • List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);獲取的候選配置的類名
    • 由SpringFactoriesLoader加載器負責加載配置類名,已經裝載配置類到容器,SpringFactoriesLoader的loadSpringFactories方法讀取自動配置工程的META-INF/spring.factories配置文件,加載配置類的全類名,包裝成Properties對象,然後再加載到容器里
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
        /* 將spring.factories的類都裝載到Spring容器*/
         public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
    
            try {
            //將META-INF/spring.factories文件里配置的屬性都裝載到Enumeration數據結構里
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                ArrayList result = new ArrayList();
                //遍歷獲取屬性,然後再獲取對應的配置類全類名
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
    
                return result;
            } catch (IOException var8) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
            }
        }
    

    ok,Springboot的自動配置類都在這個包里,源碼很多,所以本博客只是簡單跟一下源碼

    自動配置可以說是SpringBoot框架的一個很重要的功能,其強大的功能就是通過很多配置類實現的,當然這麼多配置,可以參考SpringBoot官方的配置參考:
    https://docs.spring.io/spring-boot/docs/2.1.10.RELEASE/reference/html/common-application-properties.html

    SpringBoot的自動配置類很多,顯然不是每個配置類都生效的,比如你沒引對應的jar,那對應的配置類肯定是不起效的,ok,本博客以HttpEncodingAutoConfiguration自動編碼配置類為實例,記錄一下SpringBoot的自動配置

    先補充一些@Conditional註解的用法:詳情可以參考我上篇博客

    @Conditional派生註解 作用(都是判斷是否符合指定的條件)
    @ConditionalOnJava 系統的java版本是否符合要求
    @ConditionalOnBean 有指定的Bean類
    @ConditionalOnMissingBean 沒有指定的bean類
    @ConditionalOnExpression 符合指定的SpEL表達式
    @ConditionalOnClass 有指定的類
    @ConditionalOnMissingClass 沒有指定的類
    @ConditionalOnSingleCandidate 容器只有一個指定的bean,或者這個bean是首選bean
    @ConditionalOnProperty 指定的property屬性有指定的值
    @ConditionalOnResource 路徑下存在指定的資源
    @ConditionalOnWebApplication 系統環境是web環境
    @ConditionalOnNotWebApplication 系統環境不是web環境
    @ConditionalOnjndi JNDI存在指定的項

    通過上篇博客的學習,我們已經知道了SpringBoot有很多自動配置類,所以本博客拿HttpEncodingAutoConfiguration類來看看

    補充:

    • @Configuration proxyBeanMethods屬性:默認是開啟的,開啟后允許其它配置類調用這個類的@bean方法,詳情參看Spring官方文檔:
    package org.springframework.boot.autoconfigure.web.servlet;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
    import org.springframework.boot.autoconfigure.http.HttpProperties;
    import org.springframework.boot.autoconfigure.http.HttpProperties.Encoding;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
    import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.web.filter.CharacterEncodingFilter;
    
    @Configuration(
        proxyBeanMethods = false
    )//指定是一個配置列,關了proxyBeanMethods,其它配置類就不能調相應的@bean類
    @EnableConfigurationProperties({HttpProperties.class})//讓使用 @ConfigurationProperties註解的HttpProperties類生效,HttpProperties類通過ConfigurationProperties註解,將屬性配置一個一個加載進來
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )//指定系統環境是Web環境配置才起效,並且指定類型是SERVLET
    @ConditionalOnClass({CharacterEncodingFilter.class})//系統有CharacterEncodingFilter過濾器類,則配置類起效,CharacterEncodingFilter類:SpringMVC中進行亂碼解決的過濾器
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )//判斷配置文件是否有spring.http.encoding.enabled屬性,如果沒配置,也是默認為true的,因為配置了`matchIfMissing =true`
    public class HttpEncodingAutoConfiguration {
        //創建一個Encoding對象
        private final Encoding properties;
        // 構造函數里通過properties.getEncoding();進行屬性映射,獲取默認的配置
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
    
        @Bean
        @ConditionalOnMissingBean//如果系統沒有CharacterEncodingFilter類,就執行characterEncodingFilter方法
        public CharacterEncodingFilter characterEncodingFilter() {
            //重新創建一個編碼過濾器
            OrderedCharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            //設置默認的配置
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
            return filter;
        }
    
        @Bean
        public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
            return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
        }
    
        private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
            private final Encoding properties;
    
            LocaleCharsetMappingsCustomizer(Encoding properties) {
                this.properties = properties;
            }
    
            public void customize(ConfigurableServletWebServerFactory factory) {
                if(this.properties.getMapping() != null) {
                    factory.setLocaleCharsetMappings(this.properties.getMapping());
                }
    
            }
    
            public int getOrder() {
                return 0;
            }
        }
    }
    

    通過對HttpEncodingAutoConfiguration源碼的學習,可以看出,其實主要是用@Conditional及其派生註解,這些註解都是要在特定情況才會起效,起效了,才會將組件加載到Spring容器里

    ok,然後我們怎麼知道哪些配置是起效的?在SpringBoot項目里,是可以通過配置,開啟打印的,可以在application.properties加上debug=true屬性就可以

    控制台打印的Positive matches就表示有效的配置類

    console打印的Negative matches表示不起效的配置類:
    比如我的項目沒有加aop的,aop自動配置類就不起效

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 010.Kubernetes二進制部署kube-controller-manager

    010.Kubernetes二進制部署kube-controller-manager

    一 部署高可用kube-controller-manager

    1.1 高可用kube-controller-manager介紹


    本實驗部署一個三實例 kube-controller-manager 的集群,啟動后將通過競爭選舉機制產生一個 leader 節點,其它節點為阻塞狀態。當 leader 節點不可用時,阻塞的節點將再次進行選舉產生新的 leader 節點,從而保證服務的可用性。

    為保證通信安全,本文檔先生成 x509 證書和私鑰,kube-controller-manager 在如下兩種情況下使用該證書:

    • 與 kube-apiserver 的安全端口通信;
    • 在安全端口(https,10252) 輸出 prometheus 格式的 metrics。

    1.2 創建kube-controller-manager證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cat > kube-controller-manager-csr.json <<EOF
      3 {
      4   "CN": "system:kube-controller-manager",
      5   "hosts": [
      6     "127.0.0.1",
      7     "172.24.8.71",
      8     "172.24.8.72",
      9     "172.24.8.73"
     10   ],
     11   "key": {
     12     "algo": "rsa",
     13     "size": 2048
     14   },
     15   "names": [
     16     {
     17       "C": "CN",
     18       "ST": "Shanghai",
     19       "L": "Shanghai",
     20       "O": "system:kube-controller-manager",
     21       "OU": "System"
     22     }
     23   ]
     24 }
     25 EOF
     26 #創建kube-controller-manager的CA證書請求文件



    解釋:

    hosts 列表包含所有 kube-controller-manager 節點 IP;

    CN 和 O 均為 system:kube-controller-manager,kubernetes 內置的 ClusterRoleBindings system:kube-controller-manager 賦予 kube-controller-manager 工作所需的權限。



      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
      3 -ca-key=/opt/k8s/work/ca-key.pem -config=/opt/k8s/work/ca-config.json \
      4 -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager	#生成CA密鑰(ca-key.pem)和證書(ca.pem)


    1.3 分發證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     scp kube-controller-manager*.pem root@${master_ip}:/etc/kubernetes/cert/
      7   done


    1.4 創建和分發kubeconfig


    kube-controller-manager 使用 kubeconfig 文件訪問 apiserver,該文件提供了 apiserver 地址、嵌入的 CA 證書和 kube-controller-manager 證書:

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# kubectl config set-cluster kubernetes \
      4   --certificate-authority=/opt/k8s/work/ca.pem \
      5   --embed-certs=true \
      6   --server=${KUBE_APISERVER} \
      7   --kubeconfig=kube-controller-manager.kubeconfig
      8 
      9 [root@k8smaster01 work]# kubectl config set-credentials system:kube-controller-manager \
     10   --client-certificate=kube-controller-manager.pem \
     11   --client-key=kube-controller-manager-key.pem \
     12   --embed-certs=true \
     13   --kubeconfig=kube-controller-manager.kubeconfig
     14 
     15 [root@k8smaster01 work]# kubectl config set-context system:kube-controller-manager \
     16   --cluster=kubernetes \
     17   --user=system:kube-controller-manager \
     18   --kubeconfig=kube-controller-manager.kubeconfig
     19 
     20 [root@k8smaster01 work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig
     21 
     22 [root@k8smaster01 ~]# cd /opt/k8s/work
     23 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     24 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     25   do
     26     echo ">>> ${master_ip}"
     27     scp kube-controller-manager.kubeconfig root@${master_ip}:/etc/kubernetes/
     28   done


    1.5 創建kube-controller-manager的systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# cat > kube-controller-manager.service.template <<EOF
      4 [Unit]
      5 Description=Kubernetes Controller Manager
      6 Documentation=https://github.com/GoogleCloudPlatform/kubernetes
      7 
      8 [Service]
      9 WorkingDirectory=${K8S_DIR}/kube-controller-manager
     10 ExecStart=/opt/k8s/bin/kube-controller-manager \\
     11   --profiling \\
     12   --cluster-name=kubernetes \\
     13   --controllers=*,bootstrapsigner,tokencleaner \\
     14   --kube-api-qps=1000 \\
     15   --kube-api-burst=2000 \\
     16   --leader-elect \\
     17   --use-service-account-credentials\\
     18   --concurrent-service-syncs=2 \\
     19   --bind-address=##MASTER_IP## \\
     20   --secure-port=10252 \\
     21   --tls-cert-file=/etc/kubernetes/cert/kube-controller-manager.pem \\
     22   --tls-private-key-file=/etc/kubernetes/cert/kube-controller-manager-key.pem \\
     23   --port=0 \\
     24   --authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     25   --client-ca-file=/etc/kubernetes/cert/ca.pem \\
     26   --requestheader-allowed-names="" \\
     27   --requestheader-client-ca-file=/etc/kubernetes/cert/ca.pem \\
     28   --requestheader-extra-headers-prefix="X-Remote-Extra-" \\
     29   --requestheader-group-headers=X-Remote-Group \\
     30   --requestheader-username-headers=X-Remote-User \\
     31   --authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     32   --cluster-signing-cert-file=/etc/kubernetes/cert/ca.pem \\
     33   --cluster-signing-key-file=/etc/kubernetes/cert/ca-key.pem \\
     34   --experimental-cluster-signing-duration=8760h \\
     35   --horizontal-pod-autoscaler-sync-period=10s \\
     36   --concurrent-deployment-syncs=10 \\
     37   --concurrent-gc-syncs=30 \\
     38   --node-cidr-mask-size=24 \\
     39   --service-cluster-ip-range=${SERVICE_CIDR} \\
     40   --pod-eviction-timeout=6m \\
     41   --terminated-pod-gc-threshold=10000 \\
     42   --root-ca-file=/etc/kubernetes/cert/ca.pem \\
     43   --service-account-private-key-file=/etc/kubernetes/cert/ca-key.pem \\
     44   --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     45   --logtostderr=true \\
     46   --v=2
     47 Restart=on-failure
     48 RestartSec=5
     49 
     50 [Install]
     51 WantedBy=multi-user.target
     52 EOF


    1.6 分發systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for (( i=0; i < 3; i++ ))
      4   do
      5     sed -e "s/##MASTER_NAME##/${MASTER_NAMES[i]}/" -e "s/##MASTER_IP##/${MASTER_IPS[i]}/" kube-controller-manager.service.template > kube-controller-manager-${MASTER_IPS[i]}.service
      6   done						#修正相應IP
      7 [root@k8smaster01 work]# ls kube-controller-manager*.service
      8 [root@k8smaster01 ~]# cd /opt/k8s/work
      9 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     10 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     11   do
     12     echo ">>> ${master_ip}"
     13     scp kube-controller-manager-${master_ip}.service root@${master_ip}:/etc/systemd/system/kube-controller-manager.service
     14   done						#分發system


    二 啟動並驗證

    2.1 啟動kube-controller-manager 服務

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     ssh root@${master_ip} "mkdir -p ${K8S_DIR}/kube-controller-manager"
      7     ssh root@${master_ip} "systemctl daemon-reload && systemctl enable kube-controller-manager && systemctl restart kube-controller-manager"
      8   done


    2.2 檢查kube-controller-manager 服務

      1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
      2 [root@k8smaster01 ~]# for master_ip in ${MASTER_IPS[@]}
      3   do
      4     echo ">>> ${master_ip}"
      5     ssh root@${master_ip} "systemctl status kube-controller-manager|grep Active"
      6   done



    2.3 查看輸出的 metrics

      1 [root@k8smaster01 ~]# curl -s --cacert /opt/k8s/work/ca.pem --cert /opt/k8s/work/admin.pem --key /opt/k8s/work/admin-key.pem https://172.24.8.71:10252/metrics |head


    注意:以上命令在 kube-controller-manager 節點上執行。

    2.4 查看權限

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:kube-controller-manager



    ClusteRole system:kube-controller-manager 的權限很小,只能創建 secret、serviceaccount 等資源對象,各 controller 的權限分散到 ClusterRole system:controller:XXX 中。

    當在 kube-controller-manager 的啟動參數中添加 –use-service-account-credentials=true 參數,這樣 main controller 會為各 controller 創建對應的 ServiceAccount XXX-controller。內置的 ClusterRoleBinding system:controller:XXX 將賦予各 XXX-controller ServiceAccount 對應的 ClusterRole system:controller:XXX 權限。

      1 [root@k8smaster01 ~]# kubectl get clusterrole|grep controller



    如deployment controller:

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:controller:deployment-controller


    2.5 查看當前leader

      1 [root@k8smaster01 ~]# kubectl get endpoints kube-controller-manager --namespace=kube-system  -o yaml



    kubelet 認證和授權:https://kubernetes.io/docs/admin/kubelet-authentication-authorization/#kubelet-authorization
    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 代碼注入及其拓展–逆向開發

    代碼注入及其拓展–逆向開發

    今天繼續講述逆向開發中另一個比較重要的課程是代碼注入內容,本篇篇幅比較長,但還是有很多乾貨的,希望大家通過此篇文章更加了解逆向開發中的要點和知識點.我們將分解幾個內容,進行講解:

    1. Framework注入
    2. Dylib注入
    3. MethodSwizzle
    4. 微信示例講解
    5. 總結

    讓代碼執行自己的代碼,整體方案如下:

    如何讓別人的app來執行自己的代碼呢? 這就要通過代碼注入的方式來達到,而代碼注入的方式有兩種: 一種是通過framework, 一種是dylib方式,另種方案,可以通過Runtime機制

    代碼注入思路:

    DYLD會動態加載動態庫Framework中所有動態庫,在frameworks中加入自己的一個動態庫,然後在動態庫中hook和注入代碼.

    一、FrameWork注入

     1.準備工作

    • 微信6.6.5(越獄應用)
    • MachOView軟件

      MachOView的下載地址:

      如果想看源碼如下:MachOView源碼:

    • yololib工具(給MachOView注入framework)

      yololib工具下載地址:

    • 簽名文件appsign文件

    2.流程

    2.1 加入準備工作,導入微信6.6.5版本以及腳本appSign.sh重簽名文件

    2.2 將appSign導入到項目腳本中

     

     

     2.3 有了上面的兩個步驟后,然後編譯一下工程,會出現一個temp工程,裡面包含了payload文件

    2.4 显示包內容,查看可執行文件

     2.5 我們通過MachOView軟件查看WeChat

    我們看到有很多的DYLIB,代表的是加載動態庫

    2.6  我們在項目中新建framework

     

    2.7 新建文件用於驗證

    2.8 想要達到剛加載就運行,代碼要寫在load方法

     2.9 編譯一下,查看app包位置會多出一個framework

    2.10 显示包內容,在framework查看

    由上可知,WJHookFrameWork已經加入成功。

    2.11 但是運行並沒有成功,沒有執行load里的代碼

    原因:用MachOView打開可執行的WeChat,沒有找到WJHookFrameWork

    下面我們講述怎麼將WJHookFramework寫入到MachoView文件中?

    3. WJHookFramework寫入到MachOView文件中

    需要使用yololib工具,建議將yololib放到 /usr/local/bin

    3.1 解壓越獄包

    3.2 將WeChat.app显示包內容,找到WeChat可執行的文件

    需要增加執行權限: chmod +x WeChat

    3.3 寫入WeChat可執行文件

    yololib WeChat Frameworks/WJHookFrameWork.framework/WJHookFrameWork

    通過上面的過程,查看MachOView文件Load commands中是否有WJHookFrameWork

    上面圖显示已經加入成功。

    3.4 刪除原有的ipa,打包payload

    zip -ry WeChat.ipa Payload

    將WeChat.ipa放入App目錄中,刪除其他的文件夾。

     

    3.5 再次運行,發現成功!!!

    上面就是framework方式代碼注入。大家可以私信我,如有不懂!!!

     二、Dylib注入

    2.1 新建工程,添加腳本到build phases 


    加入腳本文件

    2.2添加第三方庫dylib(mac os的,非ios)

    2.3 添加依賴

    2.4 修改第三方類庫僅限mac使用,修改Base SDK

    2.5 修改signing 

    2.6 腳本中注入動態庫的代碼

    # ${SRCROOT} 它是工程文件所在的目錄
    TEMP_PATH="${SRCROOT}/Temp"
    #資源文件夾,我們提前在工程目錄下新建一個APP文件夾,裏面放ipa包
    ASSETS_PATH="${SRCROOT}/APP"
    #目標ipa包路徑
    TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
    #清空Temp文件夾
    rm -rf "${SRCROOT}/Temp"
    mkdir -p "${SRCROOT}/Temp"
    
    #----------------------------------------
    # 1. 解壓IPA到Temp下
    unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
    # 拿到解壓的臨時的APP的路徑
    TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
    # echo "路徑是:$TEMP_APP_PATH"
    
    #----------------------------------------
    # 2. 將解壓出來的.app拷貝進入工程下
    # BUILT_PRODUCTS_DIR 工程生成的APP包的路徑
    # TARGET_NAME target名稱
    TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
    echo "app路徑:$TARGET_APP_PATH"
    
    rm -rf "$TARGET_APP_PATH"
    mkdir -p "$TARGET_APP_PATH"
    cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"
    
    #----------------------------------------
    # 3. 刪除extension和WatchAPP.個人證書沒法簽名Extention
    rm -rf "$TARGET_APP_PATH/PlugIns"
    rm -rf "$TARGET_APP_PATH/Watch"
    
    #----------------------------------------
    # 4. 更新info.plist文件 CFBundleIdentifier
    #  設置:"Set : KEY Value" "目標文件路徑"
    /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
    
    #----------------------------------------
    # 5. 給MachO文件上執行權限
    # 拿到MachO文件的路徑
    APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
    #上可執行權限
    chmod +x "$TARGET_APP_PATH/$APP_BINARY"
    
    #----------------------------------------
    # 6. 重簽名第三方 FrameWorks
    TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
    if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
    then
    for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
    do
    
    #簽名
    /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
    done
    fi
    
    #注入
    yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libHankHook.dylib"

    2.7 編譯運行成功(也和上面一樣在類中加入load代碼)

     

    上面就是dylib方式代碼注入,希望對大家有所幫助!!!

     通過上面的兩種方式實現代碼注入,讓別人的app運行自己的app,下面總結如下:

     三、MethodSwizzle

    3.1 概念

    iOS 中實現AOP編程思想的方式其中之一是Method Swizzling,而 Method Swizzling 是利用Runtime特性把一個方法和另個方法的實現做替換,程序運行時修改Dispatch Table里SEL和IMP之間的映射關係.

    通過swizzling method改變目標函數selector所指向實現,在新的實現中來實現所要改的內容即可.

    3.2 特點

    • 繼承: 修改較多,無法敢保證他人一定繼承基類
    • 類別: 類別中重寫方法會覆蓋到原有的實現,其實,在真實的開發中,重寫方法並不是為了取代它,而是為了添加一些實現; 如果幾個類別實現了同樣的方法, 但只有一個類別的方法會被調用.
    • AOP優勢: 減少了重複代碼

    3.3 代碼

    @implementation NSURL (HKURL)
    
    +(void)load
    {
        Method URLWithStr = class_getClassMethod(self, @selector(URLWithString:));
        
        Method HKURL = class_getClassMethod(self, @selector(HKURLWithStr:));
        
        //交換
        method_exchangeImplementations(URLWithStr, HKURL);
    }
    
    +(instancetype)HKURLWithStr:(NSString *)str{
        //調用系統原來的方法
        NSURL * url = [NSURL HKURLWithStr:str];
        if (url == nil) {
            str = @"https://www.blog.com";
        }
        url = [NSURL HKURLWithStr:str];
        
        return url;
    }

    在上面的代碼中,利用method swizzling的交換方法.其他Runtime的使用方法,以及為什麼寫在load方法中,請參考本人另篇博客

    拓展: 為什麼寫在load中?

    • load方法在源文件被裝載到程序中會被自動調用,不需要手動調用,也不需要該類使用不使用無關,在main()前被執行.
    • 當子類重寫了load,假如子類的類別重寫了load,load的調用順序會這樣: 父類、子類、子類類別
    • 但是如果有多個子類category都重寫了load,每個子類category中load都會調用一次
    • 假如子類沒有重寫load,子類的默認load也不會去調用父類的load.此與正常繼承不太一樣.
    • 在正常的開發中, 除了method swizzle ,其他的邏輯代碼盡量不放在load,load方法中的代碼邏輯要盡量簡單

     

    四、微信示例Demo

    4.1 微信–破壞註冊

    4.1.1 將微信程序運行出來,如下圖所示

    4.1.2 根據上面紅色找出類名,方法名

    4.1.3 通過插件class-dump導出微信的.h文件

    class-dump是將OC運行時聲明的信息導出來的工具, 其實可以導出.h文件. 用此工具將未經過加密的app的頭文件導出來.

    使用它同樣也要講此工具拷貝到MAC的目錄下/usr/local/bin下.

    4.1.4 經過sublime text來找出對應的文件

    4.1.5 通過第三部分講解,利用runtime交換方法

    4.1.6 結果

     

    4.2 竊取微信密碼

    4.2.1 找到輸入密碼框的內容

    從上面看出,登錄按鈕為一個FixTitleColorButton對象,Target名字存放的地址為0x280afaa40,Action名字存放地址是0x280afac00。

    4.2.2 查看賬號密碼的輸入框

    發現賬號密碼輸入框對象屬於都一個對象,叫做WCUITextField

    4.2.3 利用LLDB查看登錄具體的Target和Action

    從上面卡出,登錄按鈕在WCAccountMainLoginViewController頁面中;

    登錄點擊方法叫做onNext

    4.2.4 利用Sublime查看WeChat文件

    發現確實有onNext()方法,並從中看出賬號輸入框和密碼輸入框都是WCAccountTextFieldItem中,但是並沒有發現textFileld,但是可以看到WCAccountTextFieldItem是繼承於WCBaseTextFieldItem,我們再看看WCBaseTextFieldItem文件內容

    看出一個m_textField對象,通過tex字段取出string。

    4.2.5 在賬號欄中輸入賬號和密碼

    po [(WCAccountMainLoginViewController *)0x1128bbc00 valueForKey:@"_textFieldUserPwdItem"]
    po [(WCAccountTextFieldItem *)0x28328e880 valueForKey:@"m_textField"]
    po [(WCUITextField *)0x112163a00 text]

    通過LLDB調試輸入的密碼是123456。

    4.2.6 Hook登錄,獲取密碼

    + (void)load {
        NSLog(@"來了,老弟");
        Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), sel_registerName("onNext"));
        //1.保存原始的IMP
        old_onNext = method_getImplementation(onNext);
        //2.SET
        method_setImplementation(onNext, (IMP)my_next);
    }
    
    IMP (*old_onNext)(id self,SEL _cmd);
    
    void my_next(id self,SEL _cmd){
        // 獲取密碼
        NSString *pwd = [[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
        NSString *accountTF = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
        NSLog(@"密碼是!%@",pwd);
        // 將密碼追加在賬號欄的後面
        [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(setText:) withObject:[NSString stringWithFormat:@"%@+%@",accountTF,pwd]];
        //調用原來的方法
        old_onNext(self,_cmd);
    }

    上面用的是setIMP和getIMP的方式,對原方法進行Hook,也可以用class_replaceMethod(),method_exchangeImplementations()。

     

    五、總結

    首先從代碼注入的方式:framework和dylib兩種方式,然後講到Method swizzling方式嘗試Hook,最後又以demo的方式來闡述代碼注入和Hook,希望對大家理解逆向開發的代碼注入有所幫助!!!,歡迎大家繼續關注!!!

     

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 從沒聽說過的併發的名詞-管程

    從沒聽說過的併發的名詞-管程

    在接觸併發之前,我只聽說過進程、線程,管程這個詞倒是頭回聽說,抱着認真好學的態度,去找了找關於 管程 的資料,不學不知道,原來併發里的兩大難題–互斥和同步都可以用管程來解決,可以說,管程是一把解決併發的萬能鑰匙。

     

    那什麼是管程呢?原來管程並不像進程、線程這樣來形容一個特指東西的名詞,管程是指管理共享變量以及讀共享變量的操作過程,讓他們支持併發。Java 中的 Monitor,我們經常將它翻譯成 “監視器”,其實它還有個更學術的名字就是管程。

     

    管程有三種模型,其中廣泛應用的是 MESA 模型,Java 管程實現參考的也是 MESA 模型,所以我就着重學習這個模型。前面提到了管程可以解決併發領域互斥和同步的兩大核心問題,下面我們先看看管程是如何解決互斥問題的。

     

    • 互斥問題的解決

     

    互斥指的同一時刻只允許有一個線程訪問共享資源,管程解決互斥問題的思路很簡單,就是將共享變量及對共享變量的操作統一都封裝起來,如圖:

     

     

    線程 A 和線程 B 如果想訪問共享變量 queue,只能通過管程提供的入隊和出隊操作,入隊和出隊操作保證互斥性,只允許一個線程進入,而對外暴露的就只有管程,看上去有點面向對象封裝的意思。

     

    • 同步問題的解決

     

    在管程解決互斥問題的解決方案中,我們看到了其實共享變量和對共享變量的操作都是被封裝起來的,要想訪問共享變量就要訪問管程,所以同步的解決辦法就是在管程的入口添加一個等待隊列,當多線程想同時進入管程內部時,只允許一個線程進入,其他線程在等待隊列中等待。

     

    進入到管程內部,有可能執行修改共享變量的方法還有條件,比如要執行入隊操作,必須保證隊列不滿;要執行出隊操作,必須保證隊列不空,管程對每個條件的變量還對應有一個等待隊列,如圖:

     

     

    這裏的入口等待隊列與條件等待隊列是完全不同的兩個隊列,當進入管程內部的線程因執行方法的條件不滿足會進入條件等待隊列,等待被其他線程喚醒,喚醒後會重新進入入口的等待隊列,競爭資源。

     

    Java 內置的管程

     

    Java 內置管程與 MESA 模型類似,在 MESA 模型中,條件變量可以有多個, Java 語言內置的管程里只有一個條件變量。

     

     

    Java 內置的管程方案就是 syncronized ,使用 syncronized 修飾的代碼塊,在編譯器會自動生成相關加鎖和解鎖代碼,但是只會支持一個條件變量。

     

     

    總結一下 :

     

     

     

    以上是管程的相關介紹,後續我們會進入 Java JUC 工具包的學習,看看除了 syncronized 其他強大的併發編程類都有哪些獨特的用處。

     

     這裡有一篇 

     

    感興趣的同學可以瀏覽一下哦~

        

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!

  • variable precision SWAR算法

    variable precision SWAR算法

          計算二進制形式中1的數量這種問題,在各種刷題網站上比較常見,以往都是選擇最笨的遍歷方法“矇混”過關。在了解Redis的過程中接觸到了variable precision SWAR算法(以下簡稱VP-SWAR算法),算法異常簡潔,是目前已知的同類方法中最快的。但如果對於位運算不是很熟悉的話,卻不一定容易理解,所以有必要記錄一下。

          下面先看看VP-SWAR算法的完整實現,然後再逐行解釋。

      public int vpSWAR(int i){
        i = (i & 0x55555555) + ((i>>1) & 0x55555555);
        i = (i & 0x33333333) + ((i>>2) & 0x33333333);
        i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
        i = (i * 0x01010101) >> 24;
        return i;
      }

          VP-SWAR算法分為四步,第一步

    i = (i & 0x55555555) + ((i>>1) & 0x55555555);
    

          第一步的作用是計算每兩位為一組的二進制形式包含1的個數。要理解這句話,我們需要從二進制的角度看看到底發生了什麼。首先, 0x55555555 的二進製表示為 0101 0101 0101 0101 0101 0101 0101 0101 ,這個数字的規律是基數位為1,偶數位為0。為簡單起見,我們只考慮兩位,總共有四種情況,即:

     

    i b i & b 結果
    00 01 00
    01 01 01
    10 01 00
    11 01 01

           觀察發現, i & (0b01) 是i的基數位對應b的1位,i的偶數位對應着b的0位, i & (0b01) 的結果會將I的偶數位置為0,而基數位保持不變,得到的結果就是i的基數位包含1的個數。 (i >> 1) & 0x55555555 先將i右移一位,也就是將i的基數位對應b的0位,i的偶數位對應着b的1位,然後再與 0x55555555 按位與,計算出來的是i的偶數位包含1的個數。兩個計算結果相加就得到i每兩位為一組中包含的1的數量,我們最後需要的就是這每兩位一組的和。

          第二步是在第一步的基礎上,計算每四位為一組包含1的個數。按照每2位為一組分組用到了 0x55555555 這個數,那麼自然的,按照每4位為一組分組自然就需要 0b0011 這種形式,這就是使用 0x33333333 的原因。理論上, i & (0b0011) 總共有16種情況,但是四位二進制位最多包含4個1,用二進製表示為 0b0100 ,所以經過第一步之後,i最多有5種取值,如下:

    i b i & b 結果
    0000 0011 0000
    0001 0011 0001
    0010 0011 0010
    0011 0011 0011
    0100 0011 0000

     

          觀察發現, i & (0b0011) 得到的是i的低兩位包含的1的個數,  (i >> 2) & 0b0011 )得到的是i的高兩位包含的1的個數,兩個結果相加得到每四位包含的1的個數。注意,這裏並不是說任何數與 0b0011 按位與得到的都是低兩位包含的1的個數,這裏的前提是第一步的計算,因為經過第一步計算之後,每兩位包含多少個1已經記錄了下來,再和 0b0011 按位與才得到正確的結果。例如, 0x0010 & 0x 0011=0x0010 ,但是我們不能說 0x0010 包含兩個1,但是如果 0x0010 是經過第一步的計算得來,那才說明 0x0010 記錄原始數據低兩位有兩個1。

          第三步在第二步基礎上,計算每8位有多少個1,由 0x010x0011 ,很自然想到 0x00001111 ,其對應的32位的十六進制數就是 0x0F0F0F0F

          第四步就很有意思了,它不再是計算每16位包含1的個數,而是直接計算32位包含1的個數。對於32位的數來說,可以將其按每8位一組分為4組,分別用ABCD表示,例如 0x01020304 用這種形式表示為:

     

     

          假設 0x01020304 是經過前三步計算之後得到的結果,那麼要計算其總共包含多少個1,只需計算A+B+C+D。而ABCD表示的是不同的位區間範圍,不能直接相加,該如何快速計算A+B+C+D的值呢?這裏又用到了移位運算,將B、C、D分別左移8位、16位、24位,使其分別與A對齊:

     

           我們發現,將数字i分別左移0位、8位、16位、24位然後相加的結果,就是 i * 0x01010101 ,因為 i + (i << 8) + (i << 16) + (i << 24) = i * (1 + 1 << 8 + 1 << 16 + 1 << 24) = i * 0x01010101 。對於32位数字來說,左移之後超過32位的部分會被捨棄,低位補0,將左移之後得到的四個数字相加,結果的高8位的值就是原32位數包含的1的個數,要得到這個值,只需要將結果右移24位,將值放在低8位即可。

          到這裏,整個算法就結束了,右移的結果就是1的數量。在Redis中,BITCOUNT命令同時使用了查表法和VP-SWAR這兩種方法。當要計算的位數小於128位時,使用查表法,否則使用VP-SWAR算法。其中查表法的做法是,程序先存一個256長度的表,按順序記錄從0-255(即 0b00000000 – 0b11111111) 數中二進制1的個數,然後對於輸入參數每8位查一次表。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 對於計算機相關專業我們在迷茫什麼

    對於計算機相關專業我們在迷茫什麼

    計算機相關專業初識–對於計算機相關專業我們在迷茫什麼

    前言

    由於種種原因,迫使我寫下這篇博客,我相信,初入計算機相關專業的萌新肯定很迷茫,我該學什麼,我該如何去學,我該如何學好等等問題纏繞心頭。有很多學弟學妹問我該如何去學計算機相關專業,作為過來人,我決定將我的所知所得寫下來,讓初入計算機相關專業的萌新的學習之路走得更順暢一些。

    一、什麼是計算機

    對於剛學習計算機相關專業的萌新來說,了解一下計算機的工作原理是十分必要的,但是在這裏我們不過多闡述,讓大家簡單了解一下就好。

    讓我們先來看一下對於計算機名詞的解釋:

    計算機(computer)俗稱電腦,是現代一種用於高速計算的电子計算機器,可以進行數值計算,又可以進行邏輯計算,還具有存儲記憶功能。是能夠按照程序運行,自動、高速處理海量數據的現代化智能电子設備。

    划重點:

    • 我們注意到,計算機就是一種用於進行數值計算的現代化智能电子設備。需要理解的是為什麼是進行數值計算,在這裏,你會疑惑,為什麼是數值計算呢,我輸入的明明不是数字呀?這個問題很容易解釋清楚,因為計算機只是一種电子設備,它不具有人類獨立思考和不斷學習的能力,它的所有功能都是事先設定好的,所以當計算機面對輸入字符的時候,會將它統一按照ASCII(計算機編碼系統)規則轉換為數值“0”和“1”(二進制數值),所以,在計算機里,數據存儲都是用“0”和“1”(即二進制數值)來實現。

    • 還有一點值得注意,按照程序運行,那麼問題來了,程序是什麼?程序就是一組計算機能識別和執行的指令, 它以某些程序設計語言編寫,運行於某種目標結構體繫上 。舉個例子,程序就像是用英語(程序設計語言,例如c,c++)寫的文章,要讓一個懂的英語的人(編譯器,如C的編譯器gcc,這裏要注意編譯器和IDE的區別,通常IDE包含編譯器)同時也會閱讀這篇文章的人(結構體系)來閱讀、理解、標記這篇文章。

    有學妹問過我,為什麼簡單的代碼,能實現豐富的效果。其實這取決於編譯器的強大能力。下面來簡單介紹一下,編輯器,編譯器,IDE(集成開發環境)的區別。

    • 編輯器:編輯器就是用來編輯的軟件,比如windows自帶的記事本就是一個編輯器, 記事本沒有語法高亮,不显示行號,當一段可執行代碼寫完后無法通過內置環境執行,必須手動輸入命令執行編譯等等一些弊端,所以很少有程序員會用記事本去寫代碼 , 寫代碼比較好用的編輯器軟件有vscode,vim,sublime,notepad++,emacs,atom等等 ,雖然編輯器原始功能不足,但是開發人員為了使編輯器更加友好,所以有很多內置插件可供使用,完全可以手動打造一個IDE。
    • 編譯器:簡單來說,編譯器就是將“一種語言(一般為高級語言,如c,c++,java,python等,計算機不可直接識別和執行)”翻譯為“另一種語言(一般為低級語言,低級語言即機器語言,機器語言是用二進制代碼錶示的計算機能直接識別和執行的一種機器指令的集合)”的程序。舉個例子,用Dev-C++寫好一段可執行"hello,world!"C語言代碼之後,我們要讓它在屏幕打印出來我們想要它輸出的"hello,world!",就需要通過gcc編譯器執行編譯后才能显示。其他語言同理。
    • IDE:集成開發環境,用於程序開發環境的應用程序,一般包含代碼編輯器編譯器調試器圖像用戶界面等工具。集成了代碼編寫程序分析程序編譯程序調試等功能。如 jetbrains 的用於Java開發的 IntelliJ IDEA 、用於JavaScript開發的WebStorm、用於Python開發Pycharm,微軟的 Visual Studio系列 ,IBM的Eclipse。

    二、我們該學什麼

    很多初入計算機相關專業的萌新,總是很迷茫,不知道自己該學什麼,通常是他們知道如何去學好學校開設的每一門課程,就是不知道自己該向哪些方向學習,這些方向指的是專業技能和就業方向,諸如web開發、Android/IOS開發、數據分析、人工智能、網絡安全、遊戲開發、軟件測試等等。有這種疑惑很正常,迷茫也是正常的,但我們總要讓自己了解自己所需,然後腳踏實地,一步一步去充實自己的能力。而我想做的也很簡單,就是幫助大家解除心裏的疑惑。那麼,我們開始進入正題。

    1. 我們該如何選擇適合自己的方向

    對於這個問題,其實是很難回答清楚的,因為每個人的興趣都不相同,所以就很難去站在自己的角度去回答疑問者的問題。但是,原理都是想通的,我相信我的經驗會幫助到你們。

    • 通常,學校每學期都會開設一門或多門語言(程序設計語言,下文同),那麼,喜歡一門語言,首先要愛上它的語言風格,諸如Java的嚴謹,Python的自由,總有一款適合你;其次,在學習語言的過程中,一定要了解它能幹什麼,市場環境如何,工作崗位多少等綜合因素,再決定要不要去深入這門語言,並且主攻自己感興趣的那個方向。

    • 對於學校沒有開設,但是自己又想學習的語言而言,該如何去選擇。首先,學校開設的語言基本是市場比較流行的語言,也符合市場需求,所以,完全可以在學校開設的語言中去選擇自己想要了解並學習的語言。此外,我們可以藉助 TIOBE ( TIOBE 編程社區指數是編程語言流行度的指標,該榜單每月更新一次,指數基於全球技術工程師、課程和第三方供應商的數量。包括流行的搜索引擎,如谷歌、必應、雅虎、維基百科、亞馬遜、YouTube 和百度都用於指數計算。 )去了解語言的流行程度,流行程度決定市場需求,以此來參考自己想要了解並學習的語言,在此附上2019年11月語言排名。

    2. 主流編程語言主要應用場景

    • Java

      1. 企業級應用開發: 大到全國聯網的系統,小到中小企業的應用解決方案,Java都佔有極為重要的地位 。
      2. web後端開發: JSP+Servlet+JavaBean 是一種比較流行的開發模式。
      3. 移動領域:手機遊戲。
      4. Android App開發: android 開發只用到了JAVA的語法和JAVA SE的一小部分API。
    • C

      C語言是一門基礎語言,是其他一些語言的基礎,例如MATLAB,Object-C,Lua等.同時也是學習來比較難的語言,達到精通的程度沒有3-10年左右很難,C語言沒有比較完善的開發框架,是面向過程的一門語言,講究算法跟邏輯。

      1. 科研
      2. 服務器: 網絡核心設備,如路由器、交換機、防火牆。
      3. 操作系統:類unix系統(Linux/freebsd)
      4. 嵌入式開發: 在一個特定的硬件環境上開發與構建特定的可編程軟件系統的綜合技術。
      5. 自動化控制
    • Python

      1. 圖形處理
      2. 數學處理
      3. 文本處理
      4. 數據庫編程
      5. 網絡編程
      6. 多媒體應用
      7. pymo引擎: 運行於Symbian S60V3,Symbian S60V5,Symbian 3,Android,Windows,Linux,Mac Os,Maemo,MeeGo系統上的AVG遊戲引擎。
      8. 黑客編程
      9. 網絡安全
    • C++

      1. 遊戲開發
      2. 科學計算
      3. 網絡軟件
      4. 操作系統
      5. 設備驅動程序
      6. 移動設備
      7. 嵌入式開發
      8. 科研
      9. 編譯器
    • C#

      1. web後端開發
      2. 桌面軟件開發
      3. 人工智能
      4. 遊戲開發
    • JavaScript
      唯一能用於前後端開發的語言web前端開發
      1. web前端開發
      2. node web後端開發
      3. 操作系統
      4. 後台
      5. 桌面軟件開發
      6. 混合App
      7. 小程序
    • PHP

      1. web後端開發
      2. 桌面軟件開發
      3. 命令行腳本
    • SQL

      1. 操作數據庫
    • Swift

      1. 蘋果生態系統應用開發
    • Ruby

      1. web開發
    • R

      數據科學闖天下,左手Python右手R

      1. 機器學習
      2. 數據分析
      3. 科學計算
    • Go

      1. web後端開發
      2. 高性能服務器應用

    3. 主流編程語言學習路徑(將持續更新,僅供參考)

    • JavaScript

    4. 主流編程語言入門學習書籍推薦

    語言 書籍
    C 《嗨翻C語言》
    C++ 《C++權威教程》
    Java 《Java輕鬆學》
    Python 《Python編程從入門到實戰》
    JavaScript 《JavaScript入門經典》
    PHP 《PHP編程實戰》
    SQL 《SQL基礎教程》
    Swift 《Swift編程權威指南》
    Ruby 《Ruby從入門到精通》
    R 《R語言實戰》
    Go 《Go語言聖經》

    5. 編程學習網站推薦

    網站 網址
    菜鳥教程
    W3School
    實驗樓
    猿學
    慕課網
    SegmentFault
    博客園
    GitHub
    掘金
    學習數據科學
    易百教程
    看雲

    三、總結

    通篇寫完,感覺自己也重新學到了很多,學習就是一個反覆複習的過程,每次學習都能帶給自己不一樣的收穫。希望以上內容可以給初入計算機相關專業的萌新帶來一些幫助,後面我會不斷更新和優化本文,請大家持續關注。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享