1. 程式人生 > 實用技巧 >Spring容器中的Bean幾種初始化方法和銷燬方法的先後順序

Spring容器中的Bean幾種初始化方法和銷燬方法的先後順序

代理模式基本介紹

  1. 代理模式:作為一個物件提供一個替身,以控制對這個物件的訪問,即通過代理物件訪問目標物件。這樣做的好處是:可以在目標物件實現的基礎上,增強額外的功能菜哦做,即擴充套件目標物件的功能
  2. 被代理物件可以是遠端物件、建立開銷大的物件或者需要安全控制的物件
  3. 代理模式有不同的形式,主要有三種:靜態代理、動態代理(JDK代理,介面代理)和Cglib代理(可以在記憶體動態的建立物件,而不需要實現介面,它屬於動態代理的範疇)

靜態代理

靜態代理模式基本介紹

靜態代理在使用時,需要定義介面或者父類,被代理物件(即目標物件)與代理物件一起實現相同的介面或者是繼承相同的父類

應用例項

具體要求:一個老師生病了,要讓另一個老師去替他上課

實現:

  1. 定義一個介面:ITeacherDao
  2. 目標物件TeacherDao實現介面ITeacherDao
  3. 使用靜態代理方式,需要在代理物件TeacherDaoProxy中也實現ITeacherDao
  4. 呼叫的時候通過待用代理物件的方法來呼叫目標物件
  5. 特別提醒:代理物件與目標物件要實現相同的介面,然後通過呼叫相同的方法來呼叫目標物件的方法

ITeacherDao介面程式碼:

package vip.bwang.proxy.staticproxy.dao;

public interface ITeacherDao {

    void teach();   //授課的方法
}

TeacherDao目標物件程式碼(要被代理的物件):

package vip.bwang.proxy.staticproxy.dao;

/**
 * @description:
 * @author: bwang
 * @date: 2020/9/11 21:10
 */
public class TeacherDao implements ITeacherDao{
    @Override
    public void teach() {
        System.out.println("老師正在授課");
    }
}

TeacherDaoProxy代理物件程式碼

package vip.bwang.proxy.staticproxy.proxy;

import vip.bwang.proxy.staticproxy.dao.ITeacherDao;

/**
 * @description:
 * @author: bwang
 * @date: 2020/9/11 21:17
 *
 * 代理物件,靜態代理
 */
public class TeacherDaoProxy implements ITeacherDao {

    private final ITeacherDao target; //目標物件,通過介面來聚合

    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    @Override
    public void teach() {
        //在這裡,可以對被代理物件的方法進行增強實現
        System.out.println("代理開始");
        target.teach();
        System.out.println("代理結束");
    }
}

client程式碼:

package vip.bwang.proxy.staticproxy;

import vip.bwang.proxy.staticproxy.dao.TeacherDao;
import vip.bwang.proxy.staticproxy.proxy.TeacherDaoProxy;

/**
 * @description:
 * @author: bwang
 * @date: 2020/9/11 21:09
 */
public class Client {

    public static void main(String[] args) {

        //建立目標物件(被代理物件)
        TeacherDao teacherDao = new TeacherDao();

        //建立代理物件,同時將被代理物件傳入代理物件中
        TeacherDaoProxy proxy = new TeacherDaoProxy(teacherDao);

        //通過代理物件,執行目標物件的方法(代理物件再去呼叫目標物件,同時對目標物件的方法實現增強功能)
        proxy.teach();
    }
}

靜態代理優缺點分析:

  1. 優點:在不修改目標物件的功能前提下,能通過代理物件對目標功能進行擴充套件
  2. 缺點:因為代理物件需要實現與目標物件實現一項的介面,所以會有很多代理類
  3. 一旦介面增加方法,目標物件與代理物件都需要維護

動態代理

動態代理模式基本介紹

  1. 代理物件,不需要實現介面,但是目標物件要實現介面,否則不能用動態代理
  2. 代理物件的生成,是利用JDK的API, 動態的在記憶體中構建代理物件
  3. 動態代理也叫做:JDK代理、介面代理

JDK中生成代理物件的API

  1. 代理類所在包:java.lang.reflect.Proxy
  2. JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接受三個引數,完整的寫法是

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

IteacherDao程式碼:

package vip.bwang.proxy.dynamic.dao;

/**
 * @description:
 * @author: bwang
 * @date: 2020/9/12 10:54
 */
public interface ITeacherDao {

    void teach();
}

IteacherDao的實現類TeacherDao(目標物件)程式碼:

package vip.bwang.proxy.dynamic.dao;

/**
 * @description:
 * @author: bwang
 * @date: 2020/9/12 10:57
 */
public class TeacherDao implements ITeacherDao{
    @Override
    public void teach() {
        System.out.println("老師在授課");
    }
}

ProxyFactory程式碼:

package vip.bwang.proxy.dynamic.proxyfactory;

import vip.bwang.proxy.dynamic.dao.TeacherDao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @description:
 * @author: bwang
 * @date: 2020/9/12 10:56
 */
public class ProxyFactory {

    //建立目標物件
    TeacherDao target;

    //通過建構函式傳入目標物件
    public ProxyFactory(TeacherDao target) {
        this.target = target;
    }

    public Object getProxyInstance() {

        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("JDK代理開始");

                        //反射機制呼叫目標物件
                        return method.invoke(target, args);
                    }
                });
    }
}

Client程式碼:

package vip.bwang.proxy.dynamic;

import vip.bwang.proxy.dynamic.dao.ITeacherDao;
import vip.bwang.proxy.dynamic.dao.TeacherDao;
import vip.bwang.proxy.dynamic.proxyfactory.ProxyFactory;

/**
 * @description:
 * @author: bwang
 * @date: 2020/9/12 10:57
 */
public class Client {

    public static void main(String[] args) {
        //1.建立目標物件
        TeacherDao target = new TeacherDao();

        //2.建立代理物件,可以轉換成ITeacherDao
        ProxyFactory proxyFactory = new ProxyFactory(target);
        ITeacherDao proxyInstance = (ITeacherDao) proxyFactory.getProxyInstance();
        //proxyInstance=class com.sun.proxy.$Proxy0(帶$符號為代理物件,記憶體中動態生成了代理物件)
        System.out.println("proxyInstance=" + proxyInstance.getClass());
        proxyInstance.teach();
    }
}

執行結果:

Cglib代理

Cglib代理模式的基本介紹

  1. 靜態代理和JDK代理模式都要求目標物件是實現一個介面,但是有時候目標物件是一個單獨的物件,並沒有實現任何介面,這個時候可以使用目標物件子類來實現代理,這就是Cglib代理
  2. Cglib代理也叫做子類代理,他是在記憶體中構建一個子類物件從而實現對咪表物件的功能拓展,有些書也將Cglib代理歸屬到動態代理
  3. Cglib是一個強大的高效能的程式碼生成包,它可以在執行期擴充套件Java類和實現Java介面,它廣泛的被許多AOP的框架使用,例如Soring AOP實現方法攔截
  4. 在AOP程式設計中如何選擇代理模式:
    1. 目標物件需要實現介面,使用JDK代理
    2. 目標物件不需要實現介面,使用Cglib代理
  1. Cglib包的底層是通過使用位元組碼處理框架ASM來轉換位元組碼並生成新的類

Cglib代理模式實現步驟

  1. 需要引入cglib的jar檔案
  2. 在記憶體中動態構建子類,注意代理類不能為final,否則報錯java.lang.IllegalArgumentException
  3. 目標物件的方法如果為final/static,那麼就不貴被攔截,就是不會執行目標物件的額外業務方法

代理模式的變體

1.防火牆代理

內網通過代理穿透防火牆,實現對公網的訪問

2.快取代理

比如:當請求圖片檔案等資源時,先到快取代理取,如果取到資源ok,取不到再到公網或者資料庫取,然後快取

3.遠端代理

遠端物件的本地代表,通過它可以把遠端物件當本地物件來呼叫。 遠端代理通過網路和真正的遠端物件溝通訊息

3.同步代理:主要使用再多執行緒程式設計中,完成多執行緒間同步工作