標籤: USB CONNECTOR

  • Spring Security 實戰乾貨:如何實現不同的接口不同的安全策略

    Spring Security 實戰乾貨:如何實現不同的接口不同的安全策略

    1. 前言

    歡迎閱讀 Spring Security 實戰乾貨 系列文章 。最近有開發小夥伴提了一個有趣的問題。他正在做一個項目,涉及兩種風格,一種是給小程序出接口,安全上使用無狀態的JWT Token;另一種是管理後台使用的是Freemarker,也就是前後端不分離的Session機制。用Spring Security該怎麼辦?

    2. 解決方案

    我們可以通過多次繼承WebSecurityConfigurerAdapter構建多個HttpSecurityHttpSecurity 對象會告訴我們如何驗證用戶的身份,如何進行訪問控制,採取的何種策略等等。

    如果你看過之前的教程,我們是這麼配置的:

    /**
     * 單策略配置
     *
     * @author felord.cn
     * @see org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration
     * @since 14 :58 2019/10/15
     */
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
    @EnableWebSecurity
    @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    public class CustomSpringBootWebSecurityConfiguration {
        
        /**
         * The type Default configurer adapter.
         */
        @Configuration
        @Order(SecurityProperties.BASIC_AUTH_ORDER)
        static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                super.configure(auth);
            }
    
            @Override
            public void configure(WebSecurity web) {
                super.configure(web);
            }
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 配置 httpSecurity
    
            }
        }
    }
    

    上面的配置了一個HttpSecurity,我們如法炮製再增加一個WebSecurityConfigurerAdapter的子類來配置另一個HttpSecurity。伴隨而來的還有不少的問題要解決。

    2.1 如何路由不同的安全配置

    我們配置了兩個HttpSecurity之後,程序如何讓小程序接口和後台接口走對應的HttpSecurity

    HttpSecurity.antMatcher(String antPattern)可以提供過濾機制。比如我們配置:

         @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 配置 httpSecurity
                http.antMatcher("/admin/v1");
    
            }
    

    那麼該HttpSecurity將只提供給以/admin/v1開頭的所有URL。這要求我們針對不同的客戶端指定統一的URL前綴。

    舉一反三隻要HttpSecurity提供的功能都可以進行個性化定製。比如登錄方式,角色體系等。

    2.2 如何指定默認的HttpSecurity

    我們可以通過在WebSecurityConfigurerAdapter實現上使用@Order註解來指定優先級,數值越大優先級越低,沒有@Order註解將優先級最低。

    2.3 如何配置不同的UserDetailsService

    很多情況下我們希望普通用戶和管理用戶完全隔離,我們就需要多個UserDetailsService,你可以在下面的方法中對AuthenticationManagerBuilder進行具體的設置來配置UserDetailsService,同時也可以配置不同的密碼策略。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                // 自行實現
                return  null ;
            }
        });
        // 也可以設計特定的密碼策略
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
        auth.authenticationProvider(daoAuthenticationProvider);
    }
    

    2.4 最終的配置模板

    上面的幾個問題解決之後,我們基本上掌握了在一個應用中執行多種安全策略。配置模板如下:

    /**
     * 多個策略配置
     *
     * @author felord.cn
     * @see org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration
     * @since 14 :58 2019/10/15
     */
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
    @EnableWebSecurity
    @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    public class CustomSpringBootWebSecurityConfiguration {
    
        /**
         * 後台接口安全策略. 默認配置
         */
        @Configuration
        @Order(1)
        static class AdminConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
                //用戶詳情服務個性化
                daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
                    @Override
                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                        // 自行實現
                        return null;
                    }
                });
                // 也可以設計特定的密碼策略
                BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
                auth.authenticationProvider(daoAuthenticationProvider);
            }
    
            @Override
            public void configure(WebSecurity web) {
                super.configure(web);
            }
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 根據需求自行定製
                http.antMatcher("/admin/v1")
                        .sessionManagement(Customizer.withDefaults())
                        .formLogin(Customizer.withDefaults());
    
    
            }
        }
    
        /**
         * app接口安全策略. 沒有{@link Order}註解優先級比上面低
         */
        @Configuration
        static class AppConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
                //用戶詳情服務個性化
                daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
                    @Override
                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                        // 自行實現
                        return null;
                    }
                });
                // 也可以設計特定的密碼策略
                BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
                auth.authenticationProvider(daoAuthenticationProvider);
            }
    
            @Override
            public void configure(WebSecurity web) {
                super.configure(web);
            }
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 根據需求自行定製
                http.antMatcher("/app/v1")
                        .sessionManagement(Customizer.withDefaults())
                        .formLogin(Customizer.withDefaults());
    
    
            }
        }
    }
    

    3. 總結

    今天我們解決了如何針對不同類型接口採取不同的安全策略的方法,希望對你有用,如果你有什麼問題可以留言。多多關注:碼農小胖哥,更多乾貨奉上。

    關注公眾號:Felordcn 獲取更多資訊

    個人博客:https://felord.cn

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

    【其他文章推薦】

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

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

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

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

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

    ※教你寫出一流的銷售文案?

  • 【Java Spring Cloud 實戰之路】添加一個SpringBootAdmin監控

    【Java Spring Cloud 實戰之路】添加一個SpringBootAdmin監控

    0. 前言

    在之前的幾章中,我們先搭建了一個項目骨架,又搭建了一個使用nacos的gateway網關項目,網關項目中並沒有配置太多的東西。現在我們就接着搭建在Spring Cloud 微服務中另一個重要的項目 – Spring boot admin.

    1. Spring Boot Admin 介紹

    Spring Boot Admin 用來監控基於Spring Boot的應用,在Spring Boot Actuator的基礎上提供了簡潔的可視化Web UI。Spring Boot Admin 提供了以下功能:

    • 显示應用的健康狀態
    • 显示應用的細節內容: JVM和內存信息,micrometer信息, 數據源信息,緩存信息等
    • 显示 編譯版本
    • 查看和下載日誌
    • 查看jvm參數和環境變量值
    • 查看Spring Boot項目配置
    • 显示 thread dump
    • 显示 http-traces

    ……

    等一系列內容。

    2. 創建一個 Spring Boot Admin項目

    那麼,我們就來創建一個Spring Boot Admin 項目吧。

    2.1 創建 Spring Boot Admin 服務端

    在manager 目錄下,創建一個 monitor目錄,並在monitor目錄下創建一個pom.xml 文件,添加以下內容:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>monitor</artifactId>
        <version>${revision}</version>
        <packaging>jar</packaging>
        <parent>
            <artifactId>manager</artifactId>
            <groupId>club.attachie</groupId>
            <version>${revision}</version>
        </parent>
    
    </project>
    

    在 manager/pom.xml 註冊我們新建的項目模塊:

    <modules>
        <module>gateway</module>
        <module>monitor</module>
    </modules>
    

    在 monitor 創建如下目錄:

    .
    ├── pom.xml
    └── src
        └── main
            ├── java
            └── resources
    

    在根目錄的pom.xml 添加 Spring Boot Admin 依賴:

    先添加spring-boot-admin版本號變量:

    <spring-boot-admin.version>2.2.3</spring-boot-admin.version>
    

    並在dependencyManagement > dependencies 下添加:

    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>
        <version>${spring-boot-admin.version}</version>
    </dependency>
    

    在monitor/pom.xml文件中添加:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
    </dependencies>
    

    運行

    mvn clean install

    檢查並刷mvn引用緩存。

    創建MonitorApplication類:

    package club.attachie.nature.monitor;
    
    import de.codecentric.boot.admin.server.config.EnableAdminServer;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @EnableAdminServer
    public class MonitorApplication {
        public static void main(String[] args) {
            SpringApplication.run(MonitorApplication.class, args);
        }
    }
    

    啟動后能看到如下界面:

    3 與網關服務進行互通

    在上一篇中,我們添加了Spring Cloud Gateway項目,到目前為止兩個項目之間完全割裂沒有關聯。在這一節,我們在兩者之間建立關聯。也就是說,將gateway 項目引入Spring Admin Boot監聽。

    在 manager/gateway 的pom.xml 文件中加入如下引用:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    然後修改 gateway項目的啟動端口,在resources/bootstrap.yml 添加:

    server:
      port: 8070
    

    在 monitor中加入nacos引用:

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>      
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    修改MonitorApplication 為:

    package club.attachie.nature.monitor;
    
    import de.codecentric.boot.admin.server.config.EnableAdminServer;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    
    @SpringBootApplication
    @EnableAdminServer
    @RefreshScope
    public class MonitorApplication {
        public static void main(String[] args) {
            SpringApplication.run(MonitorApplication.class, args);
        }
    }
    

    創建monitor項目的bootsrap.yml:

    spring:
      application:
        name: monitor
      
      cloud:
      	nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    

    關於這裏的配置 在上一篇 中有個錯誤,應該是 discovery > server-addr,不是 config > server-addr。兩者有區別,discovery表示設置nacos為服務發現中心,config表示nacos為配置中心。

    啟動 gateway 項目和 monitor項目查看效果, 訪問 8080端口:

    可以看到兩個應用可以被發現,如果沒有設置monitor項目把nacos當做服務發現中心,將無法獲取到具體在線的應用。點擊 gateway 進去后可以查看到:

    4. 總結

    我們搭建了一個Spring Boot Admin 項目作為一個監控系統,後續會在這裏添加更多的內容。

    更多內容煩請關注我的博客《高先生小屋》

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

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 通過與C++程序對比,徹底搞清楚JAVA的對象拷貝

    通過與C++程序對比,徹底搞清楚JAVA的對象拷貝

    目錄

    • 一、背景
    • 二、JAVA對象拷貝的實現
      • 2.1 淺拷貝
      • 2.2 深拷貝的實現方法一
      • 2.3 深拷貝的實現方法二
        • 2.3.1 C++拷貝構造函數
        • 2.3.2 C++源碼
        • 2.3.3 JAVA通過拷貝構造方法實現深拷貝
    • 四、總結

    一、背景

    JAVA編程中的對象一般都是通過new進行創建的,新創建的對象通常是初始化的狀態,但當這個對象某些屬性產生變更,且要求用一個對象副本來保存當前對象的“狀態”,這時候就需要用到對象拷貝的功能,以便封裝對象之間的快速克隆。

    二、JAVA對象拷貝的實現

    2.1 淺拷貝
    • 被複制的類需要實現Clonenable接口;
    • 覆蓋clone()方法,調用super.clone()方法得到需要的複製對象;
    • 淺拷貝對基本類型(boolean,char,byte,short,float,double.long)能完成自身的複製,但對於引用類型只對引用地址進行拷貝。
      — 下面我們用一個實例進行驗證:
    /**
     * 單隻牌
     *
     * @author zhuhuix
     * @date 2020-06-10
     */
    public class Card implements Comparable, Serializable,Cloneable {
    
        // 花色
        private String color = "";
        //数字
        private String number = "";
    
        public Card() {
        }
    
        public Card(String color, String number) {
            this.color = color;
            this.number = number;
        }
    
        public String getColor() {
            return this.color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public String getNumber() {
            return this.number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        @Override
        public String toString() {
            return this.color + this.number;
        }
    
        @Override
        public int compareTo(Object o) {
            if (o instanceof Card) {
                int thisColorIndex = Constant.COLORS.indexOf(this.getColor());
                int anotherColorIndex = Constant.COLORS.indexOf(((Card) o).getColor());
                int thisNumberIndex = Constant.NUMBERS.indexOf(this.getNumber());
                int anotherNumberIndex = Constant.NUMBERS.indexOf(((Card) o).getNumber());
    
                // 大小王之間相互比較: 大王大於小王
                if ("JOKER".equals(this.color) && "JOKER".equals(((Card) o).getColor())) {
                        return thisColorIndex > anotherColorIndex ? 1 : -1;
                }
    
                // 大小王與数字牌之間相互比較:大小王大於数字牌
                if ("JOKER".equals(this.color) && !"JOKER".equals(((Card) o).getColor())) {
                    return 1;
                }
                if (!"JOKER".equals(this.color) && "JOKER".equals(((Card) o).getColor())) {
                    return -1;
                }
    
                // 数字牌之間相互比較: 数字不相等,数字大則牌面大;数字相等 ,花色大則牌面大
                if (thisNumberIndex == anotherNumberIndex) {
                    return thisColorIndex > anotherColorIndex ? 1 : -1;
                } else {
                    return thisNumberIndex > anotherNumberIndex ? 1 : -1;
                }
    
            } else {
                return -1;
            }
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
    /**
     * 撲克牌常量定義
     *
     * @author zhuhuix
     * @date 2020-06-10
     */
    public class Constant {
    
        // 紙牌花色:黑桃,紅心,梅花,方塊
        final static List<String> COLORS = new ArrayList<>(
                Arrays.asList(new String[]{"", "", "", ""}));
        // 紙牌数字
        final static List<String> NUMBERS = new ArrayList<>(
                Arrays.asList(new String[]{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"}));
        // 大王小王
        final static List<String> JOKER = new ArrayList<>(
                Arrays.asList(new String[]{"小王","大王"}));
    }
    
    /**
     * 整副副撲克牌
     *
     * @author zhuhuix
     * @date 2020-06-10
     */
    public class Poker implements Cloneable, Serializable {
    
        private List<Card> cards;
    
        public Poker() {
            List<Card> cardList = new ArrayList<>();
            // 按花色與数字組合生成52張撲克牌
            for (int i = 0; i < Constant.COLORS.size(); i++) {
                for (int j = 0; j < Constant.NUMBERS.size(); j++) {
                    cardList.add(new Card(Constant.COLORS.get(i), Constant.NUMBERS.get(j)));
                }
            }
            // 生成大小王
            for (int i = 0; i < Constant.JOKER.size(); i++) {
                cardList.add(new Card("JOKER", Constant.JOKER.get(i)));
            }
    
            this.cards = cardList;
        }
    
       
        // 從整副撲克牌中抽走大小王
        public void removeJoker() {
            Iterator<Card> iterator = this.cards.iterator();
            while (iterator.hasNext()) {
                Card cardJoker = iterator.next();
                if (cardJoker.getColor() == "JOKER") {
                    iterator.remove();
                }
            }
        }
    
        public List<Card> getCards() {
            return cards;
        }
    
        public void setCards(List<Card> cards) {
            this.cards = cards;
        }
    
        public Integer getCardCount() {
            return this.cards.size();
        }
    
        @Override
        public String toString() {
            StringBuilder poker = new StringBuilder("[");
            Iterator<Card> iterator = this.cards.iterator();
            while (iterator.hasNext()) {
                poker.append(iterator.next().toString() + ",");
            }
            poker.setCharAt(poker.length() - 1, ']');
            return poker.toString();
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
    
    /**
     * 測試程序
     *
     * @author zhuhuix
     * @date 2020-6-10
     */
    public class PlayDemo {
    
        public static void main(String[] args) throws CloneNotSupportedException {
    
            // 生成一副撲克牌並洗好牌
            Poker poker1 = new Poker();
            System.out.println("新建:第一副牌共 "+poker1.getCardCount()+" 張:"+poker1.toString());
    
            Poker poker2= (Poker) poker1.clone();
            System.out.println("第一副牌拷頁生成第二副牌,共 "+poker2.getCardCount()+" 張:"+poker2.toString());
    
            poker1.removeJoker();
    
            System.out.println("====第一副牌抽走大小王后====");
            System.out.println("第一副牌還有 "+poker1.getCardCount()+" 張:"+poker1.toString());
            System.out.println("第二副牌還有 "+poker2.getCardCount()+" 張:"+poker2.toString());
    
        }
    
    }
    
    • 運行結果:
      在第一副的對象中抽走了“大小王”,克隆的第二副的對象的“大小王”竟然也被“抽走了”
    2.2 深拷貝的實現方法一
    • 被複制的類需要實現Clonenable接口;
    • 覆蓋clone()方法,自主實現引用類型成員的拷貝複製。
      — 我們只要改寫一下Poker類中的clone方法,讓引用類型成員實現複製:
    /**
     * 整副副撲克牌--自主實現引用變量的複製
     *
     * @author zhuhuix
     * @date 2020-06-10
     */
    public class Poker implements Cloneable, Serializable {
    
        private List<Card> cards;
    
        public Poker() {
            List<Card> cardList = new ArrayList<>();
            // 按花色與数字組合生成52張撲克牌
            for (int i = 0; i < Constant.COLORS.size(); i++) {
                for (int j = 0; j < Constant.NUMBERS.size(); j++) {
                    cardList.add(new Card(Constant.COLORS.get(i), Constant.NUMBERS.get(j)));
                }
            }
            // 生成大小王
            for (int i = 0; i < Constant.JOKER.size(); i++) {
                cardList.add(new Card("JOKER", Constant.JOKER.get(i)));
            }
    
            this.cards = cardList;
        }
    
        // 從整副撲克牌中抽走大小王
        public void removeJoker() {
            Iterator<Card> iterator = this.cards.iterator();
            while (iterator.hasNext()) {
                Card cardJoker = iterator.next();
                if (cardJoker.getColor() == "JOKER") {
                    iterator.remove();
                }
            }
        }
    
        public List<Card> getCards() {
            return cards;
        }
    
        public void setCards(List<Card> cards) {
            this.cards = cards;
        }
    
        public Integer getCardCount() {
            return this.cards.size();
        }
    
        @Override
        public String toString() {
            StringBuilder poker = new StringBuilder("[");
            Iterator<Card> iterator = this.cards.iterator();
            while (iterator.hasNext()) {
                poker.append(iterator.next().toString() + ",");
            }
            poker.setCharAt(poker.length() - 1, ']');
            return poker.toString();
        }
    
    	// 遍歷原始對象的集合,對生成的對象進行集合複製
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Poker newPoker = (Poker)super.clone();
            newPoker.cards = new ArrayList<>();
            newPoker.cards.addAll(this.cards);
            return newPoker;
        }
    }
    
    
    • 輸出結果:
      — 通過自主實現引用類型的複製,原對象與對象的拷貝的引用類型成員地址不再關聯
    2.3 深拷貝的實現方法二
    • 在用第二種方式實現JAVA深拷貝之前,我們首先對C++程序的對象拷貝做個了解:
    2.3.1 C++拷貝構造函數

    C++拷貝構造函數,它只有一個參數,參數類型是本類的引用,且一般用const修飾

    2.3.2 C++源碼
    // 單隻牌的類定義
    // Created by Administrator on 2020-06-10.
    //
    
    #ifndef _CARD_H
    #define _CARD_H
    
    #include <string>
    
    using namespace std;
    
    class Card {
    private :
        string color;
        string number;
    public:
        Card();
    
        Card(const string &color, const string &number);
    
        const string &getColor() const;
    
        void setColor(const string &color);
    
        const string &getNumber() const;
    
        void setNumber(const string &number);
    
        string toString();
    
    };
    
    
    #endif //_CARD_H
    
    // 單隻牌類的實現
    // Created by Administrator on 2020-06-10.
    //
    
    #include "card.h"
    
    Card::Card(){}
    
    Card::Card(const string &color, const string &number) : color(color), number(number) {}
    
    const string &Card::getColor() const {
        return color;
    }
    
    void Card::setColor(const string &color) {
        Card::color = color;
    }
    
    const string &Card::getNumber() const {
        return number;
    }
    
    void Card::setNumber(const string &number) {
        Card::number = number;
    }
    
    
    string Card::toString() {
        return getColor()+getNumber();
    }
    
    
    
    
    
    // 撲克牌類的定義
    // Created by Administrator on 2020-06-10.
    //
    
    #ifndef _POKER_H
    #define _POKER_H
    
    #include <vector>
    #include "card.h"
    
    using namespace std;
    
    const int COLOR_COUNT=4;
    const int NUMBER_COUNT=13;
    const int JOKER_COUNT=2;
    
    const string COLORS[COLOR_COUNT] = {"", "", "", ""};
    const string NUMBERS[NUMBER_COUNT]={"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
    const string JOKER[JOKER_COUNT] ={"小王","大王"};
    
    class Poker {
    private:
        vector<Card> cards;
    public:
        Poker();
    
        Poker(const Poker &poker);
    
        const vector<Card> &getCards() const;
    
        void setCards(const vector<Card> &cards);
    
        int getCardCount();
    
        void toString();
    
        void clear();
    };
    
    
    #endif //_POKER_H
    
    // 撲克牌類的實現
    // Created by zhuhuix on 2020-06-10.
    //
    
    #include "Poker.h"
    #include <iostream>
    
    const vector<Card> &Poker::getCards() const {
        return this->cards;
    }
    
    void Poker::setCards(const vector<Card> &cards) {
        Poker::cards = cards;
    }
    
    // 構造函數
    Poker::Poker() {
        for (int i = 0; i < NUMBER_COUNT; i++) {
            for (int j = 0; j < COLOR_COUNT; j++) {
                this->cards.emplace_back(COLORS[j], NUMBERS[i]);
            }
        }
        for (int i = 0; i < JOKER_COUNT; i++) {
            this->cards.emplace_back("JOKER", JOKER[i]);
        }
    }
    
    // 拷貝構造函數
    Poker::Poker(const Poker &poker) {
        for (int i = 0; i < poker.getCards().size(); i++) {
            this->cards.emplace_back(poker.cards[i].getColor(), poker.cards[i].getNumber());
        }
    }
    
    int Poker::getCardCount() {
        return this->cards.size();
    }
    
    void Poker::toString() {
        cout << "共" << getCardCount() << "張牌:";
        cout << "[";
        for (int i = 0; i < this->cards.size(); i++) {
            cout << this->cards[i].toString();
            if (i != getCardCount() - 1) {
                cout << ",";
            }
        }
        cout << "]" << endl;
    
    }
    
    void Poker::clear() {
        this->cards.clear();
    }
    
    
    // 主測試程序
    // Created by Administrator on 2020-06-10.
    //
    
    #include "Poker.h"
    #include <iostream>
    
    using namespace std;
    
    int main() {
        Poker poker1;
        cout << "第一副牌:";
        poker1.toString();
        // 通過拷貝構造函數生成第二副牌
        Poker poker2(poker1);
        cout << "第二副牌:";
        poker2.toString();
        // 清除撲克牌1
        poker1.clear();
        cout << "清空后,第一副牌:";
        poker1.toString();
        cout << "第二副牌:";
        poker2.toString();
        return 0;
    }
    
    • 輸出:
    2.3.3 JAVA通過拷貝構造方法實現深拷貝
    • JAVA拷貝構造方法與C++的拷貝構造函數相同,被複制對象的類需要實現拷貝構造方法:
      首先需要聲明帶有和本類相同類型的參數構造方法
      其次拷貝構造方法可以通過序列化實現快速複製
    • 拷貝對象通過調用拷貝構造方法進行創建。
      — 我們再改寫一下Poker類,實現拷貝構造方法:
    /**
     * 整副副撲克牌--實現拷貝構造方法
     *
     * @author zhuhuix
     * @date 2020-06-10
     */
    public class Poker implements Serializable {
    
        private List<Card> cards;
    
        public Poker() {
            List<Card> cardList = new ArrayList<>();
            // 按花色與数字組合生成52張撲克牌
            for (int i = 0; i < Constant.COLORS.size(); i++) {
                for (int j = 0; j < Constant.NUMBERS.size(); j++) {
                    cardList.add(new Card(Constant.COLORS.get(i), Constant.NUMBERS.get(j)));
                }
            }
            // 生成大小王
            for (int i = 0; i < Constant.JOKER.size(); i++) {
                cardList.add(new Card("JOKER", Constant.JOKER.get(i)));
            }
    
            this.cards = cardList;
        }
    
        // 拷貝構造方法:利用序列化實現深拷貝
        public Poker(Poker poker) {
    
            try {
    
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(os);
                oos.writeObject(poker);
    
                ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(is);
                this.cards = ((Poker) ois.readObject()).getCards();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    
        // 從整副撲克牌中抽走大小王
        public void removeJoker() {
            Iterator<Card> iterator = this.cards.iterator();
            while (iterator.hasNext()) {
                Card cardJoker = iterator.next();
                if (cardJoker.getColor() == "JOKER") {
                    iterator.remove();
                }
            }
        }
    
        public List<Card> getCards() {
            return cards;
        }
    
        public void setCards(List<Card> cards) {
            this.cards = cards;
        }
    
        public Integer getCardCount() {
            return this.cards.size();
        }
    
        @Override
        public String toString() {
            StringBuilder poker = new StringBuilder("[");
            Iterator<Card> iterator = this.cards.iterator();
            while (iterator.hasNext()) {
                poker.append(iterator.next().toString() + ",");
            }
            poker.setCharAt(poker.length() - 1, ']');
            return poker.toString();
        }
    }
    
    
    • 對測試主程序進行修改:
    /**
     * 測試程序
     *
     * @author zhuhuix
     * @date 2020-6-10
     */
    public class PlayDemo {
    
        public static void main(String[] args) throws CloneNotSupportedException {
    
            // 生成一副撲克牌並洗好牌
            Poker poker1 = new Poker();
            System.out.println("新建:第一副牌共 "+poker1.getCardCount()+" 張:"+poker1.toString());
    
            Poker poker2 = new Poker(poker1);
            System.out.println("第一副牌拷頁生成第二副牌,共 "+poker2.getCardCount()+" 張:"+poker2.toString());
    
            poker1.removeJoker();
    
            System.out.println("====第一副牌抽走大小王后====");
            System.out.println("第一副牌還有 "+poker1.getCardCount()+" 張:"+poker1.toString());
            System.out.println("第二副牌還有 "+poker2.getCardCount()+" 張:"+poker2.toString());
    
    
            Poker poker3 = new Poker(poker1);
            System.out.println("第三副牌還有 "+poker3.getCardCount()+" 張:"+poker3.toString());
        }
    
    }
    
    • 輸出結果:
      –通過序列化的有手段,同樣也能實現對象的深拷貝

    四、總結

    • java程序進行對象拷貝時,如果對象的類中存在引用類型時,需進行深拷貝
    • 對象拷貝可以通過實現Cloneable接口完成
    • java編程也可仿照 C++程序的拷貝構造函數,實現拷貝構造方法進行對象的複製
    • 通過序列化與反序化手段可實現對象的深拷貝

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

    【其他文章推薦】

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

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

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

    ※回頭車貨運收費標準

  • 本田摘得Google2016年度搜尋冠軍,繽智在其中扮演怎樣的角色?

    本田摘得Google2016年度搜尋冠軍,繽智在其中扮演怎樣的角色?

    繽智的內飾也凸顯着先鋒人群的獨到品味:充滿朝氣的撞色設計,搭配非對稱航空式座艙,這一設計巧思,頗受年輕人青睞,傳達出他們的脫俗格調。懸浮式儀錶台、多變魔術杯架等設計同樣極為出彩,縱享人性化舒適的同時,又展現了他們務實的另一面。

    據Google Trends近日公布的2016年度搜尋清單來看,在汽車品牌這一類別中,位列榜首的並非德系三強,而是一向以技術著稱的日系本田。更值得一提的是,Google今年採用了與去年不同的計算方式,既結合了去年的搜尋次數,亦結合了“最高搜索流量和最持久峰值搜索流量”的數據,由此產生的榜單含金量相比往年來得更高。如此看來,在高含金量的2016榜單之中,日系本田力壓奔馳、特斯拉等一眾豪華德系美系品牌獲此殊榮,更具說服力。

    SUV表現突出,本田關注節節攀升

    那麼,本田奪冠的奧秘又在哪裡?這與本田SUV車型的賣座息息相關。SUV車型無論在國內抑或是全球市場來說均為大熱車型,其中更以價格親民、功能實用的小型SUV尤為突出,而本田旗下的小型SUV—— HR-V(國內繽智)則在全球範圍內掀起了“本田熱”。作為一款全球車型,HR-V在國內有着一個消費者更為熟悉的名字——繽智。繽智採用了全球化的概念設計,其身為SUV的多面實用性、類Coupe的時尚外觀、MpV化的空間配置,都極為貼切的迎合了國內乃至國外車主的需求。

    憑藉全球車型的特殊身份,繽智在國內市場早已擁有較高人氣。以即將過去的2016年為例,廣汽本田繽智穩坐1至11月熱門合資車型銷量桂冠寶座,其中以11月銷量為例,繽智當月終端銷量高達17,485輛,同比增長45.1%,成績喜人的同時又不免讓人深思:繽智憑什麼打動眾多消費者的心?

    先鋒產品力 打造引領潮流的時尚座駕

    面對來自年輕化的受眾群體與瞬息萬變的時尚風向的雙重壓力,單純追逐潮流的設計已顯得有些捉襟見肘,在更多時候,只有引領潮流才能受人追捧持久不衰,而繽智就深知此道。在這個“看顏”的時代,繽智外觀具有天生優勢,更引領着年輕時尚人群對車型外觀的美學要求。

    那麼,繽智的獨特優勢又體現在何處呢?外觀整體以鑽石切面的幾何視覺效果為理念,頗為符合當下主流時尚圈審美;稜角分明的前臉與LED投影式前大燈組的組合極具時尚美學;側身上揚的腰線和隱藏式後門把手設計則頗有幾分Coupe風味,符合時下國際車型設計風向及國際化大眾口味。

    繽智的內飾也凸顯着先鋒人群的獨到品味:充滿朝氣的撞色設計,搭配非對稱航空式座艙,這一設計巧思,頗受年輕人青睞,傳達出他們的脫俗格調;懸浮式儀錶台、多變魔術杯架等設計同樣極為出彩,縱享人性化舒適的同時,又展現了他們務實的另一面。

    同時,繽智作為以黑科技著稱的廣本首款SUV車型,智能配置自然不容小覷,輕鬆滿足受眾需求。SmartEntry智能無匙進入系統輕鬆拉開車門坐進車內,按下一鍵啟動功能,懸浮式儀錶盤隨即點亮,盡顯科技感更充分調動了駕馭的熱情。與此同時,兼具手機屏幕映射功能的智能屏互聯繫統也十分便捷。

    追本溯源,無論一款車其他表現如何,與受眾匹配的動力系統都必須具備,而繽智要迎合的是對速度與駕馭快感追求都較高的年輕時尚人群,要求不可謂不高。繽智用銷量證明了自身的優異,以1.8L i-VTEC發動機打入市場,配合CVT無級變速器與AWD四驅系統,不但滿足了先鋒人士速度的追求又兼顧了良好的通過性。而之後上市的1.5L車型則搭載了地球夢科技發動機,不只滿足了年輕人不同的動力需求,官方給出的6.8L/100km的綜合油耗也滿足了年輕群體經濟環保願望,並由此受到了熱捧。

    廣本里程碑 繽智後市值得期待

    作為廣汽本田首款SUV車型,繽智自上市起便肩負廣汽本田打入國內SUV市場的重任。在上市短短2年時間里,繽智就以累計快高達30萬用戶,一舉成為廣汽本田旗下主打車型,同時更奪得前十一個月合資品牌小型SUV銷量冠軍。在幫助本田力壓其他日系對手成就日系在華銷量第一的同時,繽智早已不負眾望地扛起了廣汽本田SUV市場的大梁。

    在SUV市場如日中天的今天,繽智作為小型SUV的旗幟車型,憑藉潮流先鋒的外觀、品味獨到的內飾、科技感十足的配置與洶湧澎湃的動力,不只滿足了年輕人的多元化購車需求,更肩擔起了引領市場潮流風向的重任,如此看來,繽智獲得銷量冠軍是綜合實力的體現,帶動本田博得Google年度熱搜。

    再看此次Google榜單,奔馳、特斯拉位列二三,相比旗下GLC與Model 3等熱門車型,像國外的HR-V抑或是國內的繽智這樣的小型車型來說,更為親民的價格,同樣豐富的空間,以及不錯的通過性和全面的配置表現,都是其更為大熱的理由。今後也會有越來越多的年輕消費者會選擇經濟性更高的小型SUV,繽智作為熱門小型SUV,熱銷勢將繼續升溫。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

    ※教你寫出一流的銷售文案?

  • 7.98萬起產品力不輸哈弗H6,這款SUV車主是怎樣評價的?

    7.98萬起產品力不輸哈弗H6,這款SUV車主是怎樣評價的?

    最不滿意的地方:起步有點肉,而且這個時候發動機聲音是比較大的。所以感覺上這個發動機實際動力還是比較一般的,而且發動機倉隔音也是比較一般。車主:老兵哥購買車型:北汽幻速S6 2017款 1。5T CVT樂享型裸車購買價:10。

    前言

    作為國內的造車大戶,北汽奉行着“多生孩兒多掙錢”的政策,有注重一般家用市場的北汽紳寶、有注重硬派SUV市場的北京汽車、還有着北汽幻速以及北汽比速這兩個入門品牌。那麼作為一個比較年輕的品牌,北汽幻速的口碑究竟怎樣呢?今天筆者就搜集了幾位北汽幻速S6車主的意見,這款7.98萬起售的緊湊型SUV有着該價位中不俗的競爭力。全系標配的是1.5T發動機,而且有着CVT變速箱作為自動擋的選擇。

    那麼多孩子,打起群架肯定贏

    北汽銀翔幻速S6

    官方指導價:7.98-11.68萬

    編者意見:

    性價比較高,動力表現在同價位中表現比較優秀。不過全系沒能標配ESp車身穩定系統以及电子助力轉向比較可惜。

    車主:BY2000

    購買車型:北汽幻速S6 2017款 1.5T CVT尊享型

    裸車購買價11.68 萬元

    最滿意的地方:整體都比較滿意,但是最滿意的的是價格,性價比很高。能在這個價格買到這樣配置的緊湊型SUV還是不錯的,而且是渦輪增壓發動機。

    最不滿意的地方:起步有點肉,而且這個時候發動機聲音是比較大的。所以感覺上這個發動機實際動力還是比較一般的,而且發動機倉隔音也是比較一般。

    車主:老兵哥

    購買車型:北汽幻速S6 2017款 1.5T CVT樂享型

    裸車購買價:10.68 萬元

    最滿意的地方:乘坐空間,沒有想到這個價格的車還能有着那麼大的空間,滿載的情況下也不是很擁擠。而且後備箱容積也是相當可觀,大天窗還有那麼多的配置,買這款車真的是比較值。

    最不滿意的地方:裝配工藝有待加強,有些部分的縫隙是比較大的,就如尾門的縫隙,看着很掉價,而且方向盤塑料感太強了。

    車主:smg20900

    購買車型:北汽幻速S6 2017款 1.5T CVT尊享型

    裸車購買價:10.68 萬元

    最滿意的地方:外觀,看着更像是二十多萬的SUV。能給人更多的面子,而且在動力方面感覺還是不錯的,一直都可以維持在較低轉速,120km/h時速下轉速也只是2200rpm左右,這個是最為滿意的,所以綜合油耗上也是9L左右,對於一款SUV來說是滿意了。

    最不滿意的地方:儲物空間實在是少得可憐,中間只有一個杯架,不夠用。其次是噪音的問題,不過對於如此便宜的車來說,還是可以接受的。

    編者總結:

    北汽雖然是個歷史悠長的品牌,但事實基本是為別人“代工”,自身在工藝方面以及控製成本方面還是需要向合資學習,所以在做工以及隔音用料上表現一般。不過在主要的發動機上表現卻是相當不錯,雖然渦輪遲滯現象還是有的,但是油耗表現卻是令人信服的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 比埃爾法還大的MPV! 20萬就能買你敢信?

    比埃爾法還大的MPV! 20萬就能買你敢信?

    0T發動機,賬面數據203馬力,峰值扭矩300牛米。與之匹配的則是5速手動變速箱。這個時候你甚至可以直接二擋。雖然開着一台這麼大的手動擋MpV感覺很爽,但是開着MpV 7200干他的人肯定不多,據聞明年將會匹配一款6AT變速箱。而邁特威這邊則是EA888發動機,204馬力,峰值扭矩350牛·米。

    早些年的時候看到MpV總覺得和麵包車差不多,但是現在的MPV越來越豪華,空間上又有轎車無可比擬的優勢,逐漸成為很多公司和家庭的多用途車首選。

    今天我們就為大家帶來福特途睿歐與大眾邁特威的對比評測。

    空間

    既然是MpV,那麼空間肯定是很重要的部分,你說轎車有多長軸距,在MpV面前全部都是渣渣 。此次試駕的兩台車均為7座車型。坐姿足夠舒服,坐墊支撐到位,腿部空間?自己看!

    再到後面,福特途睿歐的第三排座椅擁有三個可獨立調整的靠背,而邁特威第三排的中間座椅就稍微吃虧了,但是可以抽出一個扶手,兩個人乘坐的時候比較舒適。

    不過邁特威最爽的用法應該是這樣。

    儲物空間

    在以往轎車的評測中,我們會到處找放水杯的地方,但是在MPV上真的就是多擔心了,兩台車的空間都是可以用海量來形容,圖中皆為2L大水瓶!!!

    福特途睿歐的杯架

    邁特威的杯架

    除此之外邁特威座椅下方空間還有神奇的儲物空間。途睿歐取而代之的則是獨立的空調出風口。

    一般7座SUV如果使用第三排座椅時,後備箱基本上沒有什麼空間,來到MpV上。情況則有所不同。如有需要福特途睿歐還可以將第三排座椅翻起,除了可以開去買菜,心情好還可以開去賣菜。

    邁特威的後備箱空間也不俗,不過座椅只能向前移,不能繼續將座椅翻起來。

    動力

    雖然一台MpV你不指望它能開多快,但是動力就像存款一樣,可以不用,但不能沒有。途睿歐搭載的是福特ECOboost的2.0T發動機,賬面數據203馬力,峰值扭矩300牛米。與之匹配的則是5速手動變速箱。這個時候你甚至可以直接二擋。

    雖然開着一台這麼大的手動擋MpV感覺很爽,但是開着MpV 7200干他的人肯定不多,據聞明年將會匹配一款6AT變速箱。

    而邁特威這邊則是EA888發動機,204馬力,峰值扭矩350牛·米。加上DQ500的7速雙離合變速箱,動力是足,但是感覺變速箱是刻意放慢了動作以尋求一個平順的效果,大腳油門並不會直接降檔,而是會選擇拉高發動機轉速,並且傳遞到車廂的發動機聲音較大。

    配置對比

    福特途睿歐配上了這個級別少有的後排天窗。

    邁特威則是側滑的玻璃窗。

    邁特威兩側都用上了電動門,這一點福特途睿歐上略有遺憾。

    舒適性對於MpV同樣重要,福特途睿歐用上了空氣懸挂,這也是福特專門為中國研發的,對於細碎過濾相當到位,國內的這套空氣懸挂是標配,而在英國也只有改款后的頂配車型才配備。

    邁特威則是自家的DCC動態懸挂,兩種懸挂相比各有特色。不過有一個細節方面則是搞不懂大眾,這個最常用的扶手,調節起高低真的有點反人類。

    總結

    邁特威和福特途睿歐相比各有優勢,但是從價格看來,一台邁特威的價格能夠買兩台福特途睿歐了,邁特威(41.88-54.98萬);福特途睿歐(17.69-20.39萬)。質感方面邁特威會更好一些,但是福特途睿歐在如此價位還能取得如此不俗的成績實屬驚人。大家會怎麼選呢?

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

    【其他文章推薦】

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

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

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

    ※回頭車貨運收費標準

  • Vue —— 精講 VueX (1)

    Vue —— 精講 VueX (1)

    大綱

    這一講我們最主要的就是學習vue中的數據管理VueX,這個是一個大殺器

    demo源代碼地址 https://github.com/BM-laoli/BMlaoli-learn-VueX

    一、回顧一些Promise相關的東西

    Promise 有幾個比較重要的方法,最重要的還是有一個叫做all的方法,這個也是非常的強大的

    假設我們目前要求,希望能按順序的拿到先後的兩個ajax那麼我應該怎麼處理呢

    Promse.all( [
    new Promose( ( resolve,rejcet ) => {
            $.ajax({
                url:'xxxx',
                data:'xxxx',
                sucess:(res) => {
                    resolve(res)
                }
            })
            $.ajax({
                url:'xxxx',
                data:'xxxx',
                sucess:(res) => {
                    resolve(res)
                }
            })
        })
    
    ]).then( results => {
        consel.log(results)
        // 這樣拿到的就是一個數組了, 先後的順序就是裏面的值
    } )
    

    注意啊這裏對promise的深入的解釋說明

    1. 首先我們的兩個回調resolve 還有reject注意啊,
    這兩個回調回調函數是 在傳入的時候定義的,但是調用是在promse里調的!這兩個參數是函數!!函數!!回調函數!
    
    

    一、概念

    Vue官方介紹
    絕大多數的管方都非常喜歡用概念來解釋概念,這就有點難搞了,我這個概念的都不懂,你又給我搞另一個概念
    實際上那個Vuex就是一個大管家,統一進行管理,全局的單例模式

    1.最通俗的解釋

    Vuex實際上就是一個 用來放 一些組件共享的數據的,實際上這可能是是下面這些情況

    1. 登錄
      假設我們目前有50+頁面。我們都每一個頁面都要發送接口請求而且這些請求需要token,那麼如果我是登錄的,我就需要在每一個頁面拿到我的登錄token這樣就造成了數據的傳來傳去非常麻煩,如果我們有一個公共的地方來放這些東西就好了

    2. 購物車。收藏
      也會有這種組件之間打出傳值的情況發生,那麼我如何管理這些東西呢,這個就是一個問題

    綜上所述,我們需要使用Vuex*

    二、如何入門的使用

    2.簡單的使用

    這裏假設有這樣的一個需求:我們目前有兩個組件App.vue 還有BMlaoli.vue 我呢,他們之間有層級的關係,app裏面有一個變量叫做contuend 我希望我在app裏面對countend的操作能夠動態的傳遞到我們的BMlaoli里,而且不使用父子組件傳值,那麼我們如何做呢?親看下面講演

    1. 首先我們需要有兩個組件
      他們都是最基礎的樣子

    App

    <template>
      <div id="app">
        <h1> 我是vueapp </h1>
      </div>
    </template>
    
    <script>
    
    export default {
      name: 'App',
      components: {
      }
    }
    </script>
    
    <style>
    </style>
    
    

    BMlaoli

    
    <template>
        <div>
            <h1>我是bm界面</h1>
        </div>
    </template>
    
    <script>
        export default {
            
        }
    </script>
    
    <style lang="sass" scoped>
    
    </style>
    
    1. app的業務邏輯
    <template>
      <div id="app">
        <p>{{contuned}}</p>
        
          <button @click="contuned ++" >+</button>
          <button @click="contuned --" >-</button>
    
      </div>
    </template>
    
    <script>
    
    import bmlao from '@/components/Bmlaoli';
    
    export default {
      name: 'App',
      components: {
        bmlao,
      },
      data() {
        return {
          contuned: 100
        }
      },
    }
    </script>
    
    <style>
    </style>
    
    

    但是問題來了,我目前希望你們在app裏面做的更改可以反映到我的Bm組件里,而且不通過父子組件的方式,那麼我該怎麼做呢?實際上非常的簡單

    這個時候我們就需要一個 ‘第三者來處理這個東西’,這個第三者就是這個Vuex。

    1. vueX的引入

    實際上,如果你有手動的安裝使用配VueRouter的經驗的話。這Vuex也是差不多的都是一樣的使用方法

    第一步:npm install vuex
    第二步:創建一個文件夾sote里寫一個index.js
    第三部:在index裏面安裝
    第四部:在main里掛載就好了

    index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    // 安裝
    Vue.use(Vuex)
    
    // 使用
    const store = new Vuex.Store({
        state:{},
        mutations: {
        },
        actions:{},
        getters:{},
        modules:{}
    
    })
    
    // 倒出
    export default store
    
    
    

    main.js

    import Vue from 'vue'
    import App from './App.vue'
    
    // 導入
    import Store from './store'
    
    Vue.config.productionTip = false
    
    // 掛載
    new Vue({
      Store,
      render: h => h(App),
    }).$mount('#app')
    
    

    非常的簡單

    1. app里的業務邏輯
    <template>
      <div id="app">
        <p>{{ $store.state.contuned }}</p>
        
          <button @click="$store.state.contuned ++" >+</button>
          <button @click="$store.state.contuned --" >-</button>
        
        <h1>------bmlaoli的界面--------</h1>
    
        <bmlao></bmlao>
    
      </div>
    </template>
    
    <script>
    
    import bmlao from '@/components/Bmlaoli';
    
    export default {
      name: 'App',
      components: {
        bmlao,
      },
      data() {
        return {
          // contuned: 100
        }
      },
    }
    </script>
    
    <style>
    </style>
    
    

    三、正確的操作state的方式

    1.需要注意的地方

    $store.state.contuned

    需要非常說的就是 請你不要這樣去修改vuex里的值,而是通過如下的方式去修改,詳細見官方api說明

    1. 概述我們的更改邏輯
      view視圖提交(Dispatch) —-> actions處理異步操作(commit) —–> Muations 記錄你的修改 ,方便以後追蹤(Mutate) —–> state修改(render)

    2. 代碼邏輯
      /state/index.js

        state:{
            contuned:1000
        },
        mutations: {
            increment(state){
                state.contuned++
            },
            decrement(state){
                state.contuned--
            },
        },
        actions:{},
        getters:{},
        modules:{}
    

    /app.vue

    <template>
      <div id="app">
        <p>{{ $store.state.contuned }}</p>
        
          <button @click="additon" >+</button>
          <button @click="subraction" >-</button>
        
        <h1>------bmlaoli的界面--------</h1>
    
        <bmlao></bmlao>
    
      </div>
    </template>
    
    <script>
    
    import bmlao from '@/components/Bmlaoli';
    
    export default {
      name: 'App',
      components: {
        bmlao,
      },
      data() {
        return {
          // contuned: 100
        }
      },
      methods: {
        additon() {
          this.$store.commit('increment')
        },
        subraction() {
          this.$store.commit('decrement')
          
        },
      },
    }
    </script>
    
    <style>
    </style>
    
    
    
    1. 除了使用this.$store.state.XXX或缺vuex的數據之外,我們還有一種方法,也是開發和工作中,比較常見的東西,那就是使用map進行各種數據的映射,它可以映射全部的vuex裏面的東西
    // 假設我們現在就使用map把東西數據,state裏面的東西,映射到我們的computed裏面
    improt { mapState } form 'vuex
    computed {
          ...mapState( ['XXX'] )
             // 但是我們不推薦使用上面得方式,我們更加推薦使用對象器別名的方式
          ...mapState( { xCount:'Count' } )      
    }
    
    ===> 這樣你就得到這些State ,除了state之外,其它的mutation 還有getter也是一樣的原理
    
    1. 深入立即map的映射原理,

    一個優秀的程序員,不應該只是停留在會用的層面,還應該靈活的掌握其中的原理,只有掌握了原理,才能做到行雲流水的開發.工具永遠只是工具,只有自己變強才是王道

    ====> 我們一點點的分析,
    // 1. 首先我們得computed需要接受函數
    computed:{
          XXXX:() => {  return this.$stroe.state.XXX }
     }
    // 2. 我們要寫一個方法mapState
    function mapState( array ){
         let obj = {}
         array.forEach( stateKey => { obj[stateKey] = () => this.$store.state[stateKey] }  )
         return obj
    }
    // 以上就是內部的實現原理
    

    這樣我們就能開發者工具追綜這些東西的變化了

    四、核心概念解讀

    vueX中有五個核心

    1.單一狀態樹

    1. 管理系統 現實生活中的例子
      我們先來舉一個例子,在我們國家一個人有很多的信息會被記錄到檔案管理的各個部門,車貸房貸,身份證 ,戶口 ,結婚登記,這些信息都分佈式的存放在各個局,地產局,戶口部門……,這樣對於我們的人 來說, 我們的數據來來源就是多樣的,多數據源,但是這樣有問題,就是一起管理的時候是不好管理的,你可能需要去這個地方蓋章,去哪個地方改造,如果不通過又要重新回來蓋章,XXXX太麻煩了。
    2. vuex的管理邏輯
      在我們的vue中確確實實 ,你可以new 多個Vuex但是,我們是不推薦的,因為這樣管理起來就會非常的麻煩,我們的vuex推薦是 只使用一個vuex來管理共享的數據源,這個設計理念就是;單一數據源(也叫單一狀態樹)

    2.getter

    這個東西類似於計算屬性
    有時候我們需要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數:
    高階函數 ,返回函數的調用

    1. 需求,還是原來的案例,我希望我獲取的contuned的平方

    當然了,你這樣也是可以的

      <h2>{{ $store.state.contuned * $store.state.contuned }}</h2>
    

    但是很low 是不啦,如果你要寫很多很多的複雜邏輯操作,那不就涼涼了嗎,所以這裏引申出我們的getter,字面理解就是獲取的時候,對數據做一些手腳,那麼我們看看如何使用

    1. 明確一下,我們的操作基本上都是在我們的vuex文件裏面進行的

    在getter裏面搞事情
    store/index.js

    
    import Vue from 'vue'
    import Vuex from 'vuex'
    // 安裝
    Vue.use(Vuex)
    
    // 使用
    const store = new Vuex.Store({
        state:{
            contuned:1000
        },
        mutations: {
            increment(state){
                state.contuned++
            },
            decrement(state){
                state.contuned--
            },
        },
        actions:{},
        getters:{
            powerCounter(state){
                return state.contuned * state.contuned 
            }
        },
        modules:{}
    
    })
    
    // 倒出
    export default store
    
    

    使用的時候就非常簡單了
    /bmlaoli.vue

      <h2>{{ $store.getters.powerCounter }}</h2>
    

    現在我們又有了另一個需求,如果我想傳遞參數,怎麼辦,我希望我過濾出一些數據,而且我們希望我們是指定條件的過濾
    這裏就涉及到我們的傳遞參數的問題了
    store/index.js

    
      fliter(state,getters){
        console.log(getters)//這裏的getters實際上就是你的整個外面的getters對象 
    
      // 如果你要傳遞參數,你只能返回函數的調用
          return age => {
            state.students.filter( s => s.age >= age )
          }
        }
    
    

    /bmlaoli.vue

    原數據
     <h2>{{ $store.getters.students }}</h2>
    過濾之後
     <h2>{{ $store.getters.fliter(40) }}</h2>
    

    3.mutation

    vuex唯一更新狀態的方式,就是在這裏,如果你要更改數據,vuex唯一的更改方式就是 mutation

    3.1 概念

    事件類型(函數名)
    回調函數(回調函數,具體的業務代碼)

    mutations: {
    //      increment 事件類型
    // (state){ 回調函數
                // state.contuned++
            // },
            increment(state){
                state.contuned++
            },
    
            decrement(state){
                state.contuned--
            },
        },
    

    3.2 傳遞參數payload負載

    1. 單個參數
    2. 多參數(傳遞對象)

    需求:我們希望點擊更改狀態的時候的時候可傳入參數
    /sotre/index.js

    
       mutations: {
            increment(state){
                state.contuned++
            },
            decrement(state){
                state.contuned--
            },
            incrementCour(state,palyload){
                consle.log(palyload)//拿到了一個傳遞過來的對象
            }
        },
    
    

    bmlaoliu.vue3

    addcCount(parmas){
    this.$sore.commit( 'incrementCour' ,palyload)
    }
    

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

    【其他文章推薦】

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

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

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

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

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

    ※教你寫出一流的銷售文案?

  • javascript 面向對象學習(三)——this,bind、apply 和 call

    this 是 js 里繞不開的話題,也是非常容易混淆的概念,今天試着把它理一理。

    this 在非嚴格模式下,總是指向一個對象,在嚴格模式下可以是任意值,本文僅考慮非嚴格模式。記住它總是指向一個對象對於理解它的意義很重要。this 在實際使用中,大致分為以下幾種情況:

    1. 函數作為對象的方法調用時,this 指向調用該函數的對象

    var obj = {
        name: 'jack',
        getName: function() {
            console.log(this === obj) // true
            console.log(this.name)  // jack
        }
    }
    obj.getName()

    這個應該很好理解,不多說了。

    2. 函數作為普通函數被調用時,this 指向全局對象。在瀏覽器中,全局對象是window。

    var name = 'global'
    function getName() {
        console.log(this === window) // true
        console.log(this.name) // global
    }
    getName()

    我的理解是上面的代碼可以改寫為

    window.name = 'global'
    window.getName = function() {
        console.log(this === window) // true
        console.log(this.name) // global
    }
    window.getName()

    這樣其實與情況1是一樣的,相當於函數作為對象的方法調用,只不過這裏的對象是全局對象。

    《Javascript 設計模式與開發實踐》一書中有個例子如下:

    window.name = 'globalName';
    var myObject = {
        name: 'seven',
        getName: function(){
            return this.name
        } 
    }
    
    var getName = myObject.getName
    console.log(getName())  // globalName

    getName 是定義在myObject 對象中的方法,在調用getName 方法時,打印出的卻是全局對象的name,而不是myObject對象的name,這再次證明了 this 並非指向函數被聲明時的環境對象,而是指向函數被調用時的環境對象

    3. 函數作為構造函數調用時,指向構造出的新對象

    function Person(name) {
        this.name = name  
    }
    
    var jack = new Person('Jack')
    console.log(jack.name) // Jack
    var rose = new Person('Rose')
    console.log(rose.name) // Rose

    這裏創建了兩個不同名字的對象,打印出的name也是不一樣的,說明構造函數的 this 會根據創建對象的不同而變化。需要注意的是,如果構造函數里返回了一個Object類型的對象,那麼this會指向這個對象,而不是利用構造函數創建出的對象。我們在構造函數一章里也提到過,new 操作符所做的最後一步就是返回新對象,而如果我們顯式地返回一個對象,就會覆蓋這步操作,this也就不再指向新對象。

    4. 函數作為事件處理函數調用時,指向觸發事件的元素

    document.getElementById("myBtn").addEventListener("click", function(e){
        console.log(this === e.currentTarget) // true
    });

    5. 箭頭函數

    由於箭頭函數沒有this,它的 this 是繼承父執行上下文裏面的 this。執行上下文後面再討論,現在只要知道簡單對象(非函數)是沒有執行上下文的。

    var obj = {
        name:  'obj',
        getName: function() {
    console.log(this) // 執行上下文里的 this
    return (()=>{ console.log(this.name) }) } } var fn = obj.getName() fn() // obj

    按照情況2來處理的話,this 指向全局對象,應該輸出 undefined,結果並不是。與普通函數不同,箭頭函數的 this 是在函數被聲明時決定的,而不是函數被調用時。在這裏,父執行上下文是 getName 函數,也就繼承了 getName 的 this,即 obj。

    利用 bind、apply、call 改變 this 指向

    bind、apply、call 都是定義在 Function 原型對象上的方法,所有函數對象都能繼承這個方法,三者都能用來改變 this 指向,我們來看看它們的聯繫與區別。

    function fn() {
        console.log(this.name)
    }
    
    // bind
    var bindfn = fn.bind({name: 'bind'})
    bindfn() // bind // apply
    fn.apply({name: 'apply'}) // apply // call
    fn.call({name: 'call'}) // call

    我們定義了一個函數fn,然後分別調用了它的 bind、apply、call 方法,並傳入一個對象參數,通過打印出的內容可以看到 this 被綁定到了參數對象上。bind 似乎有些不同,多了一步 bindfn() 調用,這是因為 bind 方法返回的是一個函數,不會立即執行,而調用 apply 和 call 方法會立即執行。

    下面再來看一下 fn 函數存在參數的情況:

    function fn(a, b, c) {
        console.log(a, b, c)
    }
    
    var bindfn = fn.bind(null, 'bind');
    bindfn('A', 'B', 'C');           // bind A B
    
    fn.apply(null, ['apply', 'A']) // apply A undefined
    
    fn.call(null, 'call', 'A');  // bind A undefined

    bindfn 打印出的結果是fn調用bind方法時的傳遞的參數加上bindfn傳遞的參數,參數 ‘C’ 被捨棄掉了。調用 apply 和 call 方法打印出的則是傳遞給它們的參數,不一樣的是,apply 的參數是一個數組(或類數組),call 則是把參數依次傳入函數。這時候再看它們的定義應該會好理解很多:

    bind() 方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定為 bind() 的第一個參數,而其餘參數將作為新函數的參數,供調用時使用。

    apply() 方法調用一個具有給定 this 值的函數,以及作為一個數組(或類數組對象)提供的參數。

    call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。

    我們可以利用它們來借用其他對象的方法。已知函數的參數列表 arguments 是一個類數組對象,比如上例中函數 fn 的參數 a, b, c,因為它不是一個真正的數組,不能調用數組方法,這時借用 apply/call 方法(bind 也可以,就是用得比較少)將 this 指向 arguments 就能借用數組方法:

    (function(){
        Array.prototype.push.call(arguments, 'c')
        console.log(arguments) // ['a', 'b', 'c']
    })('a','b')

    值得一提的是,push 方法並不是只有數組才能調用,一個對象只要滿足1.可讀寫 length 屬性;2.對象本身可存取屬性. 就可以利用 call / apply 調用 push 方法。

     

    參考:

    《Javascript 設計模式與開發實踐》

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this

    http://www.imooc.com/article/80117

    https://blog.csdn.net/weixin_42519137/article/details/88053339

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

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • Linux系統如何設置開機自動運行腳本?

    Linux系統如何設置開機自動運行腳本?

    大家好,我是良許。

    在工作中,我們經常有個需求,那就是在系統啟動之後,自動啟動某個腳本或服務。在 Windows 下,我們有很多方法可以設置開機啟動,但在 Linux 系統下我們需要如何操作呢?

    Linux 下同樣可以設置開機啟動,但可能需要我們敲一些命令(可能也有 UI 界面的設置方法,但我不熟,我更多是玩命令)。下面我們就介紹三種簡單但可行的開機啟動設置方法。

    方法一:修改 /etc/rc.d/rc.local 文件

    /etc/rc.d/rc.local 文件會在 Linux 系統各項服務都啟動完畢之後再被運行。所以你想要自己的腳本在開機后被運行的話,可以將自己腳本路徑加到該文件里。

    但是,首先需要確認你有運行這個文件的權限。

    $ chmod +x /etc/rc.d/rc.local
    

    為了演示,我們創建了一個腳本,當它被執行之後,將在家目錄下寫入有特定信息的文件。

    $ vim auto_run_script.sh
    
    #!/bin/bash
    date >> /home/alvin/output.txt
    hostname >> /home/alvin/output.txt
    

    保存退出后,再給它賦予可執行權限:

    $ chmod +x auto_run_script.sh
    

    然後,我們再將腳本添加到 /etc/rc.d/rc.local 文件最後一行:

    $ vim /etc/rc.d/rc.local
    
    /home/alvin/auto_run_script.sh
    

    接下來,我們就可以試試效果了。直接重啟系統就可以了:

    $ sudo reboot
    

    重啟之後,就會在家目錄下看到腳本執行的結果了。

    方法二:使用 crontab

    大家知道,crontab 是 Linux 下的計劃任務,當時間達到我們設定的時間時,可以自動觸發某些腳本的運行。

    我們可以自己設置計劃任務時間,然後編寫對應的腳本。但是,有個特殊的任務,叫作 @reboot ,我們其實也可以直接從它的字面意義看出來,這個任務就是在系統重啟之後自動運行某個腳本。

    那它將運行的是什麼腳本呢?我們如何去設置這個腳本呢?我們可以通過 crontab -e 來設置。

    $ crontab -e
    
    @reboot /home/alvin/auto_run_script.sh
    

    然後,直接重啟即可。運行的效果跟上面類似。

    方法三:使用 systemd 服務

    以上介紹的兩種方法,在任何 Linux 系統上都可以使用。但本方法僅適用於 systemd 系統。如何區分是不是 systemd 系統?很簡單,只需運行 ps aux 命令,查看 pid 為 1 的進程是不是 systemd 。

    為了實現目的,我們需要創建一個 systemd 啟動服務,並把它放置在 /etc/systemd/system/ 目錄下。

    我們創建的 systemd 啟動服務如下。請注意,這時後綴是 .service ,而不是 .sh

    $ vim auto_run_script.service
    
    [Unit]
    Description=Run a Custom Script at Startup
    After=default.target
    
    [Service]
    ExecStart=/home/alvin/auto_run_script.sh
    
    [Install]
    WantedBy=default.target
    

    從服務的內容可以看出來,我們最終還是會調用 /home/alvin/auto_run_script.sh 這個腳本。

    然後,我們再把這個腳本放置在 /etc/systemd/systerm/ 目錄下,之後我們再運行下面兩條命令來更新 systemd 配置文件,並啟動服務。

    $ systemctl daemon-reload
    $ systemctl enable auto_run_script.service
    

    萬事俱備之後,我們就可以重啟系統啦。

    $ reboot
    

    公眾號:良許Linux

    有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

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

    【其他文章推薦】

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

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

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

    ※回頭車貨運收費標準

  • 【譯】Introducing YARP Preview 1

    1 YARP

        YARP是一個項目,用於創建反向代理服務器。它開始於我們注意到來自微軟內部團隊的一系列問題。他們要麼為其服務構建反向代理,要麼詢問 API 和用於構建 API 的技術。因此我們決定讓他們聚在一起開發一個通用解決方案,該解決方案形成了YARP。

        YARP是一個反向代理工具包,用於使用 ASP.NET 和 .NET 中的基礎設施在 .NET 中構建代理服務器。YARP 的主要區別是,它被設計為易於自定義和調整,以滿足不同方案的特定需求。YARP 插入ASP.NET管道以處理傳入請求,然後它擁有自己的子管道,用於執行將請求代理到後端服務器的步驟。客戶可以添加其他module,或根據需要更換常備module。

        隨着其開發已基本到位,我們製作了 YARP 的第一個正式版本(Preview 1),以便更好地協作並獲得反饋。

    2 Preview 1 是什麼

      • 核心代理的基礎結構
      • 基於配置的路由定義
      • 擴展性的管道模型
      • Forwarded標頭(硬編碼)
      • 目標 .NET Core 3.1 和 .NET Core 5

    3 Preview 1 不包括

      • 會話親和性(又稱會話保持)
      • Forwarded標頭(可配置)
      • 基於代碼的路由定義和預請求路由
      • 指標和日誌
      • 性能調整
      • 連接篩選

    4 快速開始

    Step 01 下載.net framework

        YARP 適用於 .NET Core 3.1 或 .NET 5 Preview 4(或更高版本)。

    Step 02 創建一個ASP.NET Core項目

    Step 03 打開項目,添加引用,確保其包含

    <PropertyGroup>
        <TargetFramework>netcoreapp5.0</TargetFramework>
    </PropertyGroup>

      和

    <ItemGroup>
        <PackageReference Include="Microsoft.ReverseProxy" Version="1.0.0-preview.1.*" />
    </ItemGroup>

    Step 04 Startup.cs

      YARP 當前使用配置文件來定義代理的路由和終結點。在ConfigureServices方法中加載。

    public IConfiguration Configuration { get; }
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddReverseProxy()
            .LoadFromConfig(Configuration.GetSection("ReverseProxy"));
    }

      Configure方法定義ASP.NET的請求處理管道。反向代理插入到ASP.NET的終結點路由,然後具有其自己的代理子管道。在這裏,可以添加代理管道模塊(如負載均衡)來自定義請求的處理。

    /// <summary>
    /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    /// </summary>
    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
    
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapReverseProxy(proxyPipeline =>
            {
                proxyPipeline.UseProxyLoadBalancing();
            });
        });
    }

    Step 05 配置

      YARP 的配置定義在appsettings.json中:

    "ReverseProxy": {
        "Routes": [
          {
            "RouteId": "app1",
            "BackendId": "backend1",
            "Match": {
              "Methods": [ "GET", "POST" ],
              "Host": "localhost",
              "Path": "/app1/"
            }
          },
          {
            "RouteId": "route2",
            "BackendId": "backend2",
            "Match": {
              "Host": "localhost"
            }
          }
        ],
        "Backends": {
          "backend1": {
            "LoadBalancing": {
              "Mode": "Random"
            },
            "Destinations": {
              "backend1_destination1": {
                "Address": "https://example.com:10000/"
              },
              "backend1_destination2": {
                "Address": "http://example.com:10001/"
              }
            }
          },
          "backend2": {
            "Destinations": {
              "backend2_destination1": {
                "Address": "https://example.com:10002/"
              }
            }
          }
        }
      }
      • Backends:請求可以路由到的服務器群集。
      • Destinations:是用於指標、日誌記錄和會話保持的標識符。
      • Address:URL前綴(基地址)
      • Routes:根據請求的各個方面(如主機名、路徑、方法、請求標頭等)將傳入請求映射到後端群集。路由是有序的,因此,需要首先定義 app1 路由,因為 route2 將作為尚未匹配的所有路徑的 catchall。

      好啦,先介紹到這裏。

    原文鏈接

      https://devblogs.microsoft.com/dotnet/introducing-yarp-preview-1/?utm_source=vs_developer_news&utm_medium=referral

     

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

    【其他文章推薦】

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

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

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

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

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

    ※教你寫出一流的銷售文案?