整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          「Spring」只想用一篇文章記錄@Value的使用

          「Spring」只想用一篇文章記錄@Value的使用(附思維導(dǎo)圖)

          簡(jiǎn)介

          不得不說(shuō),Spring為大家提供許多開箱即用的功能,@Value就是一個(gè)極其常用的功能,它能將配置信息注入到bean中去。即使是一個(gè)簡(jiǎn)單的功能,Spring也提供了豐富的注入類型和形式。我經(jīng)常會(huì)忘記一些特別類型注入的寫法,比如說(shuō)數(shù)組,現(xiàn)在整理一下,希望以后不用再找了。



          2 三種形式

          使用@Value注入有三種形式,如下所示:

          (1)直接寫值@Value("string value")

          這種方式就是直接把要注入的值字面量寫在注解里,比較少用。如果要寫死在注解里了,那直接定義變量的時(shí)候?qū)懰谰涂梢粤恕?/p>

          (2)占位符@Value("${myvalue}")

          這種應(yīng)該最常用,通過屬性名,將值注入進(jìn)來(lái)。

          如果可能為空,需要設(shè)置默認(rèn)值,用法:@Value("${unknown.param:defaultValue}")

          (3)SpEL表達(dá)式@Value("#{someBean.someValue}")
          SpEL表達(dá)式很強(qiáng)大,還能在屬性值基礎(chǔ)上加以運(yùn)算等。

          如果可能為空,需要設(shè)置默認(rèn)值,用法:@Value("#{systemProperties['unknown'] ?: 'defaultValue'}")

          另外,占位符形式和SpEL表達(dá)式是可以結(jié)合使用的,如下:

          @Value("#{'${listOfValues}'.split(',')}")
          private List valuesList;
          

          需要注意的,內(nèi)外順序不能倒過來(lái),應(yīng)該要#{}外面,${}在里面。

          3 使用場(chǎng)景

          對(duì)于注入的場(chǎng)景,主要有三種:

          (1)bean聲明的變量

          (2)setter方法注入

          (3)構(gòu)造方法或其它方法的入?yún)?/p>

          例子代碼如下:

          //bean聲明的變量
          public static class MyValues {
              @Value("#{systemProperties['user.timezone']}")
              private String timeZone;
          }
          
          //setter 方法中
          public static class MyValues {
              private String timeZone;
              @Value("#{systemProperties['user.timezone']}")
              public void setTimeZone(String timeZone) {
                  this.timeZone=timeZone;
              }
          }
          
          //方法入?yún)?
          public class MyValues {
              private String timeZone;
              @Autowired
              public void configure(@Value("#{systemProperties['user.timezone']}") String timeZone) {
                  this.timeZone=timeZone;
              }
          }
          

          4 引入配置文件

          既然是注入配置屬性,那就需要有配置文件。對(duì)于Springboot,引入配置文件有兩種方法,一種是默認(rèn)引入的application.properties,另一種則需要通過@PropertySource來(lái)引入,引入的方式如下:

          @PropertySources({
            @PropertySource(value="classpath:missing.properties", ignoreResourceNotFound=true),
            @PropertySource("classpath:config.properties")
          })
          public class AppConfig {
                  //...
          }
          

          加上ignoreResourceNotFound后,即使文件找不到,也不會(huì)拋FileNotFoundException異常。

          5 使用舉例

          現(xiàn)在例舉一些可能使用到的例子,以后在這找就行了。

          Java代碼如下所示:

          //直接寫值
          @Value("plainText")
          private String plainText;
          
          //普通形式-字符串
          @Value("${myValues.string}")
          private String myValuesString;
          
          //普通形式-數(shù)字
          @Value("${myValues.int}")
          private int myValuesInt;
          
          //普通形式-布爾類型
          @Value("${myValues.boolean}")
          private boolean myValuesBoolean;
          
          //數(shù)組
          @Value("${myValues.array}")
          private String[] myValuesArray;
          
          //Map
          @Value("#{${myValues.map}}")
          private Map<String, String> myValuesMap;
          
          //操作系統(tǒng)屬性
          @Value("#{systemProperties['user.timezone']}")
          private String timeZone;
          
          //表達(dá)式結(jié)果
          @Value("#{ T(java.lang.Math).random() * 100.0 }")
          private double randomNumber;
          
          //其它bean的屬性
          @Value("#{propertiesApplication.class.getName()}")
          private String className;
          
          //文件資源
          @Value("classpath:larry.txt")
          private Resource file;
          
          //URL資源
          @Value("https://www.github.com")
          private Resource url;
          

          其中,配置文件application.properties內(nèi)容為:

          myValues.int=99
          myValues.boolean=true
          myValues.string=Larry
          myValues.array=my,name,is,larry
          myValues.map={name: 'Larry', age: '18', city: 'Guangzhou'}
          

          資源文件larry.txt內(nèi)容為:

          上善若水,水利萬(wàn)物而不爭(zhēng)!
          

          啟動(dòng)程序,打印以上所有屬性,輸出結(jié)果如下所示:

          {
          plainText='plainText', 
          myValuesString='Larry', 
          myValuesInt=99, 
          myValuesBoolean=true, 
          myValuesArray=[my, name, is, larry], 
          myValuesMap={name=Larry, age=18, city=Guangzhou}, 
          timeZone='Asia/Shanghai', 
          randomNumber=19.775129662772294, 
          className='com.pkslow.properties.PropertiesApplication$$EnhancerBySpringCGLIB$$4d0912c', 
          file=上善若水,水利萬(wàn)物而不爭(zhēng)!, 
          url=<!DOCTYPE html>
          <html lang="en">
          省略html內(nèi)容
          </html>
          }
          

          6 總結(jié)

          本文講解了@Value注解的使用,基本上平時(shí)開發(fā)用到的都涉及了,應(yīng)該不需要再找其它資料了吧。


          歡迎關(guān)注公眾號(hào)<南瓜慢說(shuō)>,將持續(xù)為你更新...

          多讀書,多分享;多寫作,多整理。

          TML 中使用 <input> 元素表示單行輸入框和 <textarea> 元素表示多行文本框。

          HTML中使用的 <input> 元素在 JavaScript 中對(duì)應(yīng)的是 HTMLInputElement 類型。HTMLInputElement 繼承自 HTMLElement 接口:

          interface HTMLInputElement extends HTMLElement {
              ...
          }

          HTMLInputElement 類型有一些獨(dú)有的屬性和方法:

          • name:表示 <input> 元素的名稱。
          • disabled:表示 <input> 元素是否禁止使用。在表單提交時(shí)不會(huì)包含被禁止的 <input> 元素。
          • autofocus:表示頁(yè)面加載時(shí),該元素是否會(huì)自動(dòng)獲得焦點(diǎn)。
          • required:表示表單提交時(shí),該 <input>元素是否為必填。
          • value:表示該 <input> 元素的值。
          • validity:返回一個(gè) ValidityState 對(duì)象,表示 <input> 元素的校驗(yàn)狀態(tài)。是只讀屬性。
          • validationMessage:表示 <input> 元素校驗(yàn)失敗時(shí),用戶看到的報(bào)錯(cuò)信息。如果該元素不需要校驗(yàn),或者通過校驗(yàn),該屬性為空字符串。是只讀屬性。
          • willValidate:表示表單提交時(shí),<input> 元素是否會(huì)被校驗(yàn)。是只讀屬性。
          • select():選中 <input> 元素內(nèi)部的所有文本。
          • click():模擬鼠標(biāo)點(diǎn)擊當(dāng)前的 <input> 元素。

          而在上述介紹 HTMLInputElement 類型中的屬性時(shí),type 屬性要特別關(guān)注一下,因?yàn)楦鶕?jù) type 屬性的改變,可以改變<input>的屬性。

          類型

          描述

          text

          文本輸入

          password

          密碼輸入

          submit

          表單數(shù)據(jù)提交

          button

          按鈕

          radio

          單選框

          checkbox

          復(fù)選框

          file

          文件

          hidden

          隱藏的字段

          image

          定義圖像作為提交按鈕

          reset

          重置按鈕

          省略 type 屬性與 type="text"效果一樣, <input> 元素顯示為文本框。

          當(dāng) type 的值為text/password/number/時(shí),會(huì)有以下屬性對(duì) <input> 元素有效。

          屬性

          類型

          描述

          autocomplete

          string

          字符串on或off,表示<input>元素的輸入內(nèi)容可以被瀏覽器自動(dòng)補(bǔ)全。

          maxLength

          long

          指定<input>元素允許的最多字符數(shù)。

          size

          unsigned long

          表示<input>元素的寬度,這個(gè)寬度是以字符數(shù)來(lái)計(jì)量的。

          pattern

          string

          表示<input>元素的值應(yīng)該滿足的正則表達(dá)式

          placeholder

          string

          表示<input>元素的占位符,作為對(duì)元素的提示。

          readOnly

          boolean

          表示用戶是否可以修改<input>的值。

          min

          string

          表示<input>元素的最小數(shù)值或日期。

          max

          string

          表示<input>元素的最大數(shù)值或日期。

          selectionStart

          unsigned long

          表示選中文本的起始位置。如果沒有選中文本,返回光標(biāo)在<input>元素內(nèi)部的位置。

          selectionEnd

          unsigned long

          表示選中文本的結(jié)束位置。如果沒有選中文本,返回光標(biāo)在<input>元素內(nèi)部的位置。

          selectionDirection

          string

          表示選中文本的方向。可能的值包括forwardbackwardnone

          下面創(chuàng)建一個(gè) type="text" ,一次顯示 25 個(gè)字符,但最多允許顯示 50 個(gè)字符的文本框:

          <input type="text" size="25" maxlength="50" value="initial value">

          HTML 使用的 <textarea> 元素在 JavaScript 中對(duì)應(yīng)的是 HTMLTextAreaElement 類型。HTMLTextAreaElement類型繼承自 HTMLElement 接口:

          interface HTMLTextAreaElement extends HTMLElement {
              ...
          }

          HTMLTextAreaElement 類型有一些獨(dú)有的屬性和方法:

          • form:只讀屬性,返回對(duì)父表單元素的引用。如果此元素未包含在表單元素中,則它可以是同一文檔中 id 屬性同 form 值的 <form> 元素,如果沒有,該值為 null
          • type:只讀屬性,返回字符串textarea
          • valuestring類型,返回/設(shè)置控件中包含的原始值。
          • textLength:只讀屬性,long類型,返回<textarea>元素的value值的長(zhǎng)度。與 value.length 相同。
          • defaultValuestring類型,返回/設(shè)置textarea元素的默認(rèn)值。
          • placeholderstring類型,返回/設(shè)置 textarea 元素的占位符,作為對(duì)輸入內(nèi)容的提示。
          • rowsunsigned long類型,返回/設(shè)置textarea元素的可見文本行數(shù)。
          • colsunsigned long類型,返回/設(shè)置 textarea 元素的可見寬度。
          • autofocusboolean類型,返回/設(shè)置textarea元素在頁(yè)面加載時(shí)自動(dòng)獲取焦點(diǎn)。
          • name:返回/設(shè)置 textarea 元素的名稱。
          • disabledboolean類型,返回/設(shè)置 textarea 元素是否應(yīng)當(dāng)被禁用。
          • HTMLTextAreaElement.labelsNodeList類型,返回與此select元素關(guān)聯(lián)的標(biāo)簽元素列表。
          • maxLengthlong類型,返回/設(shè)置元素可以輸入的最大字符數(shù)。僅在值更改時(shí)才評(píng)估此約束。
          • minLengthlong類型,返回/設(shè)置元素可以輸入的最小字符數(shù)。僅在值更改時(shí)才評(píng)估此約束。
          • accessKeyboolean類型,返回/設(shè)置訪問 textarea 的鍵盤快捷鍵。
          • readOnlyboolean類型,返回/設(shè)置 textarea 元素是否為只讀。
          • requiredboolean 類型,返回/設(shè)置 textarea 元素是否為必填項(xiàng)。
          • tabIndexlong類型,返回/設(shè)置 textarea 元素的 tab 鍵導(dǎo)航順序中元素的位置。

          下面創(chuàng)建一個(gè)高度為 25,寬度為 5 的 <textarea> 多行文本框。它與 <input> 不同的是,初始值顯示在 <textarea>...</textarea> 之間:

          <textarea rows="25" cols="5">initial value</textarea>

          注意:處理文本框值的時(shí)候最好不要使用 DOM 方法,而應(yīng)該使用 value 屬性。

          選擇文本

          <input><textarea> 都支持 select() 方法,該方法用于選中文本框中的所有內(nèi)容。該方法的語(yǔ)法為:

          select(): void

          下面看一個(gè)示例:

          let textbox=document.forms[0].elements["input-box"];
          textbox.select();

          也可以在文本框獲得焦點(diǎn)時(shí),選中文本框的內(nèi)容:

          textbox.addEventListener("focus", (event)=> {
              event.target.select();
          });

          select 事件

          當(dāng)選中文本框中的文本或使用 select() 方法時(shí),會(huì)觸發(fā) select 事件。

          let textbox=document.forms[0].elements["textbox1"];
          textbox.addEventListener("select", (event)=> {
              console.log(`Text selected: ${textbox.value}`);
          });

          取得選中的文本

          HTML5 對(duì) select 事件進(jìn)行了擴(kuò)展,通過 selectionStartselectionEnd 屬性獲取文本選區(qū)的起點(diǎn)偏移量和終點(diǎn)偏移量。如下所示:

          function getSelectedText(textbox){
              return textbox.value.substring(textbox.selectionStart,
          textbox.selectionEnd);
          }

          注意:在 IE8 及更早版本不支持這兩個(gè)屬性。

          選中部分文本

          HTML5 提供了 setSelectionRange() 方法用于選中部分文本:

          setSelectionRange(start, end, direction): void;
          • start:被選中的第一個(gè)字符的位置索引,從 0 開始。如果這個(gè)值比元素的 value 長(zhǎng)度還大,則會(huì)被看做 value 最后一個(gè)位置的索引。
          • end:被選中的最后一個(gè)字符的下一個(gè)位置索引。如果這個(gè)值比元素的 value 長(zhǎng)度還大,則會(huì)被看做 value 最后一個(gè)位置的索引。
          • direction:該參數(shù)是可選的字符串類型,表示選擇方向,可以傳入 “forward”/“backward”/“none” 三個(gè)值。

          下面看一個(gè)例子:

          <input type="text" id="text-sample" size="20" value="Hello World!">
          <button onclick="selectText()">選中部分文本</button>
          <script>
              function selectText() {
                  let input=document.getElementById("text-sample");
                  input.focus();
                  input.setSelectionRange(4, 8);    // o Wo
              }
          </script>

          如果想要看到選中效果,必須讓文本框獲得焦點(diǎn)。

          輸入過濾

          不同文本框經(jīng)常需要保證輸入特定類型或格式的數(shù)據(jù),或許數(shù)據(jù)需要包含特定字符或必須匹配某個(gè)特定模式。而文本框并未提供驗(yàn)證功能,因此要配合 JavaScript 腳本實(shí)現(xiàn)輸入過濾功能。

          屏蔽字符

          有些輸入框需要出現(xiàn)或不出現(xiàn)特定字符。如果想要將輸入框變成只讀的,只需要使用 preventDefault()方法將按鍵都屏蔽:

          input.addEventListener("keypress", (event)=> {
              event.preventDefault();
          });

          而要屏蔽特定字符,就需要檢查事件的 charCode 屬性。如下所示,使用正則表達(dá)式實(shí)現(xiàn)只允許輸入數(shù)字的輸入框:

          input.addEventListener("keypress", (event)=> {
              if (!/\d/.test(event.key)) {
                  event.preventDefault();
              }
          });

          還有一個(gè)問題需要處理:復(fù)制、粘貼及涉及Ctrl 鍵的其他功能。在除IE 外的所有瀏覽器中,前面代碼會(huì)屏蔽快捷鍵Ctrl+C、Ctrl+V 及其他使用Ctrl 的組合鍵。因此,最后一項(xiàng)檢測(cè)是確保沒有按下Ctrl鍵,如下面的例子所示:

          textbox.addEventListener("keypress", (event)=> {
          if (!/\d/.test(String.fromCharCode(event.charCode)) &&
          event.charCode > 9 &&
          !event.ctrlKey){
          event.preventDefault();
          }
          });

          最后這個(gè)改動(dòng)可以確保所有默認(rèn)的文本框行為不受影響。這個(gè)技術(shù)可以用來(lái)自定義是否允許在文本框中輸入某些字符。

          處理剪貼板

          IE 是第一個(gè)實(shí)現(xiàn)了剪切板相關(guān)的事件以及通過JavaScript訪問剪切板數(shù)據(jù)的瀏覽器,其它瀏覽器在后來(lái)也都支持了相同的事件和剪切板的訪問,后來(lái) HTML5 將其納入了規(guī)范。以下是與剪切板相關(guān)的 6 個(gè)事件:

          • beforecopy:發(fā)生復(fù)制操作前觸發(fā)。
          • copy:發(fā)生復(fù)制操作時(shí)觸發(fā)。
          • beforecut:發(fā)生剪切操作前觸發(fā)。
          • cut:發(fā)生剪切操作時(shí)觸發(fā)。
          • beforepaste:發(fā)生粘貼操作前觸發(fā)。
          • paste:發(fā)生粘貼操作時(shí)觸發(fā)。

          剪切板事件的行為及相關(guān)對(duì)象會(huì)因?yàn)g覽器而異。在 Safari、Chrome 和 Firefox 中,beforecopybeforecutbeforepaste 事件只會(huì)在顯示文本框的上下文菜單時(shí)觸發(fā),但 IE 不僅在這種情況下觸發(fā),也會(huì)在 copycutpaste 事件在所有瀏覽器中都會(huì)按預(yù)期觸發(fā)。

          在實(shí)際的事件發(fā)生之前,通過beforecopybeforecutbeforepaste 事件可以在向剪貼板發(fā)送或從中檢索數(shù)據(jù)前修改數(shù)據(jù)。不過,取消這些事件并不會(huì)取消剪貼板操作。要阻止實(shí)際的剪貼板操作,必須取消 copycutpaste 事件。

          剪貼板的數(shù)據(jù)通過 clipboardData 對(duì)象來(lái)獲取,且clipboardData 對(duì)象提供 3 個(gè)操作數(shù)據(jù)的方法:

          • getData(format):從剪貼板獲取指定格式的數(shù)據(jù),接收的參數(shù)為指定獲取的數(shù)據(jù)格式,如果沒有指定數(shù)據(jù)格式或指定數(shù)據(jù)格式不存在,則此方法返回一個(gè)空字符串。
          • setData(format, data):給剪貼板賦予指定格式的數(shù)據(jù),第一個(gè)參數(shù)指定數(shù)據(jù)格式,第二個(gè)參數(shù)為第一個(gè)參數(shù)賦予數(shù)據(jù)。如果指定數(shù)據(jù)格式不存在,則將其添加到數(shù)據(jù)存儲(chǔ)的末尾;如果指定數(shù)據(jù)格式存在,則將數(shù)據(jù)替換在指定數(shù)據(jù)格式的位置。
          • clearData(format):刪除指定格式的數(shù)據(jù),接收的參數(shù)為指定要?jiǎng)h除的數(shù)據(jù)格式,如果指定格式不存在,則此方法不執(zhí)行任何操作;如果此參數(shù)為空字符串或未提供,則將刪除所有格式的數(shù)據(jù)。

          clipboardData 對(duì)象在 IE 中使用 window 獲取,在 Firefox、Safari 和 Chrome 中使用 event 獲取。為防止未經(jīng)授權(quán)訪問剪貼板,只能在剪貼板事件期間訪問 clipboardData 對(duì)象;IE 會(huì)在任何時(shí)候都暴露 clipboardData 對(duì)象。因此,要兼容兩者,最好在剪貼板事件期間使用該對(duì)象。

          function getClipboardText(event){
              var clipboardData=(event.clipboardData || window.clipboardData);
              return clipboardData.getData("text");
          }
          function setClipboardText (event, value){
              if (event.clipboardData){
                  return event.clipboardData.setData("text/plain", value);
              } else if (window.clipboardData){
                  return window.clipboardData.setData("text", value);
              }
          }

          如果文本框只有數(shù)字,那剪貼時(shí),就需要使用paste事件檢查剪貼板上的文本是否無(wú)效。如果無(wú)效,可以取消默認(rèn)行為:

          input.addEventListener("paste", (event)=> {
              let text=getClipboardText(event);
              if (!/^\d*$/.test(text)){
                  event.preventDefault();
              }
          });

          注意:Firefox、Safari和Chrome只允許在onpaste事件中訪問getData()方法。

          自動(dòng)切換

          在 JavaScript 中,可以用在當(dāng)前字段完成時(shí)自動(dòng)切換到下一個(gè)字段的方式來(lái)增強(qiáng)表單字段的易用性。比如,常用手機(jī)號(hào)分為國(guó)家好加手機(jī)號(hào)。因此,我們?cè)O(shè)置 2 個(gè)文本框:

          <form>
              <input type="text" name="phone1" id="phone-id-1" maxlength="4">
              <input type="text" name="phone2" id="phone-id-2" maxlength="11">
          </form>

          當(dāng)文本框輸入到最大允許字符數(shù)后,就把焦點(diǎn)移到下一個(gè)文本框,這樣可以增加表單的易用性并加速數(shù)據(jù)輸入。如下所示:

          <script>
              function tabForward(event){
                  let target=event.target;
                  if (target.value.length==target.maxLength){
                      let form=target.form;
                      for (let i=0, len=form.elements.length; i < len; i++) {
                          if (form.elements[i]==target) {
                              if (form.elements[i+1]) {
                                  form.elements[i+1].focus();
                              }
                              return;
                          }
                      }
                  }
              }
              let inputIds=["phone-id-1", "phone-id-2"];
              for (let id of inputIds) {
                  let textbox=document.getElementById(id);
                  textbox.addEventListener("keyup", tabForward);
              }
          </script>

          這里,tabForward() 函數(shù)通過比較用戶輸入文本的長(zhǎng)度與 maxLength 屬性的值來(lái)檢測(cè)輸入是否達(dá)到了最大長(zhǎng)度。如果兩者相等,就通過循環(huán)表中的元素集合找到當(dāng)前文本框,并把焦點(diǎn)設(shè)置到下一個(gè)元素。

          注意:上面的代碼只適用于之前既定的標(biāo)記,沒有考慮可能存在的隱藏字段。

          HTML5

          HTML5 新增了一些表單提交前,瀏覽器會(huì)基于指定的規(guī)則進(jìn)行驗(yàn)證,并在出錯(cuò)時(shí)顯示適當(dāng)?shù)腻e(cuò)誤信息。而驗(yàn)證會(huì)基于某些條件應(yīng)用到表單字段中。

          必填字段

          表單字段中添加 required 屬性,用于標(biāo)注該字段是必填項(xiàng),不填則無(wú)法提交。該屬性適用于<input><textarea><select>。如下所示:

          <input type="text" name="account" required>

          也可以通過 JavaScript 檢測(cè)對(duì)應(yīng)元素的 required 屬性來(lái)判斷表單字段是否為必填項(xiàng):

          let isRequired=document.forms[0].elements["account"].required;

          也可以檢測(cè)瀏覽器是否支持 required 屬性:

          let isRequiredSupported="required" in document.createElement("input");

          注意:不同瀏覽器處理必填字段的機(jī)制不同。Firefox、Chrome、IE 和Opera 會(huì)阻止表單提交并在相應(yīng)字段下面顯示有幫助信息的彈框,而Safari 什么也不做,也不會(huì)阻止提交表單。

          更多輸入類型

          HTML5 為 <input> 元素增加了幾個(gè)新的 type 值。如下所示:

          類型

          描述

          number

          數(shù)字值的輸入

          date

          日期輸入

          color

          顏色輸入

          range

          一定范圍內(nèi)的值的輸入

          month

          允許用戶選擇月份和年份

          week

          允許用戶選擇周和年份

          time

          允許用戶選擇時(shí)間(無(wú)時(shí)區(qū))

          datetime

          允許用戶選擇日期和時(shí)間(有時(shí)區(qū))

          datetime-local

          允許用戶選擇日期和時(shí)間(無(wú)時(shí)區(qū))

          email

          電子郵件地址的輸入

          search

          搜索(表現(xiàn)類似常規(guī)文本)

          tel

          電話號(hào)碼的輸入

          url

          URL地址的輸入

          這些輸入表名字段應(yīng)該輸入的數(shù)據(jù)類型,并且提供了默認(rèn)驗(yàn)證。如下所示:

          <input type="email" name="email">
          <input type="url" name="homepage">

          要檢測(cè)瀏覽器是否支持新類型,可以在 JavaScript 中創(chuàng)建 <input> 并設(shè)置 type 屬性,之后讀取它即可。老版本中會(huì)將我只類型設(shè)置為 text,而支持的會(huì)返回正確的值。如下所示:

          let input=document.createElement("input");
          input.type="email";
          let isEmailSupported=(input.type=="email");

          數(shù)值范圍

          而上面介紹的幾個(gè)如 number/range/datetime/datetime-local/date/month/week/time 幾個(gè)填寫數(shù)字的類型,都可以指定 min/max/step 等幾個(gè)與數(shù)值有關(guān)的屬性。step 屬性用于規(guī)定合法數(shù)字間隔,如 step="2",則合法數(shù)字應(yīng)該為 0、2、4、6,依次類推。如下所示:

          <input type="number" min="0" max="100" step="5" name="count">

          上面的例子是<input>中只能輸入從 0 到 100 中 5 的倍數(shù)。

          也可以使用 stepUp()stepDown() 方法對(duì) <input> 元素中的值進(jìn)行加減,它倆會(huì)接收一個(gè)可選參數(shù),用于表示加減的數(shù)值。如下所示:

          input.stepUp(); // 加1
          input.stepUp(5); // 加5
          input.stepDown(); // 減1
          input.stepDown(10); // 減10

          輸入模式

          HTML5 還為文本添加了 pattern 屬性,用于指定一個(gè)正則表達(dá)式。這樣就可以自己設(shè)置 <input> 元素的輸入模式了。如下所示:

          <input type="text" pattern="\d+" name="count">

          注意模式的開頭和末尾分別假設(shè)有^$。這意味著輸入內(nèi)容必須從頭到尾都嚴(yán)格與模式匹配。

          與新增的輸入類型一樣,指定 pattern 屬性也不會(huì)阻止用戶輸入無(wú)效內(nèi)容。模式會(huì)應(yīng)用到值,然后瀏覽器會(huì)知道值是否有效。通過訪問 pattern 屬性可以讀取模式:

          let pattern=document.forms[0].elements["count"].pattern;

          使用如下代碼可以檢測(cè)瀏覽器是否支持pattern 屬性:

          let isPatternSupported="pattern" in document.createElement("input");

          檢測(cè)有效性

          HTML5 新增了 checkValidity() 方法,用來(lái)檢測(cè)表單中任意給定字段是否有效。而判斷的條件是約束條件,因此必填字段如果沒有值會(huì)被視為無(wú)效,字段值不匹配 pattern 屬性也會(huì)被視為無(wú)效。如下所示:

          if (document.forms[0].elements[0].checkValidity()){
              // 字段有效,繼續(xù)
          } else {
              // 字段無(wú)效
          }

          要檢查整個(gè)表單是否有效,可以直接在表單上調(diào)用checkValidity()方法。這個(gè)方法會(huì)在所有字段都有效時(shí)返回true,有一個(gè)字段無(wú)效就會(huì)返回false

          if(document.forms[0].checkValidity()){
              // 表單有效,繼續(xù)
          } else {
              // 表單無(wú)效
          }

          validity 屬性會(huì)返回一個(gè)ValidityState 對(duì)象,表示 <input> 元素的校驗(yàn)狀態(tài)。返回的對(duì)象包含一些列的布爾值的屬性:

          • customError:如果設(shè)置了 setCustomValidity() 就返回 true,否則返回false。
          • patternMismatch:如果字段值不匹配指定的 pattern 屬性則返回true。
          • rangeOverflow:如果字段值大于 max 的值則返回true。
          • rangeUnderflow:如果字段值小于 min 的值則返回true。
          • stepMisMatch:如果字段值與 minmaxstep 的值不相符則返回true。
          • tooLong:如果字段值的長(zhǎng)度超過了 maxlength 屬性指定的值則返回true。
          • typeMismatch:如果字段值不是 "email""url" 要求的格式則返回true。
          • valid:如果其他所有屬性的值都為false 則返回true。與checkValidity()的條件一致。
          • valueMissing:如果字段是必填的但沒有值則返回true。

          因此,通過 validity 屬性可以檢查表單字段的有效性,從而獲取更具體的信息,如下所示:

          if (input.validity && !input.validity.valid){
              if (input.validity.valueMissing){
                  console.log("請(qǐng)指定值.")
              } else if (input.validity.typeMismatch){
                  console.log("請(qǐng)指定電子郵件地址.");
              } else {
                  console.log("值無(wú)效.");
              }
          }

          禁用驗(yàn)證

          通過指定 novalidate 屬性可以禁止對(duì)表單進(jìn)行任何驗(yàn)證:

          <form method="post" action="/signup" novalidate>
              <!-- 表單元素 -->
          </form>

          也可以在 JavaScript 通過 noValidate 屬性設(shè)置,為 true 表示屬性存在,為 false 表示屬性不存在:

          document.forms[0].noValidate=true; // 關(guān)閉驗(yàn)證

          如果一個(gè)表單中有多個(gè)提交按鈕,那么可以給特定的提交按鈕添加formnovalidate 屬性,指定通過該按鈕無(wú)需驗(yàn)證即可提交表單:

          <form method="post" action="/foo">
              <!-- 表單元素 -->
              <input type="submit" value="注冊(cè)提交">
              <input type="submit" formnovalidate name="btnNoValidate"
          value="沒有驗(yàn)證的提交按鈕">
          </form>

          也可以使用 JavaScript 設(shè)置 formNoValidate 屬性:

          // 關(guān)閉驗(yàn)證
          document.forms[0].elements["btnNoValidate"].formNoValidate=true;

          總結(jié)

          以上總結(jié)了 <input><textarea> 兩個(gè)元素的一些功能,主要是 <input> 元素可以通過設(shè)置 type 屬性獲取不同類型的輸入框,可以通過監(jiān)聽鍵盤事件并檢測(cè)要插入的字符來(lái)控制文本框的內(nèi)容。

          還有一些與剪貼板相關(guān)的事件,并對(duì)剪貼的內(nèi)容進(jìn)行檢測(cè)。還介紹了一些 HTML5 新增的屬性和方法和新增的更多的 <input> 元素的類型,和一些與驗(yàn)證相關(guān)的屬性和方法。

          Value 注入過程

          從一個(gè)最簡(jiǎn)單的程序開始:

          @Configuration
          @PropertySource("classpath:application.properties")
          public class ValueAnnotationDemo {
          
              @Value("${username}")
              private String username;
          
              public static void main(String[] args) {
                  AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(ValueAnnotationDemo.class);
          
                  System.out.println(context.getBean(ValueAnnotationDemo.class).username);
          
                  context.close();
              }
          }

          application.properties 文件內(nèi)容:

          username=coder-xiao-hei
          

          AutowiredAnnotationBeanPostProcessor 負(fù)責(zé)來(lái)處理 @Value ,此外該類還負(fù)責(zé)處理 @Autowired@Inject

          AutowiredAnnotationBeanPostProcessor 中有兩個(gè)內(nèi)部類:AutowiredFieldElementAutowiredMethodElement

          當(dāng)前為 Field 注入,定位到 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject 方法。

          通過 debug 可知,整個(gè)調(diào)用鏈如下:

          • AutowiredFieldElement#injectDefaultListableBeanFactory#resolveDependencyDefaultListableBeanFactory#doResolveDependencyAbstractBeanFactory#resolveEmbeddedValue

          通過上述的 debug 跟蹤發(fā)現(xiàn)可以通過調(diào)用 ConfigurableBeanFactory#resolveEmbeddedValue 方法可以獲取占位符的值。

          這里的 resolver 是一個(gè) lambda表達(dá)式,繼續(xù) debug 我們可以找到具體的執(zhí)行方法:

          到此,我們簡(jiǎn)單總結(jié)下:

          1. @Value 的注入由 AutowiredAnnotationBeanPostProcessor 來(lái)提供支持
          2. AutowiredAnnotationBeanPostProcessor 中通過調(diào)用 ConfigurableBeanFactory#resolveEmbeddedValue來(lái)獲取占位符具體的值
          3. ConfigurableBeanFactory#resolveEmbeddedValue 其實(shí)是委托給了 ConfigurableEnvironment 來(lái)實(shí)現(xiàn)

          Spring Environment

          Environment 概述

          https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-environment

          The Environment interface is an abstraction integrated in the container that models two key aspects of the application environment: profiles and properties.

          A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or with annotations. The role of the Environment object with relation to profiles is in determining which profiles (if any) are currently active, and which profiles (if any) should be active by default.

          Properties play an important role in almost all applications and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Map objects, and so on. The role of the Environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.

          Environment 是對(duì) profiles 和 properties 的抽象:

          • 實(shí)現(xiàn)了對(duì)屬性配置的統(tǒng)一存儲(chǔ),同時(shí) properties 允許有多個(gè)來(lái)源
          • 通過 Environment profiles 來(lái)實(shí)現(xiàn)條件化裝配 Bean

          現(xiàn)在我們主要來(lái)關(guān)注 Environment 對(duì) properties 的支持。

          StandardEnvironment

          下面,我們就來(lái)具體看一下 AbstractApplicationContext#finishBeanFactoryInitialization 中的這個(gè) lambda 表達(dá)式。

          strVal -> getEnvironment().resolvePlaceholders(strVal)
          

          首先,通過 AbstractApplicationContext#getEnvironment 獲取到了 ConfigurableEnvironment 的實(shí)例對(duì)象,這里創(chuàng)建的其實(shí)是 StandardEnvironment 實(shí)例對(duì)象。

          StandardEnvironment 中,默認(rèn)添加了兩個(gè)自定義的屬性源,分別是:systemEnvironment 和 systemProperties。

          也就是說(shuō),@Value 默認(rèn)是可以注入 system properties 和 system environment 的。

          PropertySource

          StandardEnvironment 繼承了 AbstractEnvironment

          AbstractEnvironment 中的屬性配置被存放在 MutablePropertySources 中。同時(shí),屬性占位符的數(shù)據(jù)也來(lái)自于此。

          MutablePropertySources 中存放了多個(gè) PropertySource ,并且這些 PropertySource 是有順序的。

          PropertySource 是 Spring 對(duì)配置屬性源的抽象。

          name 表示當(dāng)前屬性源的名稱。source 存放了當(dāng)前的屬性。

          讀者可以自行查看一下最簡(jiǎn)單的基于 Map 的實(shí)現(xiàn):MapPropertySource

          配置屬性源

          有兩種方式可以進(jìn)行屬性源配置:使用 @PropertySource 注解,或者通過 MutablePropertySources 的 API。例如:

          @Configuration
          @PropertySource("classpath:application.properties")
          public class ValueAnnotationDemo {
          
              @Value("${username}")
              private String username;
          
              public static void main(String[] args) {
                  AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(ValueAnnotationDemo.class);
          
                  Map<String, Object> map=new HashMap<>();
                  map.put("my.name", "coder小黑");
                  context.getEnvironment()
                          .getPropertySources()
                          .addFirst(new MapPropertySource("coder-xiaohei-test", map));
              }
          }

          總結(jié)

          1. Spring 通過 PropertySource 來(lái)抽象配置屬性源, PropertySource 允許有多個(gè)。MutablePropertySources
          2. 在 Spring 容器啟動(dòng)的時(shí)候,會(huì)默認(rèn)加載 systemEnvironment 和 systemProperties。StandardEnvironment#customizePropertySources
          3. 我們可以通過 @PropertySource 注解或者 MutablePropertySources API 來(lái)添加自定義配置屬性源
          4. Environment 是 Spring 對(duì) profiles 和 properties 的抽象,默認(rèn)實(shí)現(xiàn)是 StandardEnvironment
          5. @Value 的注入由 AutowiredAnnotationBeanPostProcessor 來(lái)提供支持,數(shù)據(jù)源來(lái)自于 PropertySource
          public class Demo {
          
              @Value("${os.name}") // 來(lái)自 system properties
              private String osName;
          
              @Value("${user.name}") // 通過 MutablePropertySources API 來(lái)注冊(cè)
              private String username;
          
              @Value("${os.version}") // 測(cè)試先后順序
              private String osVersion;
          
              public static void main(String[] args) {
                  AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext();
                  context.register(Demo.class);
                  ConfigurableEnvironment environment=context.getEnvironment();
                  MutablePropertySources propertySources=environment.getPropertySources();
          
                  Map<String, Object> source=new HashMap<>();
                  source.put("user.name", "xiaohei");
                  source.put("os.version", "version-for-xiaohei");
                  // 添加自定義 MapPropertySource,且放在第一位
                  propertySources.addFirst(new MapPropertySource("coder-xiao-hei-test", source));
                  // 啟動(dòng)容器
                  context.refresh();
          
                  Demo bean=context.getBean(Demo.class);
                  // Mac OS X
                  System.out.println(bean.osName);
                  // xiaohei
                  System.out.println(bean.username);
                  // version-for-xiaohei
                  System.out.println(bean.osVersion);
                  // Mac OS X
                  System.out.println(System.getProperty("os.name"));
                  // 10.15.7
                  System.out.println(System.getProperty("os.version"));
                  // xiaohei
                  System.out.println(environment.getProperty("user.name"));
                  //xiaohei
                  System.out.println(environment.resolvePlaceholders("${user.name}"));
          
                  context.close();
              }
          }

          簡(jiǎn)易版配置中心

          @Value 支持配置中心數(shù)據(jù)來(lái)源

          @Value 的值都來(lái)源于 PropertySource ,而我們可以通過 API 的方式來(lái)向 Spring Environment 中添加自定義的 PropertySource

          在此處,我們選擇通過監(jiān)聽 ApplicationEnvironmentPreparedEvent 事件來(lái)實(shí)現(xiàn)。

          @Slf4j
          public class CentralConfigPropertySourceListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
          
              private final CentralConfig centralConfig=new CentralConfig();
          
              @Override
              public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
                  centralConfig.loadCentralConfig();
                  event.getEnvironment().getPropertySources().addFirst(new CentralConfigPropertySource(centralConfig));
              }
          
          
              static class CentralConfig {
                  private volatile Map<String, Object> config=new HashMap<>();
          
                  private void loadCentralConfig() {
                      // 模擬從配置中心獲取數(shù)據(jù)
                      config.put("coder.name", "xiaohei");
                      config.put("coder.language", "java");
          
                      new Thread(() -> {
                          try {
                              TimeUnit.SECONDS.sleep(10);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          // 模擬配置更新
                          config.put("coder.language", "java222");
                          System.out.println("update 'coder.language' success");
                      }).start();
          
                  }
              }
          
              static class CentralConfigPropertySource extends EnumerablePropertySource<CentralConfig> {
          
                  private static final String PROPERTY_SOURCE_NAME="centralConfigPropertySource";
          
                  public CentralConfigPropertySource(CentralConfig source) {
                      super(PROPERTY_SOURCE_NAME, source);
                  }
          
                  @Override
                  @Nullable
                  public Object getProperty(String name) {
                      return this.source.config.get(name);
                  }
          
                  @Override
                  public boolean containsProperty(String name) {
                      return this.source.config.containsKey(name);
                  }
          
                  @Override
                  public String[] getPropertyNames() {
                      return StringUtils.toStringArray(this.source.config.keySet());
                  }
              }
          }

          通過 META-INF/spring.factories 文件來(lái)注冊(cè):

          org.springframework.context.ApplicationListener=com.example.config.CentralConfigPropertySourceListener
          

          實(shí)時(shí)發(fā)布更新配置

          一般來(lái)說(shuō)有兩種方案:

          • 客戶端拉模式:客戶端長(zhǎng)輪詢服務(wù)端,如果服務(wù)端數(shù)據(jù)發(fā)生修改,則立即返回給客戶端
          • 服務(wù)端推模式:發(fā)布更新配置之后,由配置中心主動(dòng)通知各客戶端在這里我們選用服務(wù)端推模式來(lái)進(jìn)行實(shí)現(xiàn)。在集群部署環(huán)境下,一旦某個(gè)配置中心服務(wù)感知到了配置項(xiàng)的變化,就會(huì)通過 redis 的 pub/sub 來(lái)通知客戶端和其他的配置中心服務(wù)節(jié)點(diǎn)輕量級(jí)實(shí)現(xiàn)方案,代碼簡(jiǎn)單,但強(qiáng)依賴 redis,pub/sub 可以會(huì)有丟失

          自定義注解支持動(dòng)態(tài)更新配置

          Spring 的 @Value 注入是在 Bean 初始化階段執(zhí)行的。在程序運(yùn)行過程當(dāng)中,配置項(xiàng)發(fā)生了變更, @Value 并不會(huì)重新注入。

          我們可以通過增強(qiáng) @Value 或者自定義新的注解來(lái)支持動(dòng)態(tài)更新配置。這里小黑選擇的是第二種方案,自定義新的注解。

          @Target(ElementType.FIELD)
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface ConfigValue {
              String value();
          }
          @Component
          public class ConfigValueAnnotationBeanPostProcessor implements BeanPostProcessor, EnvironmentAware {
          
              private static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER=    new PropertyPlaceholderHelper(
                              SystemPropertyUtils.PLACEHOLDER_PREFIX,
                              SystemPropertyUtils.PLACEHOLDER_SUFFIX,
                              SystemPropertyUtils.VALUE_SEPARATOR,
                              false);
          
              private MultiValueMap<String, ConfigValueHolder> keyHolder=new LinkedMultiValueMap<>();
          
              private Environment environment;
          
              @Override
              public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
          
                  ReflectionUtils.doWithFields(bean.getClass(),
                          field -> {
                              ConfigValue annotation=AnnotationUtils.findAnnotation(field, ConfigValue.class);
                              if (annotation==null) {
                                  return;
                              }
                              String value=environment.resolvePlaceholders(annotation.value());
                              ReflectionUtils.makeAccessible(field);
                              ReflectionUtils.setField(field, bean, value);
                              String key=PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(annotation.value(), placeholderName -> placeholderName);
                              ConfigValueHolder configValueHolder=new ConfigValueHolder(bean, beanName, field, key);
                              keyHolder.add(key, configValueHolder);
                          });
          
                  return bean;
              }
          
              /**
               * 當(dāng)配置發(fā)生了修改
               *
               * @param key 配置項(xiàng)
               */
              public void update(String key) {
                  List<ConfigValueHolder> configValueHolders=keyHolder.get(key);
                  if (CollectionUtils.isEmpty(configValueHolders)) {
                      return;
                  }
                  String property=environment.getProperty(key);
                  configValueHolders.forEach(holder -> ReflectionUtils.setField(holder.field, holder.bean, property));
              }
          
              @Override
              public void setEnvironment(Environment environment) {
                  this.environment=environment;
              }
          
              @AllArgsConstructor
              static class ConfigValueHolder {
                  final Object bean;
                  final String beanName;
                  final Field field;
                  final String key;
              }
          }

          主測(cè)試代碼:

          @SpringBootApplication
          public class ConfigApplication {
          
              @Value("${coder.name}")
              String coderName;
          
              @ConfigValue("${coder.language}")
              String language;
          
              public static void main(String[] args) throws InterruptedException {
                  ConfigurableApplicationContext context=SpringApplication.run(ConfigApplication.class, args);
                  ConfigApplication bean=context.getBean(ConfigApplication.class);
                  // xiaohei
                  System.out.println(bean.coderName);
                  // java
                  System.out.println(bean.language);
          
                  ConfigValueAnnotationBeanPostProcessor processor=context.getBean(ConfigValueAnnotationBeanPostProcessor.class);
          
                  // 模擬配置發(fā)生了更新
                  TimeUnit.SECONDS.sleep(10);
          
                  processor.update("coder.language");
          
                  // java222
                  System.out.println(bean.language);
              }
          }

          作者:Coder小黑

          原文鏈接:https://www.cnblogs.com/coderxiaohei/p/14026219.html


          主站蜘蛛池模板: 亚洲国产AV一区二区三区四区| 精品亚洲A∨无码一区二区三区| 亚洲香蕉久久一区二区| 中文无码精品一区二区三区| 国产在线乱子伦一区二区| 一区视频在线播放| 色狠狠色狠狠综合一区| 中文字幕亚洲乱码熟女一区二区| 国产精品高清一区二区人妖| 无人码一区二区三区视频| 国产一区二区福利久久| 亚洲精品伦理熟女国产一区二区| 日韩一区二区电影| 国产综合无码一区二区三区| 国产MD视频一区二区三区| 亚洲AV成人精品一区二区三区| 中文字幕在线观看一区| 视频在线一区二区| 国产精品日韩一区二区三区| 亚洲欧洲专线一区| 在线日产精品一区| 88国产精品视频一区二区三区| 清纯唯美经典一区二区| 一区二区三区在线观看中文字幕 | A国产一区二区免费入口| 鲁丝丝国产一区二区| 竹菊影视欧美日韩一区二区三区四区五区| 国产凹凸在线一区二区| 国产一区二区高清在线播放| 国产精品99无码一区二区| 一区二区中文字幕在线观看| 精品人妻一区二区三区四区| 日韩AV片无码一区二区不卡 | 久久99精品波多结衣一区| 国模大尺度视频一区二区| 一区二区三区无码高清视频| 亚洲一区二区三区在线播放| 一区二区三区免费精品视频| 久久精品无码一区二区app| 国产精品污WWW一区二区三区 | 一区二区中文字幕|