Spring之IOC,DI,動態代理,反射

  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型數據。

相關文章
相關標籤/搜索