Spring[02.基礎知識整理(下)]

Bean使用外部屬性文件

  • 在配置文件裏配置 Bean 時, 有時需要在 Bean 的配置裏混入系統部署的細節信息(例如: 文件路徑, 數據源配置信息等). 而這些部署細節實際上需要和 Bean 配置相分離

  • Spring 提供了一個 PropertyPlaceholderConfigurer 的 BeanFactory 後置處理器, 這個處理器允許用戶將 Bean 配置的部分內容外移到屬性文件中. 可以在 Bean 配置文件裏使用形式爲 ${var} 的變量, PropertyPlaceholderConfigurer 從屬性文件里加載屬性, 並使用這些屬性來替換變量.

  • xml context加載外部配置文件<context:property-placeholder location="">

Spel表達式

是一個支持運行時查詢和操作對象圖的強大的表達式語言

通過 SpEL 可以實現:

  • 通過 bean 的 id 對 bean 進行引用
  • 調用方法以及引用對象中的屬性
  • 計算表達式的值
  • 正則表達式的匹配

案例

驗證郵箱

^[_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)"+"*@[A-Za-z0-9]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$

Bean的生命週期

生命週期

  • Spring IOC 容器可以管理 Bean 的生命週期, Spring 允許在 Bean 生命週期的特定點執行定製的任務.
  • Spring IOC 容器對 Bean 的生命週期進行管理的過程:
    • 通過構造器或工廠方法創建 Bean 實例
    • 爲 Bean 的屬性設置值和對其他 Bean 的引用
    • 調用 Bean 的初始化方法
    • Bean 可以使用了
    • 當容器關閉時, 調用 Bean 的銷燬方法
  • 在 Bean 的聲明裏設置 init-method 和 destroy-method 屬性, 爲 Bean 指定初始化和銷燬方法.

Bean後置處理器

基本介紹

  • Bean 後置處理器允許在調用初始化方法前後對 Bean 進行額外的處理.
  • Bean 後置處理器對 IOC 容器裏的所有 Bean 實例逐一處理, 而非單一實例. 其典型應用是: 檢查 Bean 屬性的正確性或根據特定的標準更改 Bean 的屬性.
  • 對Bean 後置處理器而言, 需要實現BeanPostProcessor接口.

在初始化方法被調用前後, Spring 將把每個 Bean 實例分別傳遞給上述接口的以下兩個方法:

public class MyBeanPostProcesser implements BeanPostProcessor {

    // 在init-method之前調用
    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        System.out.println("postProcessBeforeInitialization:  " + s);
        return o;
    }

    // 在init-method之後調用
    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        System.out.println("postProcessAfterInitialization:  " + s);
        return o;
    }
}

後置處理器特性

//Bean 後置處理器允許在調用初始化方法前後對 Bean 進行額外的處理.
//Bean 後置處理器對 IOC 容器裏的所有 Bean 實例逐一處理, 而非單一實例.
// 其典型應用是: 檢查 Bean 屬性的正確性或根據特定的標準更改 Bean 的屬性.

//採用前置和後置處理之後
//首先運行構造函數
//然後給屬性賦值
//然後訪問前置處理函數
//然後訪問init函數
//然後訪問後置處理函數
//然後輸出結果
//最後訪問destroy函數

@Autowired 自動裝配 Bean

  • 構造器, 普通字段(即使是非 public), 一切具有參數的方法都可以應用@Authwired 註解
    默認情況下, 所有使用 @Authwired 註解的屬性都需要被設置. 當 Spring 找不到匹配的 Bean 裝配屬性時, 會拋出異常, 若某一屬性允許不被設置, 可以設置 @Authwired 註解的 required 屬性爲 false

  • 默認情況下, 當 IOC 容器裏存在多個類型兼容的 Bean 時, 通過類型的自動裝配將無法工作. 此時可以在 @Qualifier 註解裏提供 Bean 的名稱. Spring 允許對方法的入參標註 @Qualifiter 已指定注入 Bean 的名稱

  • @Authwired 註解也可以應用在數組類型的屬性上, 此時 Spring 將會把所有匹配的 Bean 進行自動裝配.

  • @Authwired 註解也可以應用在集合屬性上, 此時 Spring 讀取該集合的類型信息, 然後自動裝配所有與之兼容的 Bean.

  • @Authwired 註解用在 java.util.Map 上時, 若該 Map 的鍵值爲 String, 那麼 Spring 將自動裝配與之 Map 值類型兼容的 Bean, 此時 Bean 的名稱作爲鍵值

AOP與AspectJ

概述

是一種新的方法論, 是對傳統 OOP(Object-Oriented Programming, 面向對象編程) 的補充.

AOP 的主要編程對象是切面(aspect), 而切面模塊化橫切關注點.

在應用 AOP 編程時, 仍然需要定義公共功能, 但可以明確的定義這個功能在哪裏, 以什麼方式應用, 並且不必修改受影響的類. 這樣一來橫切關注點就被模塊化到特殊的對象(切面)裏.

AOP 的好處:

  • 每個事物邏輯位於一個位置, 代碼不分散, 便於維護和升級
  • 業務模塊更簡潔, 只包含核心業務代碼.

術語

  • 切面(Aspect): 橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象
  • 通知(Advice): 切面必須要完成的工作
  • 目標(Target): 被通知的對象
  • 代理(Proxy): 向目標對象應用通知之後創建的對象
  • 連接點(Joinpoint):程序執行的某個特定位置:如類某個方法調用前、調用後、方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點;相對點表示的方位。例如 ArithmethicCalculator#add() 方法執行前的連接點,執行點爲 ArithmethicCalculator#add(); 方位爲該方法執行前的位置
  • 切點(pointcut):每個類都擁有多個連接點:例如 ArithmethicCalculator 的所有方法實際上都是連接點,即連接點是程序類中客觀存在的事務。AOP 通過切點定位到特定的連接點。類比:連接點相當於數據庫中的記錄,切點相當於查詢條件。切點和連接點不是一對一的關係,一個切點匹配多個連接點,切點通過 org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作爲連接點的查詢條件。

AspectJ

AspectJ:Java 社區裏最完整最流行的 AOP 框架.在 Spring2.0 以上版本中, 可以使用基於 AspectJ 註解或基於 XML 配置的 AOP

依賴包:

  • aopalliance-1.0.jar
  • aspectjrt.jar
  • aspectjtools.jar
  • aspectjweaver.jar
  • org.aspectj.matcher.jar

AspectJ通知註解

  • @Before: 前置通知, 在方法執行之前執行
  • @After: 後置通知, 在方法執行之後執行
  • @AfterRunning: 返回通知, 在方法返回結果之後執行
  • @AfterThrowing: 異常通知, 在方法拋出異常之後
  • @Around: 環繞通知, 圍繞着方法執行

    切入點合併

  • @Pointcut 將多個被限制的bean或者bean方法合併爲一個集合,AspectJ通知註解可以直接調用合併切入點的方法,
    如: combinePointCut

    Order

  • 制定切面的優先級
  • 數字越小,代表優先級越高

案例

@Aspect
@Order(0)
public class AspectProcessor {

    // 合併切入點
    @Pointcut("execution(* com.demo.aop.aspect.CalculatorImpl.*(..)))")
    public void combinePointCut(){}

    // 此函數在指定的bean方法執行前響應
    // @Before("execution (* com.demo.aop.aspect.CalculatorImpl.*(..))")
    @Before("combinePointCut()")
    public void beforeAspectProcessor(JoinPoint joinPoint){
        System.out.println("beforeAspectProcessor method name: " + joinPoint.getSignature().getName());
        System.out.println("beforeAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));
    }

    // 此函數在指定的bean方法執行後響應
    // @After("execution (* com.demo.aop.aspect.CalculatorImpl.*(..))")
    @After("combinePointCut()")
    public void afterAspectProcessor(JoinPoint joinPoint){
        System.out.println("afterAspectProcessor method name: " + joinPoint.getSignature().getName());
        System.out.println("afterAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));
    }

//    此函數在指定的bean方法執行前、後響應
//    @Around("combinePointCut()")
//    public void around(ProceedingJoinPoint pjp) throws Throwable{
//        System.out.println("已經記錄下操作日誌@Around 方法執行前");
//        pjp.proceed();
//        System.out.println("已經記錄下操作日誌@Around 方法執行後");
//    }

    // 如果只想在連接點返回的時候記錄, 應使用返回通知代替後置通知
    @AfterReturning(value = "combinePointCut()", returning = "result")
    public void afterReturningAspectProcessor(JoinPoint joinPoint, Object result){
        System.out.println("afterReturningAspectProcessor method name: " + joinPoint.getSignature().getName());
        System.out.println("afterReturningAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));
        System.out.println("afterReturningAspectProcessor method result: " + result);

    }

    // 異常通知
    @AfterThrowing(value = "combinePointCut()", throwing = "e")
    public void afterThrowingAspectProcessor(JoinPoint joinPoint, Exception e){
        System.out.println("afterThrowingAspectProcessor method name: " + joinPoint.getSignature().getName());
        System.out.println("afterThrowingAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));
        System.out.println("afterThrowingAspectProcessor method throwing: " + e);
    }
}
  • 將 aop Schema 添加到 <beans> 根元素中.
  • 要在 Spring IOC 容器中啓用 AspectJ 註解支持, 只要在 Bean 配置文件中定義一個空的 XML 元素 <aop:aspectj-autoproxy>
  • 當 Spring IOC 容器偵測到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素時, 會自動爲與 AspectJ 切面匹配的 Bean 創建代理
### 通過代理方式實現AspectJ ``` import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; /** * Created by plusplusxu on 2017/10/23. * * 動態代理模式主要用來做方法的增強,讓你可以在不修改源碼的情況下,增強一些方法, * 在方法執行前後做任何你想做的事情(甚至根本不去執行這個方法), * 因爲在InvocationHandler的invoke方法中,你可以直接獲取正在調用方法對應的Method對象, * 具體應用的話,比如可以添加調用日誌,做事務控制等。 * 動態代理是設計模式當中代理模式的一種。 */ @Repository public class CalculatorImplProxy { @Autowired private Calculator target; public Calculator getProxy(){ Calculator proxy = null; // 代理對象由哪一個加載器加載 ClassLoader loader = target.getClass().getClassLoader(); // 代理對象的內容 Class[] interfaces = new Class[]{Calculator.class}; // 當調用代理對象的方法的時候,該方法被執行 InvocationHandler h = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 代理對象方法執行前操作 System.out.println("method: " + method.getName() + " " + " args: " + Arrays.asList(args)); Object result = method.invoke(target, args); // 代理對象方法執行後操作 System.out.println("method: " + method.getName() + " " + " args: " + Arrays.asList(args) + " result: " + result); return result; } }; proxy = (Calculator)Proxy.newProxyInstance(loader, interfaces, h); return proxy; } } ``` ## JDBC ### JdbcTemplate > 作爲 Spring JDBC 框架的核心, JDBC 模板的設計目的是爲不同類型的 JDBC 操作提供模板方法. 每個模板方法都能控制整個過程, 並允許覆蓋過程中的特定任務. 通過這種方式, 可以在儘可能保留靈活性的情況下, 將數據庫存取的工作量降到最低. - 每次使用都創建一個 JdbcTemplate 的新實例, 這種做法效率很低下. - JdbcTemplate 類被設計成爲線程安全的, 所以可以再 IOC 容器中聲明它的單個實例, 並將這個實例注入到所有的 DAO 實例中. - JdbcTemplate 也利用了 Java 1.5 的特定(自動裝箱, 泛型, 可變長度等)來簡化開發 ### JdbcDaoSupport > 該類聲明瞭 jdbcTemplate 屬性, 它可以從 IOC 容器中注入, 或者自動從數據源中創建. ``` package com.mysql; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; /** * Created by plusplusxu on 2017/10/25. * * 操作mysql的數據類 * * JdbcTemplate 類被設計成爲線程安全的, 所以可以再 IOC 容器中聲明它的單個實例, 並將這個實例注入到所有的 DAO 實例中. */ public class ProjectDao { public ProjectDao(JdbcTemplate jdbcTemplateObject) { this.jdbcTemplateObject = jdbcTemplateObject; } public ProjectDao() { } public JdbcTemplate getJdbcTemplateObject() { return jdbcTemplateObject; } public void setJdbcTemplateObject(JdbcTemplate jdbcTemplateObject) { this.jdbcTemplateObject = jdbcTemplateObject; } // spring 提供的訪問數據庫的工具類,需要配置javax.sql.DataSource // 本例中在applicationContext中配置的mysql數據源 private JdbcTemplate jdbcTemplateObject; // 添加一條項目記錄 // tbl_devops_project爲vsdo數據庫的項目表(Project) public void addproject(Project project) { String sql = "INSERT INTO tbl_devops_project(name,public,language,description," + "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " + "VALUES(?,?,?,?,?,?,?,?,?,?)"; try { jdbcTemplateObject.update(sql, project.getName(),project.getPub(),project.getLanguage(),project.getDescription(), project.getAdmin(),project.getMembers(),project.getGitlab_id(), project.getGitlab_url(),project.getCode_styles(),project.getSonar_lint_server_url()); } catch (Exception e){ System.out.println(e); } return ; } // 根據項目名刪除記錄 public void delprojectbyname(String name) { String sql = "DELETE FROM tbl_devops_project WHERE name=?"; try { jdbcTemplateObject.update(sql,name); } catch (Exception e){ System.out.println(e); } return ; } // 刪除所有項目 public void delallproject() { String sql = "DELETE FROM tbl_devops_project"; try { jdbcTemplateObject.update(sql); } catch (Exception e){ System.out.println(e); } return ; } // 更新某個項目 public void updproject(Project project) { String sql = "UPDATE tbl_devops_project set description=? WHERE name=?"; try { jdbcTemplateObject.update(sql,project.getDescription(), project.getName()); } catch (Exception e){ System.out.println(e); } return ; } // 獲取所有項目 public List allproject() { List projects = null; String sql = "SELECT * FROM tbl_devops_project"; try { // ProjectMapper 項目表的映射類 projects = jdbcTemplateObject.query(sql, new ProjectMapper()); } catch (Exception e){ System.out.println(e); } return projects; } // 查詢項目名對應的記錄 public List queryprojectbyname(String name) { List projects = null; String sql = "SELECT * FROM tbl_devops_project WHERE name=?"; try { projects = jdbcTemplateObject.query(sql, new Object[]{name}, new ProjectMapper()); } catch (Exception e){ System.out.println(e); } return projects; } // 打印所有項目 public void displayall(){ List projects = allproject(); for(Project s : projects){ System.out.println(s); } } // 批量插入 public void insertBatch(final List projects){ String sql = "INSERT INTO tbl_devops_project(name,public,language,description," + "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " + "VALUES(?,?,?,?,?,?,?,?,?,?)"; jdbcTemplateObject.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { Project project = projects.get(i); ps.setString(1, project.getName()); ps.setBoolean(2, project.getPub()); ps.setString(3, project.getLanguage()); ps.setString(4, project.getDescription()); ps.setString(5, project.getAdmin()); ps.setString(6, project.getMembers()); ps.setInt(7, project.getGitlab_id()); ps.setString(8, project.getGitlab_url()); ps.setString(9, project.getCode_styles()); ps.setString(10, project.getSonar_lint_server_url()); } @Override public int getBatchSize() { return projects.size(); } }); } } ``` ``` package com.mysql; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.support.JdbcDaoSupport; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; /** * Created by plusplusxu on 2017/10/26. * * 繼承於JdbcDaoSupport,直接封裝了JdbcTemplate,寫起來更方便 * * 該類聲明瞭 jdbcTemplate 屬性, 它可以從 IOC 容器中注入, 或者自動從數據源中創建. * */ public class JdbcDaoSupportImpl extends JdbcDaoSupport { // 添加一條項目記錄 // tbl_devops_project爲vsdo數據庫的項目表(Project) public void addproject(Project project) { String sql = "INSERT INTO tbl_devops_project(name,public,language,description," + "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " + "VALUES(?,?,?,?,?,?,?,?,?,?)"; try { this.getJdbcTemplate().update(sql, project.getName(),project.getPub(),project.getLanguage(),project.getDescription(), project.getAdmin(),project.getMembers(),project.getGitlab_id(), project.getGitlab_url(),project.getCode_styles(),project.getSonar_lint_server_url()); } catch (Exception e){ System.out.println(e); } return ; } // 根據項目名刪除記錄 public void delprojectbyname(String name) { String sql = "DELETE FROM tbl_devops_project WHERE name=?"; try { this.getJdbcTemplate().update(sql,name); } catch (Exception e){ System.out.println(e); } return ; } // 刪除所有項目 public void delallproject() { String sql = "DELETE FROM tbl_devops_project"; try { this.getJdbcTemplate().update(sql); } catch (Exception e){ System.out.println(e); } return ; } // 更新某個項目 public void updproject(Project project) { String sql = "UPDATE tbl_devops_project set description=? WHERE name=?"; try { this.getJdbcTemplate().update(sql,project.getDescription(), project.getName()); } catch (Exception e){ System.out.println(e); } return ; } // 獲取所有項目 public List allproject() { List projects = null; String sql = "SELECT * FROM tbl_devops_project"; try { // ProjectMapper 項目表的映射類 projects = this.getJdbcTemplate().query(sql, new ProjectMapper()); } catch (Exception e){ System.out.println(e); } return projects; } // 查詢項目名對應的記錄 public List queryprojectbyname(String name) { List projects = null; String sql = "SELECT * FROM tbl_devops_project WHERE name=?"; try { projects = this.getJdbcTemplate().query(sql, new Object[]{name}, new ProjectMapper()); } catch (Exception e){ System.out.println(e); } return projects; } // 打印所有項目 public void displayall(){ List projects = allproject(); for(Project s : projects){ System.out.println(s); } } // 批量插入 public void insertBatch(final List projects){ String sql = "INSERT INTO tbl_devops_project(name,public,language,description," + "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " + "VALUES(?,?,?,?,?,?,?,?,?,?)"; this.getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { Project project = projects.get(i); ps.setString(1, project.getName()); ps.setBoolean(2, project.getPub()); ps.setString(3, project.getLanguage()); ps.setString(4, project.getDescription()); ps.setString(5, project.getAdmin()); ps.setString(6, project.getMembers()); ps.setInt(7, project.getGitlab_id()); ps.setString(8, project.getGitlab_url()); ps.setString(9, project.getCode_styles()); ps.setString(10, project.getSonar_lint_server_url()); } @Override public int getBatchSize() { return projects.size(); } }); } } ``` ``` ``` ## 事務 事務管理是企業級應用程序開發中必不可少的技術, 用來確保數據的完整性和一致性. 事務就是一系列的動作, 它們被當做一個單獨的工作單元. 這些動作要麼全部完成, 要麼全部不起作用 事務的四個關鍵屬性(ACID) - 原子性(atomicity): 事務是一個原子操作, 由一系列動作組成. 事務的原子性確保動作要麼全部完成要麼完全不起作用. - 一致性(consistency): 一旦所有事務動作完成, 事務就被提交. 數據和資源就處於一種滿足業務規則的一致性狀態中. - 隔離性(isolation): 可能有許多事務會同時處理相同的數據, 因此每個事物都應該與其他事務隔離開來, 防止數據損壞. - 持久性(durability): 一旦事務完成, 無論發生什麼系統錯誤, 它的結果都不應該受到影響. 通常情況下, 事務的結果被寫到持久化存儲器中. Spring 既支持編程式事務管理, 也支持聲明式的事務管理. - 編程式事務管理: 將事務管理代碼嵌入到業務方法中來控制事務的提交和回滾. 在編程式管理事務時, 必須在每個事務操作中包含額外的事務管理代碼. - 聲明式事務管理: 大多數情況下比編程式事務管理更好用. 它將事務管理代碼從業務方法中分離出來, 以聲明的方式來實現事務管理. 事務管理作爲一種橫切關注點, 可以通過 AOP 方法模塊化. Spring 通過 Spring AOP 框架支持聲明式事務管理. ### 用 @Transactional 註解聲明式地管理事務 > Spring 還允許簡單地用 @Transactional 註解來標註事務方法. - 爲了將方法定義爲支持事務處理的, 可以爲方法添加 @Transactional 註解. 根據 Spring AOP 基於代理機制, 只能標註公有方法. - 可以在方法或者類級別上添加 @Transactional 註解. 當把這個註解應用到類上時, 這個類中的所有公共方法都會被定義成支持事務處理的. - 在 Bean 配置文件中只需要啓用 元素, 併爲之指定事務管理器就可以了. - 如果事務處理器的名稱是 transactionManager, 就可以在 元素中省略 transaction-manager 屬性. 這個元素會自動檢測該名稱的事務處理器. ![](http://i2.51cto.com/images/blog/201801/03/3fbec91623e98552fde5d0927e73d616.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) ### 事務的傳播行爲 > 當事務方法被另一個事務方法調用時, 必須指定事務應該如何傳播. 例如: 方法可能繼續在現有事務中運行, 也可能開啓一個新事務, 並在自己的事務中運行. ![](http://i2.51cto.com/images/blog/201801/03/9f28ebfca76b25cde46e897d7d604de2.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章