Spring框架是J2EE開發中一個使用普遍的框架,它使得dao和service層的維護更加便利。Spring框架有兩個重要的特徵,一個是IOC,另外一個是AOP。咱們在這裏主要介紹IOC,以及IOC中涉及的主要技術。java
IOC(Inversion Of Control),控制反轉是將建立對象的控制權從程序員手中轉向Spring框架。Spring框架在建立對象時使用了DI技術。程序員
DI(Dependency Injection),依賴注入主要是經過setter注入和構造器注入,產生一個類Class的bean(實例)。DI主要是經過動態代理和反射技術來實現的。spring
咱們先來了解代理的概念。何爲代理呢?顧名思義,代理能夠幫助咱們去完成某項事務,並且能夠在完成事務中加強、完善相應的功能。舉例來講,我國多個地區已經實施適齡公民能夠直接報考駕照,但在實際中,咱們在考取駕照時,每每會委託一家駕校幫助咱們報考駕照,在這之中,駕校就充當了咱們報考駕照的代理角色。顯然,在不少時候,經過代理能夠幫助咱們節省時間,得到更加便捷的服務。框架
其實,咱們在Java開發中運用代理,與生活中的代理概念類似。jvm
, 接下來,咱們在介紹動態代理以前,先了解靜態代理。咱們接下來以生活中報考駕照爲例寫一個簡單的案例,來了解靜態代理。ide
package bkjz; /** * 考駕照的人 */ public class Examiner { //身份證號,姓名 private String id,name; public Examiner(String id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "考生(姓名:" +name+ ",身份證號:" + id + ")"; } } ****************** package bkjz; /** * 考駕照,核心業務功能接口 * 傳遞入一個具體報考駕照的人 */ public interface GetDriverLicense { public void getDriverLicense(Examiner examiner); } ****************** package bkjz.impl; import bkjz.Examiner; import bkjz.GetDriverLicense; /** * 實現報考駕照的核心功能接口的類 */ public class GetDriverLicenseImpl implements GetDriverLicense { @Override public void getDriverLicense(Examiner examiner) { System.out.println("報考駕照核心業務:您的各項報名指標合格,能夠報考駕照"); } } ****************** package bkjz.impl; import bkjz.Examiner; import bkjz.GetDriverLicense; /** * 駕校代理,幫助報考者報考駕照 */ public class OrgProxy implements GetDriverLicense { //調用核心功能的接口實現類 private GetDriverLicense getDriverLicense; //構造器傳參 public OrgProxy(GetDriverLicenseImpl getDriverLicense){ this.getDriverLicense=getDriverLicense; } @Override public void getDriverLicense(Examiner examiner) { System.out.println("加強功能1:你好,"+examiner+",我來幫你收集報名須要提交哪些資料"); System.out.println("加強功能2:你好,"+examiner+",我來幫你推薦體檢醫院,幫你節省時間"); System.out.println("加強功能3:你好,"+examiner+",我來幫你到車管所遞交報名資料,幫你節省時間"); System.out.println("==========================="); getDriverLicense.getDriverLicense(examiner); System.out.println("==========================="); System.out.println("加強功能4:你好,"+examiner+",報名成功後,我幫你把報考所需身份資料的原件取回,幫你節省時間"); System.out.println("加強功能5:你好,"+examiner+",我來幫你安排培訓學習時間,幫你經過考試"); System.out.println("加強功能6:你好,"+examiner+",你經過駕照考試後,我幫你郵寄駕照,幫你節省時間"); } } ************************* package bkjz; import bkjz.impl.GetDriverLicenseImpl; import bkjz.impl.OrgProxy; /** * 測試類 */ public class Test { public static void main(String[] args) { //一個準備報考駕照的考生 Examiner examiner=new Examiner("320923198901142757","張三"); //找個駕校來報考駕照,以及安排後續的學習取照 OrgProxy orgProxy=new OrgProxy(new GetDriverLicenseImpl()); orgProxy.getDriverLicense(examiner); } }
執行測試類後,運行結果以下:函數
加強功能1:你好,考生(姓名:張三,身份證號:320923198901142757),我來幫你收集報名須要提交哪些資料 加強功能2:你好,考生(姓名:張三,身份證號:320923198901142757),我來幫你推薦體檢醫院,幫你節省時間 加強功能3:你好,考生(姓名:張三,身份證號:320923198901142757),我來幫你到車管所遞交報名資料,幫你節省時間 =========================== 報考駕照核心業務:您的各項報名指標合格,能夠報考駕照 =========================== 加強功能4:你好,考生(姓名:張三,身份證號:320923198901142757),報名成功後,我幫你把報考所需身份資料的原件取回,幫你節省時間 加強功能5:你好,考生(姓名:張三,身份證號:320923198901142757),我來幫你安排培訓學習時間,幫你經過考試 加強功能6:你好,考生(姓名:張三,身份證號:320923198901142757),你經過駕照考試後,我幫你郵寄駕照,幫你節省時間
可見,經過代理的確可以爲咱們帶來不少便利,還能夠加強不少功能。實際上,咱們平時遇到的過濾器、攔截器從本質上來講,也是經過代理的方式實現的,經過代理加強一些功能,從而控制對被代理的業務的訪問。固然,過濾器、攔截器運用的代理技術要比靜態代理更靈活。工具
靜態代理有個弊端,那就是若是具體業務改變時,咱們須要改寫其餘的代理類,在須要代理的功能不少時,就會顯著增長咱們的工做量,不利於效率的提高。爲此,咱們引入動態代理的概念。學習
所謂動態代理,顯然,咱們須要增長一些加強型的功能時,沒必要再屢次建立代理類,而是動態地進行相應的擴展便可。咱們經過案例來看基於業務接口的動態代理。測試
package test2_proxy.proxy; import test2_proxy.service.OrderServiceImpl; import test2_proxy.service.UserServiceImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 基於接口的動態代理 * 對於一些須要重複執行的驗證功能或下一步操做, * 在覈心方法執行先後都會執行加強的功能 */ public class ServiceProxy { public static Object newProxyInstance(Object target){ return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("動態加強功能1:登陸權限認證"); System.out.println("動態加強功能2:黑名單驗證"); System.out.println("====================="); Object invoke = method.invoke(target, args);//執行被代理的核心業務功能 System.out.println("====================="); System.out.println("動態加強功能3:進入訂單中心"); System.out.println("此處省略一萬行(●—●)..."); return invoke; } }); } } *********************** package test2_proxy.service; /** * 用戶登陸、註冊、找回密碼等一系列服務的接口 */ public interface UserService { public void testUserService(); } ************************ package test2_proxy.service; import org.springframework.stereotype.Service; /** * 實現用戶服務接口的類 */ @Service public class UserServiceImpl implements UserService { @Override public void testUserService() { System.out.println("UserService核心功能"); } } *************************** package test2_proxy.service; /** * 訂單服務接口,以下單、取消訂單、放入購物車等 */ public interface OrderService { public void testOrderService(); } *************************** package test2_proxy.service; /** * 實現訂單核心功能接口的類 */ public class OrderServiceImpl implements OrderService { @Override public void testOrderService() { System.out.println("OrderService核心功能"); } } *************************** package test2_proxy; import test2_proxy.proxy.ServiceProxy; import test2_proxy.service.OrderService; import test2_proxy.service.OrderServiceImpl; import test2_proxy.service.UserService; import test2_proxy.service.UserServiceImpl; /** * 測試類 */ public class Test { public static void main(String[] args) { UserService userService=new UserServiceImpl(); userService = (UserService)ServiceProxy.newProxyInstance(userService); userService.testUserService(); System.out.println("***************************"); OrderService orderService=new OrderServiceImpl(); orderService = (OrderService)ServiceProxy.newProxyInstance(orderService); orderService.testOrderService(); } }
上述代碼執行完後,執行結果以下:
動態加強功能1:登陸權限認證 動態加強功能2:黑名單驗證 ===================== UserService核心功能 ===================== 動態加強功能3:進入訂單中心 此處省略一萬行(●—●)... *************************** 動態加強功能1:登陸權限認證 動態加強功能2:黑名單驗證 ===================== OrderService核心功能 ===================== 動態加強功能3:進入訂單中心 此處省略一萬行(●—●)...
可見,動態代理比靜態代理更加靈活,它的核心是 java.lang.reflect.Proxy代理類,該類位於reflect反射包下,可知動態代理當中是用了反射的技術來實現的。
接下來,咱們再看基於類實現的動態代理。此處須要在項目中添加 cglib-2.2.2.jar包的依賴,運用cglib的方法,使代碼更簡潔。咱們經過案例來實現。
package test3_proxy_cglib.proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.Arrays; /** * 動態代理類 */ public class ServiceProxy { public static Object newProxyInstance(Object target) { //1.工具類 Enhancer enhancer = new Enhancer(); //2.設置父類,傳遞類對象 enhancer.setSuperclass(target.getClass()); //3.設置回調函數 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib加強功能1"); System.out.println("cglib加強功能2"); System.out.println("此處省略一萬行(●—●)..."); System.out.println("=================="); return method.invoke(target, objects); } }); //4.功能加強後,返回代理對象 return enhancer.create(); } } **************************** package test3_proxy_cglib; /** * 用戶服務功能類 */ public class UserService { public void testUserService(){ System.out.println("UserService核心功能"); } } ***************************** package test3_proxy_cglib; /** * 訂單服務功能類 */ public class OrderService { public void testOrderService(){ System.out.println("OrderService核心功能"); } } ********************************** package test3_proxy_cglib; import test3_proxy_cglib.proxy.ServiceProxy; /** * 測試類 */ public class Test { public static void main(String[] args) { UserService userService=new UserService(); userService= (UserService)ServiceProxy.newProxyInstance(userService); userService.testUserService(); System.out.println("***************************"); OrderService orderService=new OrderService(); orderService=(OrderService)ServiceProxy.newProxyInstance(orderService); orderService.testOrderService(); } }
上述代碼的執行結果以下:
cglib加強功能1 cglib加強功能2 此處省略一萬行(●—●)... ================== UserService核心功能 *************************** cglib加強功能1 cglib加強功能2 此處省略一萬行(●—●)... ================== OrderService核心功能
接下來,咱們看下反射。
Java反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。Java關於反射的定義主要在 java.lang.reflect包中。Java反射須要用到Class對象,Class對象的由來是將class文件讀入內存,併爲之建立一個Class對象。
Class 類的實例表示正在運行的 Java 應用程序中的類和接口,也就是jvm中有N多的實例,每一個類都有對應的Class對象(包括基本數據類型)。Class 沒有公共構造方法,Class 對象是在加載類時由 Java 虛擬機以及經過調用類加載器中的defineClass 方法自動構造的。也就是說,Class 對象不須要咱們本身去建立,JVM已經幫咱們建立好了。
在使用反射時,咱們須要先獲取Class對象。獲取Class對象有三種方式:
第一,Object對象.getClass()
第二,任何數據類型(包括基本數據類型)都有一個靜態static的class屬性
第三,經過Class類的靜態方法forName(String className)來獲取 Class.forName(String className) ------>[這是咱們經常使用的獲取類對象的方法]
須要注意的是,在程序運行期間,一個類只有一個Class對象產生,該Class對象由JVM管理。得到了類對象,咱們就隨之可使用這個類對象的全部屬性和方法。
經過反射,咱們能夠獲取一個類的任意方法(包括構造方法)和屬性,從而充分利用一個類所提供的一切資源。
另外,咱們在使用各種集合(如List,Set,Map)時,爲了指明集合中元素的類型,一般會用到泛型,泛型是做用於程序編譯期,編譯事後,泛型的約束就會失效;反射則做用於程序編譯期以後的運行期,所以,咱們能夠經過反射來越過泛型的檢查。咱們經過下面的案例來看:
package test9; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 泛型做用於編譯期,會在編譯以前對程序進行檢查 * 編譯期事後,泛型約束失效 * 反射做用於運行期 * 演示經過反射越過泛型的檢查 */ public class Test { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { //1.先聲明一個List集合,元素泛型約束爲String類型 List<String> strList=new ArrayList<>(); //2.向集合中添加String型的元素 strList.add("aa"); strList.add("bb"); //注:在編譯期以前,向集合中添加非泛型元素,程序會在編譯期就報錯 //strList.add(123); //這時,咱們經過反射來越過泛型檢查 Class strListClass = strList.getClass();//先獲取Class對象 //獲取add方法,爲了保障程序清晰,在main方法上拋出異常 Method method = strListClass.getMethod("add", Object.class); //經過反射調用add方法,添加一個int型數據 method.invoke(strList,123); System.out.println("strList="+strList); } }
上述代碼打印結果以下:
strList=[aa, bb, 123]
可見,定義元素泛型爲String的List集合成功越過了泛型檢查,添加了一個int型數據。