標籤: USB CONNECTOR

  • 利用Tu Share獲取股票交易信息,c#實現

    利用Tu Share獲取股票交易信息,c#實現

     一、什麼是Tu Share

    Tushare是一個免費、開源的python財經數據接口包。主要實現對股票等金融數據從數據採集清洗加工 到 數據存儲的過程,用戶可以免費(部分數據的下載有積分限制)的通過它提供的財經接口獲取股票交易、期貨等財經信息,功能非常強大。該接口和直接到各財經網站爬數據相比,最大的優勢就是快,去傳統財經網站爬數據,好多關鍵性的股票信息只能一隻股一隻股爬,而Tu Share的API,一個調用可以獲得一天的全部數據,速度差了好幾個數量級。另外一方面各財經網站的接口的API沒有對外文檔化,隨時可能變化,而Tu Share的API有正式的文檔化相對比較穩定。

    二、如何註冊

    該網站使用積分制來控制數據的訪問權限,如果想要訪問數據,先要到下面這個網址完成註冊,https://tushare.pro/register。註冊完成后,可以需要到個人主頁中拷貝Token,這個Token會在以後的訪問中用到,步驟如下

    1、登錄成功后,點擊右上角->個人主頁

    2、 在“用戶中心”中點擊“接口TOKEN”

     

    3、 可以點擊右側複製按鈕複製token

    三、Http API說明

    Tushare HTTP數據獲取的方式,採用了post的機制,通過提交JSON body參數,就可以獲得您想要的數據。具體參數說明如下:

    輸入參數

    api_name:接口名稱,比如stock_basic

    token :用戶唯一標識,可通過登錄pro網站獲取

    params:接口參數,如daily接口中start_date和end_date

    fields:字段列表,用於接口獲取指定的字段,以逗號分隔,如”open,high,low,close”

    輸出參數

    code: 接口返回碼,2002表示權限問題。

    msg:錯誤信息,比如“系統內部錯誤”,“沒有權限”等

    data:數據,data里包含fields和items字段,分別為字段和數據內容

    四、c#(.net core)實現

    1、在Visual Studio中安裝下面幾個包:Microsoft.Extensions.Http、Newtonsoft.Json

    2、封裝方法,實現對REST web service的調用

    public interface IHttpClientUtility
    {
         string HttpClientPost(string url, object datajson);
    }
    public class HttpClientUtility : IHttpClientUtility
        {
            
    
            public HttpClientUtility()
            {
                
            }
            public  string HttpClientPost(string url, object datajson)
            {
                using (HttpClient httpClient = new HttpClient()) //http對象
                {
                    httpClient.DefaultRequestHeaders.Accept.Clear();
                    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    httpClient.Timeout = new TimeSpan(0, 0, 5);
                    //轉為鏈接需要的格式
                    HttpContent httpContent = new JsonContent(datajson);
                    //請求
                    HttpResponseMessage response = httpClient.PostAsync(url, httpContent).Result;
                    if (response.IsSuccessStatusCode)
                    {
                        Task<string> t = response.Content.ReadAsStringAsync();
                        return t.Result;
                    }
                    throw new Exception("調用失敗");
                }
                                                                           
            }
        }

     

        public class JsonContent : StringContent
        {
            public JsonContent(object value)
                : base(JsonConvert.SerializeObject(value), Encoding.UTF8,
                    "application/json")
            {
            }
    
            public JsonContent(object value, string mediaType)
                : base(JsonConvert.SerializeObject(value), Encoding.UTF8, mediaType)
            {
            }
        }

    3、封裝對Tu Share API的調用

    public  class TuShareUtility 
        {
            private IHttpClientUtility _httpClientUtility;
            private string _url = "http://api.waditu.com/";
            
            public TuShareUtility(IHttpClientUtility httpClientUtility)
            {
                _httpClientUtility = httpClientUtility;
            }
    
            /// <summary>
            /// 調用TuShare API
            /// </summary>
            /// <param name="apiName"></param>
            /// <param name="parmaMap"></param>
            /// <param name="fields"></param>
            /// <returns></returns>
            public  DataTable GetData(string apiName,Dictionary<string,string> parmaMap,params string[] fields)
            {
                var tuShareParamObj=new TuShareParamObj(){ ApiName = apiName ,Params = parmaMap,Fields = string.Join(",",fields)};
                //做Http調用
                var result=_httpClientUtility.HttpClientPost(_url, tuShareParamObj);
                //將返回結果序列化成對象
                var desResult=JsonConvert.DeserializeObject<TuShareResult>(result);
                //如果調用失敗,拋出異常
                if(!string.IsNullOrEmpty(desResult.Msg))
                    throw new Exception(desResult.Msg);
                //返回結果分成兩部分,一部分是列頭信息,另一部分是數據本身,用這兩部分數據可以構建DataTable
                DataTable dt = new DataTable();
                foreach (var dataField in desResult.Data.Fields)
                {
                    dt.Columns.Add(dataField);
                }
    
                foreach (var dataItemRow in desResult.Data.Items)
                {
                    var newdr=dt.NewRow();
                    for (int i=0;i< dataItemRow.Length;i++)
                    {
                        newdr[i] = dataItemRow[i];
                    }
    
                    dt.Rows.Add(newdr);
                }
                return dt;
            }
    
            private class TuShareParamObj
            {
                [JsonProperty("api_name")]
                public string ApiName { get; set; }
    
                [JsonProperty("token")]
                public string Token { get; } = "****************";//你的Token
    
                [JsonProperty("params")]
                public Dictionary<string, string> Params { get; set; }
    
                [JsonProperty("fields")]
                public string Fields { get; set; }
            }
    
            private class TuShareData
            {
                [JsonProperty("fields")]
                public string[] Fields { get; set; }
    
                [JsonProperty("items")]
                public string[][] Items { get; set; }
            }
    
            private class TuShareResult
            {
                [JsonProperty("code")]
                public string Code { get; set; }
    
                [JsonProperty("msg")]
                public string Msg { get; set; }
    
                [JsonProperty("data")]
                public TuShareData Data { get; set; }
            }
        }

    4、調用示例

    獲得日線行情,整個過程1秒左右,返回6月24日,股票相關交易信息,代碼如下,(該網站的其它接口定義可以到https://tushare.pro/document/2查看)

    var tuShareUtility=new TuShareUtility();
    Dictionary<string, string> p = new Dictionary<string, string>();
    p["trade_date"] = "20200624";
    var table = tuShareUtility.GetData("daily", p, "");

    返回如下結果

    返回字段說明

    名稱 類型 描述
    ts_code str 股票代碼
    trade_date str 交易日期
    open float 開盤價
    high float 最高價
    low float 最低價
    close float 收盤價
    pre_close float 昨收價
    change float 漲跌額
    pct_chg float 漲跌幅 (未復權,如果是復權請用 通用行情接口 )
    vol float 成交量 (手)
    amount float 成交額 (千元)

     

     

     

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

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

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • SpringSceurity(5)—短信驗證碼登陸功能

    SpringSceurity(5)—短信驗證碼登陸功能

    SpringSceurity(5)—短信驗證碼登陸功能

    有關SpringSceurity系列之前有寫文章

    1、SpringSecurity(1)—認證+授權代碼實現

    2、SpringSecurity(2)—記住我功能實現

    3、SpringSceurity(3)—圖形驗證碼功能實現

    4、SpringSceurity(4)—短信驗證碼功能實現

    一、短信登錄驗證機制原理分析

    了解短信驗證碼的登陸機制之前,我們首先是要了解用戶賬號密碼登陸的機制是如何的,我們來簡要分析一下Spring Security是如何驗證基於用戶名和密碼登錄方式的,

    分析完畢之後,再一起思考如何將短信登錄驗證方式集成到Spring Security中。

    1、賬號密碼登陸的流程

    一般賬號密碼登陸都有附帶 圖形驗證碼記住我功能 ,那麼它的大致流程是這樣的。

    1、 用戶在輸入用戶名,賬號、圖片驗證碼後點擊登陸。那麼對於springSceurity首先會進入短信驗證碼Filter,因為在配置的時候會把它配置在
    UsernamePasswordAuthenticationFilter之前,把當前的驗證碼的信息跟存在session的圖片驗證碼的驗證碼進行校驗。
    
    2、短信驗證碼通過後,進入 UsernamePasswordAuthenticationFilter 中,根據輸入的用戶名和密碼信息,構造出一個暫時沒有鑒權的
     UsernamePasswordAuthenticationToken,並將 UsernamePasswordAuthenticationToken 交給 AuthenticationManager 處理。
    
    3、AuthenticationManager 本身並不做驗證處理,他通過 for-each 遍歷找到符合當前登錄方式的一個 AuthenticationProvider,並交給它進行驗證處理
    ,對於用戶名密碼登錄方式,這個 Provider 就是 DaoAuthenticationProvider。
    
    4、在這個 Provider 中進行一系列的驗證處理,如果驗證通過,就會重新構造一個添加了鑒權的 UsernamePasswordAuthenticationToken,並將這個
     token 傳回到 UsernamePasswordAuthenticationFilter 中。
    
    5、在該 Filter 的父類 AbstractAuthenticationProcessingFilter 中,會根據上一步驗證的結果,跳轉到 successHandler 或者是 failureHandler。
    

    流程圖

    2、短信驗證碼登陸流程

    因為短信登錄的方式並沒有集成到Spring Security中,所以往往還需要我們自己開發短信登錄邏輯,將其集成到Spring Security中,那麼這裏我們就模仿賬號

    密碼登陸來實現短信驗證碼登陸。

    1、用戶名密碼登錄有個 UsernamePasswordAuthenticationFilter,我們搞一個SmsAuthenticationFilter,代碼粘過來改一改。
    2、用戶名密碼登錄需要UsernamePasswordAuthenticationToken,我們搞一個SmsAuthenticationToken,代碼粘過來改一改。
    3、用戶名密碼登錄需要DaoAuthenticationProvider,我們模仿它也 implenments AuthenticationProvider,叫做 SmsAuthenticationProvider。
    

    這個圖是網上找到,自己不想畫了

    我們自己搞了上面三個類以後,想要實現的效果如上圖所示。當我們使用短信驗證碼登錄的時候:

    1、先經過 SmsAuthenticationFilter,構造一個沒有鑒權的 SmsAuthenticationToken,然後交給 AuthenticationManager處理。
    
    2、AuthenticationManager 通過 for-each 挑選出一個合適的 provider 進行處理,當然我們希望這個 provider 要是 SmsAuthenticationProvider。
    
    3、驗證通過後,重新構造一個有鑒權的SmsAuthenticationToken,並返回給SmsAuthenticationFilter。
    filter 根據上一步的驗證結果,跳轉到成功或者失敗的處理邏輯。
    

    二、代碼實現

    1、SmsAuthenticationToken

    首先我們編寫 SmsAuthenticationToken,這裏直接參考 UsernamePasswordAuthenticationToken 源碼,直接粘過來,改一改。

    說明

    principal 原本代表用戶名,這裏保留,只是代表了手機號碼。
    credentials 原本代碼密碼,短信登錄用不到,直接刪掉。
    SmsCodeAuthenticationToken() 兩個構造方法一個是構造沒有鑒權的,一個是構造有鑒權的。
    剩下的幾個方法去除無用屬性即可。
    

    代碼

    public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    
        /**
         * 在 UsernamePasswordAuthenticationToken 中該字段代表登錄的用戶名,
         * 在這裏就代表登錄的手機號碼
         */
        private final Object principal;
    
        /**
         * 構建一個沒有鑒權的 SmsCodeAuthenticationToken
         */
        public SmsCodeAuthenticationToken(Object principal) {
            super(null);
            this.principal = principal;
            setAuthenticated(false);
        }
    
        /**
         * 構建擁有鑒權的 SmsCodeAuthenticationToken
         */
        public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.principal = principal;
            // must use super, as we override
            super.setAuthenticated(true);
        }
    
        @Override
        public Object getCredentials() {
            return null;
        }
    
        @Override
        public Object getPrincipal() {
            return this.principal;
        }
    
        @Override
        public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
            if (isAuthenticated) {
                throw new IllegalArgumentException(
                        "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
            }
    
            super.setAuthenticated(false);
        }
    
        @Override
        public void eraseCredentials() {
            super.eraseCredentials();
        }
    }
    

    2、SmsAuthenticationFilter

    然後編寫 SmsAuthenticationFilter,參考 UsernamePasswordAuthenticationFilter 的源碼,直接粘過來,改一改。

    說明

    原本的靜態字段有 usernamepassword,都幹掉,換成我們的手機號字段。
    SmsCodeAuthenticationFilter() 中指定了這個 filter 的攔截 Url,我指定為 post 方式的 /sms/login
    剩下來的方法把無效的刪刪改改就好了。

    代碼

    public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
        /**
         * form表單中手機號碼的字段name
         */
        public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
    
        private String mobileParameter = "mobile";
        /**
         * 是否僅 POST 方式
         */
        private boolean postOnly = true;
    
        public SmsCodeAuthenticationFilter() {
            //短信驗證碼的地址為/sms/login 請求也是post
            super(new AntPathRequestMatcher("/sms/login", "POST"));
        }
    
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }
    
            String mobile = obtainMobile(request);
            if (mobile == null) {
                mobile = "";
            }
    
            mobile = mobile.trim();
    
            SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
    
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
    
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    
        protected String obtainMobile(HttpServletRequest request) {
            return request.getParameter(mobileParameter);
        }
    
        protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
            authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        }
    
        public String getMobileParameter() {
            return mobileParameter;
        }
    
        public void setMobileParameter(String mobileParameter) {
            Assert.hasText(mobileParameter, "Mobile parameter must not be empty or null");
            this.mobileParameter = mobileParameter;
        }
    
        public void setPostOnly(boolean postOnly) {
            this.postOnly = postOnly;
        }
    }
    

    3、SmsAuthenticationProvider

    這個方法比較重要,這個方法首先能夠在使用短信驗證碼登陸時候被 AuthenticationManager 挑中,其次要在這個類中處理驗證邏輯。

    說明

    實現 AuthenticationProvider 接口,實現 authenticate() 和 supports() 方法。

    代碼

    public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
    
        private UserDetailsService userDetailsService;
    
        /**
         * 處理session工具類
         */
        private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
        String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_SMS";
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
    
            String mobile = (String) authenticationToken.getPrincipal();
    
            checkSmsCode(mobile);
    
            UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
            // 此時鑒權成功后,應當重新 new 一個擁有鑒權的 authenticationResult 返回
            SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
            authenticationResult.setDetails(authenticationToken.getDetails());
    
            return authenticationResult;
        }
    
        private void checkSmsCode(String mobile) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 從session中獲取圖片驗證碼
            SmsCode smsCodeInSession = (SmsCode) sessionStrategy.getAttribute(new ServletWebRequest(request), SESSION_KEY_PREFIX);
            String inputCode = request.getParameter("smsCode");
            if(smsCodeInSession == null) {
                throw new BadCredentialsException("未檢測到申請驗證碼");
            }
    
            String mobileSsion = smsCodeInSession.getMobile();
            if(!Objects.equals(mobile,mobileSsion)) {
                throw new BadCredentialsException("手機號碼不正確");
            }
    
            String codeSsion = smsCodeInSession.getCode();
            if(!Objects.equals(codeSsion,inputCode)) {
                throw new BadCredentialsException("驗證碼錯誤");
            }
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            // 判斷 authentication 是不是 SmsCodeAuthenticationToken 的子類或子接口
            return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
        }
    
        public UserDetailsService getUserDetailsService() {
            return userDetailsService;
        }
    
        public void setUserDetailsService(UserDetailsService userDetailsService) {
            this.userDetailsService = userDetailsService;
        }
    }
    

    4、SmsCodeAuthenticationSecurityConfig

    既然自定義了攔截器,可以需要在配置里做改動。

    代碼

    @Component
    public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
        @Autowired
        private SmsUserService smsUserService;
        @Autowired
        private AuthenctiationSuccessHandler authenctiationSuccessHandler;
        @Autowired
        private AuthenctiationFailHandler authenctiationFailHandler;
    
        @Override
        public void configure(HttpSecurity http) {
            SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
            smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
            smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(authenctiationSuccessHandler);
            smsCodeAuthenticationFilter.setAuthenticationFailureHandler(authenctiationFailHandler);
    
            SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
            //需要將通過用戶名查詢用戶信息的接口換成通過手機號碼實現
            smsCodeAuthenticationProvider.setUserDetailsService(smsUserService);
    
            http.authenticationProvider(smsCodeAuthenticationProvider)
                    .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }
    

    5、SmsUserService

    因為用戶名,密碼登陸最終是通過用戶名查詢用戶信息,而手機驗證碼登陸是通過手機登陸,所以這裏需要自己再實現一個SmsUserService

    @Service
    @Slf4j
    public class SmsUserService implements UserDetailsService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private RolesUserMapper rolesUserMapper;
    
        @Autowired
        private RolesMapper rolesMapper;
    
        /**
         * 手機號查詢用戶
         */
        @Override
        public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
            log.info("手機號查詢用戶,手機號碼 = {}",mobile);
            //TODO 這裏我沒有寫通過手機號去查用戶信息的sql,因為一開始我建user表的時候,沒有建mobile字段,現在我也不想臨時加上去
            //TODO 所以這裏暫且寫死用用戶名去查詢用戶信息(理解就好)
            User user = userMapper.findOneByUsername("小小");
            if (user == null) {
                throw new UsernameNotFoundException("未查詢到用戶信息");
            }
            //獲取用戶關聯角色信息 如果為空說明用戶並未關聯角色
            List<RolesUser> userList = rolesUserMapper.findAllByUid(user.getId());
            if (CollectionUtils.isEmpty(userList)) {
                return user;
            }
            //獲取角色ID集合
            List<Integer> ridList = userList.stream().map(RolesUser::getRid).collect(Collectors.toList());
            List<Roles> rolesList = rolesMapper.findByIdIn(ridList);
            //插入用戶角色信息
            user.setRoles(rolesList);
            return user;
        }
    }
    
    

    6、總結

    到這裏思路就很清晰了,我這裡在總結下。

    1、首先從獲取驗證的時候,就已經把當前驗證碼信息存到session,這個信息包含驗證碼和手機號碼。
    
    2、用戶輸入驗證登陸,這裡是直接寫在SmsAuthenticationFilter中先校驗驗證碼、手機號是否正確,再去查詢用戶信息。我們也可以拆開成用戶名密碼登陸那樣一個
    過濾器專門驗證驗證碼和手機號是否正確,正確在走驗證碼登陸過濾器。
    
    3、在SmsAuthenticationFilter流程中也有關鍵的一步,就是用戶名密碼登陸是自定義UserService實現UserDetailsService后,通過用戶名查詢用戶名信息而這裡是
    通過手機號查詢用戶信息,所以還需要自定義SmsUserService實現UserDetailsService后。
    
    

    三、測試

    1、獲取驗證碼

    獲取驗證碼的手機號是 15612345678 。因為這裏沒有接第三方的短信SDK,只是在後台輸出。

    向手機號為:15612345678的用戶發送驗證碼:254792
    
    

    2、登陸

    1)驗證碼輸入不正確

    發現登陸失敗,同樣如果手機號碼輸入不對也是登陸失敗

    2)登陸成功

    當手機號碼 和 短信驗證碼都正確的情況下 ,登陸就成功了。

    參考

    1、Spring Security技術棧開發企業級認證與授權(JoJo)

    2、SpringBoot 集成 Spring Security(8)——短信驗證碼登錄

    別人罵我胖,我會生氣,因為我心裏承認了我胖。別人說我矮,我就會覺得好笑,因為我心裏知道我不可能矮。這就是我們為什麼會對別人的攻擊生氣。
    攻我盾者,乃我內心之矛(21)
    

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

    【【其他文章推薦】

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

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

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

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

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

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

  • 【思考】URI和URL的區別?以及URL的結構

    【思考】URI和URL的區別?以及URL的結構

    URI = Universal Resource Identifier
    URL = Universal Resource Locator

    在學習中,我們難免會遇到 URI 和 URL,有時候都傻傻分不清,為啥這邊是 URI 那邊又是 URL,這兩者到底有什麼區別呢?

    我們從名字上看

    • 統一資源標識符(Uniform Resource Identifier, URI):是一個用於標識某一互聯網資源名稱的字符串。
    • 統一資源定位符(Uniform Resource Locator, URL):是一個用於標識和定位某一互聯網資源名稱的字符串。

    可能大家就比較困惑了,這倆好像是一樣的啊?那我們就類比一下我們現實生活中的情況:
    我們要找一個人——張三,我們可以通過他的唯一的標識來找,比如說身份證,那麼這個身份證就唯一的標識了一個人,這個身份證就是一個 URI
    而要找到張三,我們不一定要用身份證去找,我們還可以根據地址去找,如 在清華大學18號宿舍樓的404房間第一個床鋪的張三,我們也可以唯一確定一個張三,
    動物住址協議://地球/中國/北京市/清華大學/18號宿舍樓/404號寢/張三.人。而這個地址就是我們用於標識和定位的 URL
    我們從上面可以很明顯的看出,URI 通過任何方法標識一個人即可,而 URL 雖然也可以標識一個人,但是它主要是通過定位地址的方法標識一個人,所以 URL 其實是 URI 的一個子集,即 URL 是靠標識定位地址的一個 URI

    Url 的構成

    URL(Uniform Resource Locator,統一資源定位符),用於定位網絡上的資源,每一個信息資源都有統一的且在網上唯一的地址。

    Url一般有以下部分組成
    scheme://host:port/path?query#fragment

    Scheme: 通信協議,一般為http、https等;
    Host: 服務器的域名主機名或ip地址;
    Port: 端口號,此項為可選項,默認為80;
    Path: 目錄,由“/”隔開的字符串,表示的是主機上的目錄或文件地址;
    Query: 查詢,此項為可選項,可以給動態網頁傳遞參數,用“&”隔開,每個參數的名和值用“=”隔開;
    Fragment: 信息片段,字符串,用於指定網絡資源中的某片斷;

    其實,把 URL 說成是網址其實是很不嚴謹的說法,因為 URL 有很嚴格的結構,表示也很靈活、有彈性。
    在 RFC 3986: Uniform Resource Identifier (URI): Generic Syntax 的 Syntax Components 把 URL 描述為如下圖:

    如圖所示,把 URL 分成幾個部分,這樣便可以了解URL的構成。 在 URI scheme – Wikipedia 頁面中對 URL 的描述更為詳細,如下圖:

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

    【其他文章推薦】

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

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

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

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • IDEA創建SpringBoot的多模塊項目教程

    IDEA創建SpringBoot的多模塊項目教程

    最近在寫一個多模塊的SpringBoot項目,基於過程總了一些總結,故把SpringBoot多個模塊的項目創建記錄下來。

    首先,先建立一個父工程:

    (1)在IDEA工具欄選擇File->New->Project

    (2)選擇Spring Initializr,默認選擇Default,然後點擊Next:    

    (3)在輸入框填寫以下截圖內容,點擊Next

    (4)直接點Next,無需選擇

    (5)直接點擊Finish完成創建

    (6)按照以上步驟,可以生成以下的項目目錄結構:

    (7)這時把沒用的.mvn目錄,src目錄,mvnw還有mvnw.cmd都刪除,最終只保留.gitignore和pom.xml,若是web項目,可在該pom.xml里添加以下依賴:

    1 <!--web特徵-->
    2 <dependency>
    3     <groupId>org.springframework.boot</groupId>
    4     <artifactId>spring-boot-starter-web</artifactId>
    5     <version>2.3.1.RELEASE</version>
    6 </dependency>

    最終得到以下的父結構目錄:

     

    以上是創建父模塊,下面創建子模塊:

    (1)在父模塊的根目錄fte上點右鍵,在彈出的框里選擇New->Module

    (2)選擇Maven,點擊Next

    (3)填寫以下內容,點擊Next

    (4)填寫Module,點擊Finish

    (5)同理添加fte-controller,fte-dao,fte-service,fte-web,最終得到以下的目錄結構:

    (6)增加模塊之間的依賴:

    controller層添加以下依賴:

     1 <dependencies>
     2     <dependency>
     3         <groupId>com.example</groupId>
     4         <artifactId>fte-common</artifactId>
     5         <version>0.0.1-SNAPSHOT</version>
     6     </dependency>
     7 
     8     <dependency>
     9         <groupId>com.example</groupId>
    10         <artifactId>fte-dao</artifactId>
    11         <version>0.0.1-SNAPSHOT</version>
    12     </dependency>
    13 
    14     <dependency>
    15         <groupId>com.example</groupId>
    16         <artifactId>fte-service</artifactId>
    17         <version>0.0.1-SNAPSHOT</version>
    18     </dependency>
    19 </dependencies>

    service層添加以下依賴:

    1 <dependencies>
    2     <dependency>
    3         <groupId>com.example</groupId>
    4         <artifactId>fte-dao</artifactId>
    5         <version>0.0.1-SNAPSHOT</version>
    6     </dependency>
    7 </dependencies>

    (7)測試

    在fte-controller創建com.zhu.fte.web包,增加以下兩個類:

    fteWebApplication類:

     1 package com.zhu.fte.web;
     2 
     3 import org.springframework.boot.SpringApplication;
     4 import org.springframework.boot.autoconfigure.SpringBootApplication;
     5 
     6 @SpringBootApplication
     7 public class fteWebApplication {
     8     public static void main(String[] args) {
     9         SpringApplication.run(fteWebApplication.class,args);
    10     }
    11 }

    DemoController類

     1 package java.com.zhu.fte.web;
     2 
     3 import org.springframework.web.bind.annotation.GetMapping;
     4 import org.springframework.web.bind.annotation.RequestMapping;
     5 import org.springframework.web.bind.annotation.RestController;
     6 
     7 @RestController
     8 @RequestMapping("demo")
     9 public class DemoController {
    10 
    11     @GetMapping("test")
    12     public String test(){
    13         return "hello world";
    14     }
    15 
    16 }

    運行發現出現錯誤:

    出現錯誤:

    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test (default-test) on project fte-common: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test failed: Plugin org.apache.maven.plugins:maven-surefire-plugin:2.22.2 or one of its dependencies could not be resolved: Could not transfer artifact junit:junit:jar:4.12 from/to central (https://repo.maven.apache.org/maven2): Connect to repo.maven.apache.org:443 [repo.maven.apache.org/151.101.52.215] failed: Connection timed out: connect -> [Help 1]

    把缺少的org.apache.maven.plugins手動放到父工程的pom.xml里

     1 <build>
     2    <plugins>
     3       <plugin>
     4          <groupId>org.apache.maven.plugins</groupId>
     5          <artifactId>maven-clean-plugin</artifactId>
     6          <version>2.5</version>
     7       </plugin>
     8       <plugin>
     9          <groupId>org.apache.maven.plugins</groupId>
    10          <artifactId>maven-source-plugin</artifactId>
    11          <version>2.2</version>
    12       </plugin>
    13       <plugin>
    14          <groupId>org.apache.maven.plugins</groupId>
    15          <artifactId>maven-compiler-plugin</artifactId>
    16          <version>3.0</version>
    17          <configuration>
    18             <source>1.8</source>
    19             <target>1.8</target>
    20             <encoding>${file.encoding}</encoding>
    21             <!--編譯的時候方法不改變方法參數名稱,用於支持使用反射獲取方法參數名稱-->
    22             <compilerArgument>-parameters</compilerArgument>
    23          </configuration>
    24       </plugin>
    25       <plugin>
    26          <groupId>org.apache.maven.plugins</groupId>
    27          <artifactId>maven-install-plugin</artifactId>
    28          <version>2.4</version>
    29       </plugin>
    30       <plugin>
    31          <groupId>org.apache.maven.plugins</groupId>
    32          <artifactId>maven-jar-plugin</artifactId>
    33          <version>2.4</version>
    34          <configuration>
    35             <archive>
    36                <manifest>
    37                   <addDefaultImplementationEntries>true
    38                   </addDefaultImplementationEntries>
    39                </manifest>
    40             </archive>
    41          </configuration>
    42       </plugin>
    43 
    44       <plugin>
    45          <groupId>org.apache.maven.plugins</groupId>
    46          <artifactId>maven-surefire-plugin</artifactId>
    47          <version>2.13</version>
    48          <configuration>
    49             <argLine>-Xmx512M -Dfile.encoding=${file.encoding}</argLine>
    50          </configuration>
    51       </plugin>
    52    </plugins>
    53 </build>

    運行fteWebApplication類里的main方法,默認端口為8080,訪問http://localhost:8080/demo/test,正常出現以下情況:

     

    按照以上步驟,就可以初步建立SpringBoot多模塊的項目,下一章將基於這個基礎搭建Mybatis以及其逆向工程。

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

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

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • Spring IoC 循環依賴的處理

    Spring IoC 循環依賴的處理

    前言

    本系列全部基於 Spring 5.2.2.BUILD-SNAPSHOT 版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的源碼解析。

    本篇文章主要介紹 Spring IoC 是怎麼解決循環依賴的問題的。

    正文

    什麼是循環依賴

    循環依賴就是循環引用,就是兩個或多個 bean 相互之間的持有對方,比如A引用B,B引用A,像下面偽代碼所示:

    public class A {
        private B b;
        
        // 省略get和set方法...
    }
    
    public class B {
        private A a;
        
        // 省略get和set方法...
    }
    

    Spring 如何解決循環依賴

    Spring IoC 容器對循環依賴的處理有三種情況:

    1. 構造器循環依賴:此依賴 Spring 無法處理,直接拋出 BeanCurrentlylnCreationException 異常。
    2. 單例作用域下的 setter 循環依賴:此依賴 Spring 通過三級緩存來解決。
    3. 非單例的循環依賴:此依賴 Spring 無法處理,直接拋出 BeanCurrentlylnCreationException 異常。

    構造器循環依賴

    還是假設上面的A和B類是構造器循環依賴,如下所示:

    public class A {
        private B b;
        
        public A(B b) {
            this.b = b;
        }
        
        // 省略get和set方法...
    }
    
    public class B {
        private A a;
        
        public B(A a) {
            this.a = a;
        }
        
        // 省略get和set方法...
    }
    

    然後我們在 XML 中配置了構造器自動注入,如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="a" class="com.leisurexi.ioc.circular.reference.A" autowire="constructor" />
    
        <bean id="b" class="com.leisurexi.ioc.circular.reference.B" autowire="constructor" />
    
    </beans>
    

    那麼我們在獲取 A 時,首先會進入 doGetBean() 方法(該方法在Spring IoC bean 的加載中分析過),會進行到如下代碼塊:

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    
        // 省略其它代碼...
        
        // 如果 bean 的作用域是單例
        if (mbd.isSingleton()) {
            // 創建和註冊單例 bean
            sharedInstance = getSingleton(beanName, () -> {
                try {
                    // 創建 bean 實例
                    return createBean(beanName, mbd, args);
                }
                catch (BeansException ex) {
                    destroySingleton(beanName);
                    throw ex;
                }
            });
            // 獲取bean實例
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
        
        // 省略其它代碼...
     
    }
    

    上面方法中的 getSingleton() 方法會判斷是否是第一次創建該 bean,如果是第一次會先去創建 bean,也就是調用 ObjectFacotygetObject() 方法,即調用 createBean() 方法創建 bean 前,會先將當前正要創建的 bean 記錄在緩存 singletonsCurrentlyInCreation 中。

    在創建A時發現依賴 B,便先去創建 B;B在創建時發現依賴A,此時A因為是通過構造函數創建,所以沒創建完,便又去創建A,發現A存在於 singletonsCurrentlyInCreation,即正在創建中,便拋出 BeanCurrentlylnCreationException 異常。

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        // 加鎖
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            // 一級緩存中不存在當前 bean,也就是當前 bean 第一次創建
            if (singletonObject == null) {
                // 如果當前正在銷毀 singletons,拋出異常
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                // 創建單例 bean 之前的回調
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    // 獲取 bean 實例
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
    				}
                // 省略異常處理...
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    // 創建單例 bean 之後的回調
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    // 將 singletonObject 放入一級緩存,並從二級和三級緩存中移除
                    addSingleton(beanName, singletonObject);
                }
            }
            // 返回 bean 實例
            return singletonObject;
        }
    }
    
    // 單例 bean 創建前的回調方法,默認實現是將 beanName 加入到當前正在創建 bean 的緩存中,
    // 這樣便可以對循環依賴進行檢測
    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }
    
    // 單例 bean 創建后的回調方法,默認實現是將 beanName 從當前正在創建 bean 的緩存中移除
    protected void afterSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }
    
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // 這邊bean已經初始化完成了,放入一級緩存
            this.singletonObjects.put(beanName, singletonObject);
            // 移除三級緩存
            this.singletonFactories.remove(beanName);
            // 移除二級緩存
            this.earlySingletonObjects.remove(beanName);
            // 將 beanName 添加到已註冊 bean 緩存中
            this.registeredSingletons.add(beanName);
        }
    }
    

    setter循環依賴

    還是假設上面的A和B類是 field 屬性依賴注入循環依賴,如下所示:

    public class A {
        private B b;
        
        // 省略get和set方法...
    }
    
    public class B {
        private A a;
        
        // 省略get和set方法...
    }
    

    然後我們在 XML 中配置了按照類型自動注入,如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="a" class="com.leisurexi.ioc.circular.reference.A" autowire="byType" />
    
        <bean id="b" class="com.leisurexi.ioc.circular.reference.B" autowire="byType" />
    
    </beans>
    

    Spring 在解決單例循環依賴時引入了三級緩存,如下所示:

    // 一級緩存,存儲已經初始化完成的bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二級緩存,存儲已經實例化完成的bean
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    // 三級緩存,存儲創建bean實例的ObjectFactory
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    // 按先後順序記錄已經註冊的單例bean
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
    

    首先在創建A時,會進入到 doCreateBean() 方法(前面的流程可以查看Spring IoC bean 的創建一文),如下:

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
        // 獲取bean的實例
        BeanWrapper instanceWrapper = null;
        if (instanceWrapper == null) {
            // 通過構造函數反射創建bean的實例,但是屬性並未賦值
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        // 獲取bean的實例
        final Object bean = instanceWrapper.getWrappedInstance();
        
        // 省略其它代碼...
    
        // bean的作用域是單例 && 允許循環引用 && 當前bean正在創建中
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
        // 如果允許bean提前曝光
        if (earlySingletonExposure) {
            // 將beanName和ObjectFactory形成的key-value對放入singletonFactories緩存中
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
        
        // 省略其它代碼...
        
    }
    

    在調用 addSingletonFactory() 方法前A的實例已經創建出來了,只是還未進行屬性賦值和初始化階段,接下來將它放入了三級緩存中,如下:

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        // 加鎖
        synchronized (this.singletonObjects) {
            // 如果一級緩存中不包含當前bean
            if (!this.singletonObjects.containsKey(beanName)) {
                // 將ObjectFactory放入三級緩存
                this.singletonFactories.put(beanName, singletonFactory);
                // 從二級緩存中移除
                this.earlySingletonObjects.remove(beanName);
                // 將beanName加入到已經註冊過的單例bean緩存中
                this.registeredSingletons.add(beanName);
            }
        }
    }
    

    接下來A進行屬性賦值階段(會在後續文章中單獨分析這個階段),發現依賴B,便去獲取B,發現B還沒有被創建,所以走創建流程;在B進入屬性賦值階段時發現依賴A,就去調用 getBean() 方法獲取A,此時會進入 getSingleton() 方法(該方法的調用流程在Spring IoC bean 的加載一文中分析過),如下:

    public Object getSingleton(String beanName) {
        // allowEarlyReference設置為true表示允許早期依賴
        return getSingleton(beanName, true);
    }
    
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 先從一級緩存中,檢查單例緩存是否存在
        Object singletonObject = this.singletonObjects.get(beanName);
        // 如果為空,並且當前bean正在創建中,鎖定全局變量進行處理
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 從二級緩存中獲取
                singletonObject = this.earlySingletonObjects.get(beanName);
                // 二級緩存為空 && bean允許提前曝光
                if (singletonObject == null && allowEarlyReference) {
                    // 從三級緩存中獲取bean對應的ObjectFactory
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 調用預先設定的getObject(),獲取bean實例
                        singletonObject = singletonFactory.getObject();
                        // 放入到二級緩存中,並從三級緩存中刪除
                        // 這時bean已經實例化完但還未初始化完
                        // 在該bean未初始化完時如果有別的bean引用該bean,可以直接從二級緩存中取出返回
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
    

    嘗試一級緩存 singletonObjects (肯定沒有,因為A還沒初始化完全),嘗試二級緩存 earlySingletonObjects(也沒有),嘗試三級緩存 singletonFactories,由於A通過 ObjectFactory 將自己提前曝光了,所以B能夠通過 ObjectFactory.getObject() 拿到A對象(雖然A還沒有初始化完全,但是總比沒有好呀)。B拿到A后順利創建並初始化完成,調用上面分析過的 addSingleton() 方法將自己放入一級緩存中。此時返回A中,A也能順利拿到完全初始化的B進行後續的階段,最後也將自己放入一級緩存中,並從二級和三級緩存中移除。

    過程圖如下所示:

    非單例循環依賴

    對於非單例的 bean,Spring 容器無法完成依賴注入,因為 Spring 容器不進行緩存,因此無法提前暴露一個創建中的 bean

    總結

    本文主要介紹了 Spring 對三種循環依賴的處理,其實還有一種字段循環依賴,比如 @Autowired 註解標註的字段,但它和 setter 循環依賴的解決方法一樣,這裏就沒有多說。

    最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新。地址:https://github.com/leisurexi/tiny-spring。

    參考

    • 《Spring 源碼深度解析》—— 郝佳
    • https://juejin.im/post/5c98a7b4f265da60ee12e9b2

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

    【【其他文章推薦】

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

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

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

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

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

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

  • C++ Primer Plus(四)

    完整閱讀C++ Primer Plus 

      系統重新學習C++語言部分,記錄重要但易被忽略的,關鍵但易被遺忘的。

     

    友元、異常和其他

      1、拋出異常類時,雖然catch的是一個引用,但是也會產生一次拷貝,因為當拋出異常的函數在棧解退的過程中會會調用異常類的析構函數,異常類將不復存在。

      2、如果有一個異常類層次結構,應該這樣排列catch塊:將捕獲位於層次結構最下面的異常類的catch語句放在最前面,將捕獲基類異常的catch語句放在最後面。拋出異常的順序要與catch塊相反。

      3、在catch語句中使用基類對象時,將捕獲所有的派生類對象,但派生類特性將被剝去,因此將使用虛方法的基類版本。

      4、 將dynamic_cast用於引用時,由於沒有與空指針對應的引用值,因此無法使用特殊的引用值來表示失敗,當請求不正確時,將引發bad_cast的異常。

      5、reinterpret_cast運算符並不支持所有的類型轉換,例如,可以將指針類型轉換為足以存儲指針的整數,但不能將指針轉換為更小的整型或浮點型。另一個限制是,不能將函數指針和數據指針互相轉換。

     

    string類和標準模板庫

      6、使用new分配內存時,可以使用auto_ptrunique_ptrshared_ptr、但只有unique_ptr有使用new[]和delete[]的版本。

      7、在unique_ptr為右值時,可以將其賦值給shared_ptr,模板shared_ptr包含一個顯式構造函數,可以用於將右值unique_ptr轉換為shared_ptr。

      8、對於所有內置的算術運算符、關係運算符和邏輯運算符,STL都提供了等價的函數符(仿函數)。

      9、valarray模板類重載了許多運算符,可以直接參与大多數數值運算;slice類可用作數組索引,它接受三個值初始值:起始索引、索引數、跨距。

    1 valarry<double> arr(10);
    2 arr[slice(1,4,3)] = 10;

      slice(1,4,3)創建的對象表示選擇4個索引,這可以將arr的第1、4、7、10個元素都設置為10。

      10、迭代器類型

    Input iterator(輸入迭代器)  讀,不能寫;只支持自增運算
    Output iterator(輸出迭代器) 寫,不能讀;只支持自增運算
    Forward iterator(前向迭代器) 讀和寫;只支持自增運算
    Bidirectional iterator(雙向迭代器) 讀和寫;支持自增和自減運算
    Random access iterator(隨機訪問迭代器) 讀和寫;支持完整的迭代器算術運算

     

    輸入、輸出和文件

      11、對於標準錯誤輸出,是沒有緩衝區的。

      12、在使用cout時,可以使用成員函數width()設置下一次輸出時的字段寬度,默認右對齊並以空格填充空白字段,當字段寬度不足時,C++不對截斷輸出寬度;使用成員函數fill()用來填充空白字段;使用成員函數precision()來設置浮點數輸出精度;成員函數setf()與unsetf()提供了更豐富的輸出格式設置方法,但使用標準控制符將更加簡單。

      13、對於cin的get()方法和getline()方法來說,如果沒有讀取到任何字符(getline()將換行符視為一個字符),則設置failbit;如果讀取了最大數目的字符,但行中還有其他字符,getline()將設置failbit

      14、cin的peak()方法可以查看輸入流中的下一個字符,gcount()方法可以返回最後一個非格式化抽取方法讀取的字符數,putback()方法可以將字符插入到輸入字符串中。

      15、fstream類中的方法seekg()和seekp()分別將輸入指針和輸出指針移到指定的文件位置,事實上,由於fstream類使用緩衝區來存儲中間數據,因此指針指向的是緩衝區中的位置,而不是實際的文件。

      16、fstream類中的方法tellg()和tellp()方法分別返回輸入流、輸出流當前指針的位置,對於fstream對象,輸入輸出指針將一前一后地移動,因此它們的返回值相同。但對於使用istream對象來管理輸入流,而使用ostream對象來管理同一個文件的輸出流,則輸入指針和輸出指針將彼此獨立的移動。

      17、關於如何生成臨時文件,使用tmpnam()可以生產TMP_NAM個不同的文件名,其中每個文件名包含的字符不超過L_tmpnam個。

      18、C++庫還提供了sstream族(包含ostringstream類和istringstream類),它們使用相同的接口提供程序和string對象之間的IO。

     

    探討C++新標準

      19、新標準引入的移動語義,用來修飾六個特殊函數的default關鍵字,用來刪除任意成員函數的delete關鍵字,以及使用類似初始化列表的方式在一個構造函數中使用另一個構造函數(被稱為委託構造),以及使用using 類名::函數名,使基類所有的非特殊成員函數對派生類可以用(繼承構造函數),以及显示聲明重寫(覆蓋)某個虛函數的標識符override,以及禁止派生類覆蓋特定的虛函數標識符final

      20、C++11引入lambda表達的主要目的是能夠將類似於函數的表達式用作接受函數指針或函數符的函數的參數。

      21、C++提供了多個包裝器對象,用於給其他編程接口提供更一致或更合適的接口。C++11提供了包括模板bind(替代bind1st和bind2nd)men_fn(將成員函數作為常規函數傳遞)reference_wrapper(創建行為像引用但可被複制的對象)以及funtion(以統一的方式處理多種類似於函數的形式,使用模板時可減少可執行代碼的規模)

      22、正確使用遞歸實現可變參數模板

      23、C++11增加了對并行編程的支持,以及相當多的新增庫等。

     

    附錄

      24、C++允許定義指向類成員(包括數據和函數)的指針,這種語法需要使用到成員解除引用運算符(* 、->*)。

      25、C++11新增了alignof運算符,它接受一個類型作為參數,返回這個類型的對齊方式;noexcept關鍵字用於指出函數不會引發異常,它也可以用作運算符,判斷表達式是否可能引發異常,不引發返回true。

      26、STL提供了豐富的全局函數,包括查詢,排序,複製等一系列算法。

      

      2020年6月2日,星期二,凌晨2點01分,首次完整讀完這本書,共勉。

      學如逆水行舟,不進則退;心似平原放馬,易縱難收。

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

    【其他文章推薦】

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

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

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

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • vue中使用element2

    vue中使用element2

    阻止谷歌下記住密碼

    當我們將input框的類型設置為密碼框的時候,就會出現下面這種效果,不僅樣式不統一,有的時候,密碼框的上面並不是用戶名,而是其他的內容,也會被強制显示為用戶名:

     首先需要解決樣式問題:

     #app input:-webkit-autofill {
        -webkit-text-fill-color: #fff !important;
        -webkit-box-shadow: none !important;
        background-color: transparent;
        background-image: none;
        transition: background-color 999999s ease-in-out, color 999999s ease-in-out;
      }

     其次,阻止谷歌自帶的記住密碼:

     回車重定向

     單個el-input獲得焦點時,點擊鍵盤迴車,會觸發路由重定向。

    解決方法:@submit.native.preven t阻止表單默認事件

     日期時間框的默認值在IE無法清除

    element的日期框添加默認值后,在ie下,默認的清空按鈕無法清空默認日期值:

     

     

     數據應該是已經清空了,但是DOM沒有刷新,所以需要強制刷新DOM:

     

     自定義表頭

    <template>
        <div>
            <el-table-column
                v-for="(item, idx) in list"
                :key="idx"
                v-bind="item" :show-overflow-tooltip="true">
                <tHeader
                    v-if="item.children"
                    :list="item.children">
                </tHeader>
            </el-table-column>
        </div>
    </template>
    <script>
    export default {
        name: 'tHeader',
        props: [
            'list'
        ],
        methods: {
            repairEleSortBug() {
                this.list.unshift(this.list.pop());
            }
        },
        
        created() {
                //修復elementUI排序倒置的bug(將數組最後一個放到第一個)
            this.repairEleSortBug();
        }
    };
    </script>

     

     

     

     對象監聽

    在vue中可以通過監聽一個變量的值變化觸發相應事件,但是當需要監聽的變量是個複雜對象時,通常在外出是監聽不到對象裏面值的變化,這時就需要深度監聽:

     

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

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

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • 航空業減碳動起來 澳航允2050年前碳排減到零

    摘錄自2019年11月11日中央社報導

    澳洲航空今(11日)允諾加入英國航空(British Airways)母公司「國際航空集團」(IAG),要在2050年前,把淨碳排降至零。澳航(Qantas)執行長喬伊斯(Alan Joyce)發布聲明說,氣候變遷的憂慮「真實存在」,「我們之所以做這件事,是因為這件事很重要」。

    來自環保團體「反抗滅絕」(Extinction Rebellion, XR)與環保小鬥士桑柏格(Greta Thunberg)壓力越來越大之際,國際航空集團上月允諾2050年前把淨碳排減到零,是首家做此承諾的大型航空公司。航空業者整體則已答應要在2050年前把碳排量減至2005年的一半。

    澳航表示,希望能把淨碳排控制在2020年水準,並在10年間投資5000萬澳元開發永續能源來減碳,與傳統航空燃料相比,可減少8成的碳排。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【【其他文章推薦】

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

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

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

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

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

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

  • 澳洲野火燒破千人家園 雪梨收災難性警告

    摘錄自2019年11月11日ETtoday 綜合報導

    澳洲東部森林野火自8日起延燒多日,造成3人死亡、數十人受傷以及上千人失去家園,仍在燃燒的上百處火場燒掉了至少150棟的住宅。當局指出,恐怕連雪梨及周邊地區也會受影響,12日將迎接「災難性」野火威脅。

    外電報導,新南威爾斯州(New South Wales)及昆士蘭州(Queensland)等地接連傳出災情,當局派出將近1300名消防員也沒能撲滅燒了4天的大火,只能宣布災區進入「緊急狀態」,並警告人口最多的雪梨地區,高溫恐飆破攝氏37度,要面臨「災難性」(catasrophic)的野火威脅。

    這也是澳洲2009年推出新的「火情危險評級制度」(Fire Danger Rating,分為中等、高、非常高、嚴重、極高及災難性)後,雪梨第一次面臨「災難性」等級,預計12日雪梨會出現攝氏37度高溫、強風、乾燥等,不排除比澳洲東部當初被野火襲擊時更嚴重。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • 非洲黑猩猩擄走人類小孩 源自於棲地銳減

    摘錄自2019年11月12日ETtoday綜合報導

    非洲烏干達黑猩猩原始棲地逐年喪失,引爆人類與猩猩之間的激烈衝突。《國家地理》雜誌網站近日報導,當地黑猩猩為了覓食,不僅破壞農作物,甚至學會對人類發動攻擊;當地自2014年以來,已經發生多起兒童遭猩猩擄走事件,其中一人甚至被猩猩開膛剖肚,內臟還被吃掉。

    在野外,雄性黑猩猩有時會獵捕其他猴子或山羊,並食用獵物的肉果腹;他們雖然對人類保持警覺心,但這也使他們變得更具侵略性。自從2014年以來,陸續發生多起孩童遭黑猩猩攻擊事件,造成至少3人死亡,另有至少6人受傷。目前還無法確定黑猩猩為何突然開始襲擊人類兒童,但據信與烏干達西部自然棲地逐漸被農田侵蝕有關。

    烏干達野生動物管理局(UWA)表示,當局已經意識到猩猩攻擊兒童的問題,但面對黑猩猩的棲地森林和自然家園遭破壞也感到無能為力,理由是人們普遍認為開闢農地來維持生計,比起保育森林更加重要。

    據悉,非洲大陸大約只剩下30萬頭野生黑猩猩,烏干達野生動物管理局考慮透過一系列教育性活動,向人們宣導保育森林的重要性。

     

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準