整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          輕松去掉SpringBoot中FreeMarker繁復的自定義指令配置(附源碼)

          reemarker是一個基于Java語言的多用途,輕量級模板引擎。它提供了很多內置的語法指令,例如條件選中,迭代,賦值,字符串和算術運算,格式化,宏定義等等。Freemarker最初是為了在Web MVC應用程序框架中生成HTML頁面而創建的,但它并不綁定到Servlet,HTML或者任何其他與Web相關的內容。此外,Freemarker還應用與非Web應用程序環境中。

          ? Freemarker自身已經提供了很多有用的語法指令,通過使用這些指令,能夠很輕松的讀取和操作Model中的數據。除此之外,Freemarker還允許開發者擴展Freemarker語法指令,自定義滿足具體需求的新指令。本文將著重講解利用Java的反射機制去優化SpringBoot與Freemarker整合后零配置自定義指令。

          1. FreeMarker是什么?

          ? 在開始之前,先花一兩分鐘回顧一下Freemarker的基本概念。Apache FreeMarker是一個基于Java庫的視圖模板引擎,主要用于根據模板和模型數據渲染生成文本輸出(HTML網頁,電子郵件,配置文件,源代碼等等)。FreeMarker模板使用其特定的模板語言(FTL)進行編寫,這是一種簡單的專用標記語言。通常,使用通用編程語言(如Java)來準備模型數據;然后,Apache FreeMarker使用模板渲染準備好的數據。在模板中,開發者可以專注于如何顯示數據,在模型中,你則專注于數據的包裝。下面通過一張官圖了解一下FreeMarker的基本工作原理:

          2. SpringBoot整合Apache FreeMaker

          ? 在SpringBoot中使用Apache FreeMarker(以下簡稱FreeMarker)是一件很容易的事情。首先,在pom.xml文件中添加Freemarker的starter依賴,IDE會根據當前SpringBoot的版本信息自動下載合適版本的FreeMarker依賴包到類路徑下,pom.xml的配置如下:

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

          然后,你只需要在application.yml或application.properties配置文件中指定FreeMarker的相關參數(例如編碼方式,緩存,模板前綴/后綴,格式化樣式等等)即可。通常,SpringBoot已經為FreeMarker提供了一些默認的配置,我們可以在org.springframework.boot.autoconfigure.freemarker包下的FreeMarkerProperties類中找到相關的默認值,內容如下:

          @ConfigurationProperties(
              prefix = "spring.freemarker"
          )
          public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {
              public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";
              public static final String DEFAULT_PREFIX = "";
              public static final String DEFAULT_SUFFIX = ".ftl";
              private Map<String, String> settings = new HashMap();
              private String[] templateLoaderPath = new String[]{"classpath:/templates/"};
              private boolean preferFileSystemAccess = true;
          
              public FreeMarkerProperties() {
                  super("", ".ftl");
              }
          
              public Map<String, String> getSettings() {
                  return this.settings;
              }
          
              public void setSettings(Map<String, String> settings) {
                  this.settings = settings;
              }
          
              public String[] getTemplateLoaderPath() {
                  return this.templateLoaderPath;
              }
          
              public boolean isPreferFileSystemAccess() {
                  return this.preferFileSystemAccess;
              }
          
              public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
                  this.preferFileSystemAccess = preferFileSystemAccess;
              }
          
              public void setTemplateLoaderPath(String... templateLoaderPaths) {
                  this.templateLoaderPath = templateLoaderPaths;
              }
          }
          

          我們可以在配置文件中對自動配置的默認值進行覆蓋,以滿足自身的需求,例如我們可以做如下的配置:

          spring:
            freemarker:
              cache: false
              suffix: .html
              settings:
                datetime_format:  yyyy-MM-dd HH:mm
                number_format:  0.##
          

          在上述的配置中,將默認的模板后綴名由.ftl修改為.html,關閉了FreeMarker的緩存模板數據,同時設置了模板中日期的格式化模板和數值的格式化模板。

          ? 經過上述的兩個步驟,SpringBoot與Apache FreeMarker的整合工作就完成了。接下來,將了解如何擴展FreeMarker的語法指令。

          3. 自定義FreeMarker語法指令

          ? 自定義FreeMarker語法指令功能讓開發者在操作FreeMarker模板時更加靈活多樣。基于FreeMarker實現自定義指令只需讓自定義指令類實現TemplateDirectiveModel接口并重寫execute()方法即可。execute()方法是實現自定義指令的核心。

          注意:

          TemplateDirectiveModel是在FreeMarker 2.3.11版本中才引入的接口,如果你使用的是舊版本的FreeMarker,則需要實現TemplateTransformModel類。

          下面是實現FreeMarker的一個示例代碼:

          接下來,我們只需要使用FreeMarker的Configuration類,將此自定義指令類的實例添加到FreeMarker共享變量中便可在模板中使用自己的指令。下面是配置示例:

          Configuration cfg = new Configuration(Configuration.VERSION_2_2_27);
          cfg.setSharedVariable("myDirective",new MyTemplateDirective());
          

          注意:

          在設置FreeMarker共享變量時,需要為自定義指令指定一個名稱。

          4. SpringBoot中配置自定義指令

          ? FreeMarker自定義指令的配置方式有很多種,在SpringBoot中配置FreeMarker自定義自定更為簡單,下面介紹其中一種方式。我們可以利用SpringBoot的組件掃描機制,統一管理FreeMarker自定義指令的配置工作,例如:

          @Component
          public class FreemarkerCustomDirectveManager {
          
              @Autowired
              private Configuration cfg;
              @Autowired
              private ApplicationContext app;
          
              @PostConstruct
              public void setSharedVariable()throws TemplateModelException{
                  cfg.setSharedVariable ( "direct1",app.getBean ( Direct1.class ) );
                  cfg.setSharedVariable ( "direct2",app.getBean ( Direct2.class ) );
                  cfg.setSharedVariable ( "direct3",app.getBean ( Direct3.class ) );
                  cfg.setSharedVariable ( "direct4",app.getBean ( Direct4.class ) );
                  cfg.setSharedVariable ( "direct5",app.getBean ( Direct5.class ) );
              }
          }
          

          在上述的配置中,@Component注解的作用是將普通的POJO實例化到Spring的容器中,而@PostConstruct注解的作用是在服務器加載Servlet的時候有且只執行一次被其標注的方法。另外,我們通過ApplicationContext(應用上下文)獲取自定義指令類的實例,并將其設置為FreeMarker的共享變量。

          提示:

          在Spring Framework中,被@PostConstruct注釋的方法會在類的init()方法執行之前,構造方法執行之后被執行,該注解注釋的方法在整個Bean初始化過程中被執行的順序如下:

          Constructor(構造方法) -> @Autowired(依賴注入)-> @PostConstruct(注釋方法)

          ? 然而,上述的這種方式有一個不好的地方,當新增一個FreeMarker自定義指令時,就需要手動修改一次配置代碼,當項目中自定義指令數量多的時候,指令名稱的命名規范以及配置將是一件很繁重的事情。接下來,將結合Java反射機制和Java抽象類來優化FreeMarker自定義指令配置問題。

          5.優化FreeMarker自定義指令配置

          ? 優化配置的重點在于零配置,首先,可以通過一定的規則生成指令的名稱,然后,通過Java的反射機制獲取自定義指令對象實例,最后將實例添加到FreeMarker的共享變量中,變量名為具有一定規則的名稱。

          5.1 抽象自定義指令

          ? 首先,我們需要定義一個抽象的自定義指令類,在此類中主要完成指令的自動配置工作,指令的細節將有具體的子類負責。由于是在SpringBoot中使用FreeMarker,我們可以用@Service此抽象指令類進行標記,這樣改類的子類就都會被Spring容器所管理,其次,使用@PostConstruct注解標注指令配置方法,最后,將excute()方法暴露給子類去執行。抽象自定義指令類的源代碼清單如下:

          @Service
          public abstract class TemplateDirective extends ApplicationObjectSupport implements TemplateDirectiveModel {
          
              @Autowired
              FreeMarkerConfigurer cfg;
          
              @PostConstruct
              public void config() throws TemplateModelException{
                  //1. 獲取當前類名
                  String className = this.getClass().getName();
                  //2.截取類名(例如Directive.java截取為Directive)
                  className = className.substring(className.lastIndexOf(".")+1);
                  //3.獲得Bean的名稱
                  String beanName = StringUtils.uncapitalize(className);
                  //4.生成指令名稱
                  String directiveName = "ramostear_"+ NameUtils.humpToUnderline(beanName);
                  //5.設置指令
                  cfg.getConfiguration()
                     .setSharedVariable(tagName,this.getApplicationContext().getBean(beanName));
              }
          
              @Override
              public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
                  try {
                      execute(new DirectiveHandler(env, params, loopVars, body));
                  }catch (Exception ex){
                      try {
                          throw ex;
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }
          
              //暴露給子類
              abstract public void execute(DirectiveHandler handler) throws Exception;
          
          }
          

          在此類中,自定義的DirectiveHandlere類對FreeMarker的Enviroment,params,TemplateModel以及TemplateDirectiveBody做了封裝,其源代碼清單如下:

          public class DirectiveHandler {
          
              private Environment env;
          
              private Map<String, TemplateModel> params;
          
              private TemplateModel[] loopVars;
          
              private TemplateDirectiveBody body;
          
              private Environment.Namespace namespace;
          
          
              /**
               * 構造函數
               * @param env
               * @param params
               * @param loopVars
               * @param body
               */
              public DirectiveHandler(Environment env,Map<String,TemplateModel> params,TemplateModel[] loopVars,TemplateDirectiveBody body){
                  this.env = env;
                  this.loopVars = loopVars;
                  this.params = params;
                  this.body = body;
                  this.namespace = env.getCurrentNamespace();
              }
          
              public void render() throws IOException, TemplateException {
                  Assert.notNull(body, "must have template directive body");
                  body.render(env.getOut());
              }
          
              ...
          
              /**
               * 包裝對象
               * @param object
               * @return
               * @throws TemplateModelException
               */
              public TemplateModel wrap(Object object) throws TemplateModelException {
                  return env.getObjectWrapper().wrap(object);
              }
          
              /**
               * 獲取局部變量
               * @param name
               * @return
               * @throws TemplateModelException
               */
              public TemplateModel getEnvModel(String name) throws TemplateModelException {
                  return env.getVariable(name);
              }
          
              public void write(String text) throws IOException {
                  env.getOut().write(text);
              }
          
              private TemplateModel getModel(String name) {
                  return params.get(name);
              }
          

          另外,NameUtils.humpToUnderline()方法主要是將類名的駝峰命名格式轉換為下劃線分割的新式,例如:

          BeanNameFactory ->NameUtils.humpToUnderline()-> bean_name_factory
          

          生成指令名稱的格式大家可以自定義,這里僅僅提供其中一種轉換方式。

          接下來,我們便可繼承TemplateDirective類,實現具體的自定義FreeMarker指令,而無需進行任何配置。

          5.2 實現自定義指令

          ? 前面我們已經對FreeMarker自定義指令做了抽象,并在其中完成了指令的自動配置工作。接下來,我們只需要繼承抽象的自定義指令類,專注完成具體的業務邏輯即可。例如,我們需要有一個生成文章歸檔的自定義指令,下面是具體的實現細節:

          @Service
          public class Archives extends TemplateDirective {
          
              @Autowired
              private ArchiveService archiveService;
          
              @Override
              public void execute(DirectiveHandler handler) throws TemplateException, IOException {
                  List<ArchiveVO> archiveVOList = archiveService.archives();
                  handler.put("results",archiveVOList).render();
              }
          }
          

          只需這樣,無需其他的配置,我們便可在FreeMarker模板中使用<@ramostear_archives></@ramostear_archives>指令來獲取文章歸檔信息。例如:

          <div class="archive-card">
              <h2 class="archive-title">
                  博客歸檔
              </h2>
              <ul class="archive-box">
                  <@ramostear_archives>
                      <#list results as archive>
                          <li class="archive-item">
                              <a href="/${archive.name}">${archive.name}----${archive.count}篇</a>
                          </li>
                      </#list>
                  </@ramostear_archives>
              </ul>
          </div>
          

          6. 總結

          ? 通過上述的重構與改造,使得FreeMarker自定義標簽的配置工作得到簡化,同時指令的命名也以統一的格式和規范進行管理。另外,由于統一了命名方式,開發人員在看到自定義指令類時便可知道模板中對應的指令名稱是什么,提高了代碼的可讀性。

          最后附上項目測試短動畫和github項目地址。

          你可以訪問下面的地址獲取本文附屬的項目源碼:

          https://github.com/ramostear/springboot-freemarker

          • 件版本:
          • 軟件大小:
          • 軟件授權:
          • 適用平臺:
          • http://dl.pconline.com.cn/download/830946.html

          win10系統怎么取消代理服務器?

          1、按下“Win+R”組合鍵打開運行,輸入: 按下回車鍵打開internet 屬性;

          2、在internet 屬性 界面的“連接”選卡界面點擊“局域網設置”;

          3、將“為LAN使用代理服務器(這些設置不用于撥號或VPN連接)”前面的勾去掉,若 【自動檢測設置】已勾選則取消勾選,若【自動檢測設置】未勾選則將其勾選,然后點擊確定

          4、在internet 屬性界面點擊確定即可。

          在 Web 自動化測試中,處理彈框是一項常見的任務。彈框可能包括警告、確認和提示框。Playwright 是一個功能強大的自動化測試工具,提供了處理這些彈框的靈活方法。在本文中,我們將深入探討如何使用 Python 編寫代碼來處理各種類型的彈框。

          彈框的分類

          彈框通常分為3種,分別為alert,confirm,promot。

          • alert彈框:只有信息和確定按鍵
          • confirm彈框:在alert彈窗基礎上增加了取消按鈕
          • prompt彈框:在confirm的基礎上增加了可輸入文本內容的功能

          三種彈框的圖像分別如下:

          出現彈框時,我們需要點擊確定,彈框才會消失,當我們使用selenium處理彈框時,通常是使用switch_to.alert來進行處理,具體代碼如下:

          pythonfrom selenium import webdriver
           
          driver = webdriver.Chrome()
          driver.switch_to.alert.accept() #確定、同意;三種彈窗都可使用
          driver.switch_to.alert.dismiss() #取消、不同意;confirm和prompt彈窗中使用
          title = driver.switch_to.alert.text #打印彈窗信息
          alert = driver.switch_to.alert #獲取alert對象
          alert.send_keys() #Prompt彈窗中輸入內容
          

          下面,我們來看看playwright是如何處理彈框的。

          dialog 事件監聽

          playwright 框架可以監聽dialog事件,不管你alert 什么時候彈出來,監聽到事件就自動處理了。

          當出現 JavaScript 對話框時發出,例如alert、prompt。偵聽器必須dialog.accept()dialog.dismiss()對話框 - 否則頁面將凍結等待對話框,并且單擊等操作將永遠不會完成。

          處理代碼如下:

          pythonpage.on("dialog", lambda dialog: dialog.accept())
          

          注:當沒有page.on("dialog")偵聽器存在時,所有對話框都會自動關閉。

          代碼如下:

          pythonpage.on("dialog", handler)
          

          處理警告框

          警告框通常用于向用戶顯示一條消息,并要求用戶采取某種操作。在 Playwright 中,我們可以使用 page.on('dialog') 方法來處理警告框。以下是一個示例:

          pythonfrom playwright.sync_api import sync_playwright
          
          with sync_playwright() as p:
              browser = p.chromium.launch()
              page = browser.new_page()
              
              def on_dialog(dialog):
                  print('Dialog message:', dialog.message)
                  dialog.accept()
          
              page.on('dialog', on_dialog)
              page.goto('https://example.com')
          
          

          在這個示例中,我們定義了一個 on_dialog 函數來處理彈框。在這個函數中,我們打印出警告框的消息并接受它。

          處理確認框

          確認框通常用于向用戶顯示一條消息,并要求用戶確認或取消操作。在 Playwright 中,我們可以使用 dialog.accept() dialog.dismiss() 來分別接受或取消確認框。以下是一個示例:

          pythonfrom playwright.sync_api import sync_playwright
          
          with sync_playwright() as p:
              browser = p.chromium.launch()
              page = browser.new_page()
              
              def on_dialog(dialog):
                  print('Dialog message:', dialog.message)
                  dialog.accept()
          
              page.on('dialog', on_dialog)
              page.goto('https://example.com')
          
              # Perform actions that trigger a confirm dialog
              # Your code here
          

          在這個示例中,我們通過調用 dialog.accept() 來接受確認框。如果要取消確認框,可以調用 dialog.dismiss()

          處理提示框

          提示框通常用于向用戶顯示一條消息,并要求用戶輸入文本或點擊確定按鈕。在 Playwright 中,我們可以使用 dialog.accept(prompt_text) 來接受提示框并輸入文本。以下是一個示例:

          pythonfrom playwright.sync_api import sync_playwright
          
          with sync_playwright() as p:
              browser = p.chromium.launch()
              page = browser.new_page()
              
              def on_dialog(dialog):
                  print('Dialog message:', dialog.message)
                  dialog.accept('Hello, Playwright!')
          
              page.on('dialog', on_dialog)
              page.goto('https://example.com')
          
              # Perform actions that trigger a prompt dialog
              # Your code here
          

          在這個示例中,我們通過調用 dialog.accept('Hello, Playwright!') 來接受提示框并輸入文本。

          總結

          通過本文,我們了解了如何使用 Python 編寫代碼來處理不同類型的彈框。Playwright 提供了簡潔而強大的 API,使得處理彈框變得非常容易。開始使用 Playwright,并加快您的 Web 自動化測試流程吧!


          主站蜘蛛池模板: 理论亚洲区美一区二区三区| 日韩免费一区二区三区| 国产免费私拍一区二区三区| 国产成人av一区二区三区在线观看| 国产一区二区精品久久91| 狠狠做深爱婷婷综合一区| 亚洲一区二区三区久久| 久久精品国产一区二区三区不卡| 无码人妻啪啪一区二区| 激情综合丝袜美女一区二区| 无码人妻精品一区二区三区夜夜嗨 | 国产在线观看一区二区三区四区| 亲子乱AV视频一区二区| 午夜视频在线观看一区二区| 在线精品亚洲一区二区小说| 日韩视频在线观看一区二区| 国内精品视频一区二区三区八戒| 色婷婷av一区二区三区仙踪林| 国产精品亚洲一区二区麻豆| 精品无码国产一区二区三区AV| 日韩精品中文字幕无码一区| 亚洲制服丝袜一区二区三区| 国产香蕉一区二区精品视频| 精品视频一区二区三区四区| 欧美日韩国产免费一区二区三区 | 亚洲日本中文字幕一区二区三区| 久久无码AV一区二区三区| 日韩中文字幕一区| 一区视频在线播放| 亚洲电影一区二区三区| AV鲁丝一区鲁丝二区鲁丝三区| 色屁屁一区二区三区视频国产| 在线日韩麻豆一区| 精品人伦一区二区三区潘金莲| 成人在线观看一区| 欧洲精品无码一区二区三区在线播放| 无码成人一区二区| 红杏亚洲影院一区二区三区| 久久久久无码国产精品一区| 亚洲综合一区无码精品| 无码精品视频一区二区三区|