Spring容器中的Bean幾種初始化方法和銷燬方法的先後順序
阿新 • • 發佈:2020-09-12
代理模式基本介紹
- 代理模式:作為一個物件提供一個替身,以控制對這個物件的訪問,即通過代理物件訪問目標物件。這樣做的好處是:可以在目標物件實現的基礎上,增強額外的功能菜哦做,即擴充套件目標物件的功能
- 被代理物件可以是遠端物件、建立開銷大的物件或者需要安全控制的物件
- 代理模式有不同的形式,主要有三種:靜態代理、動態代理(JDK代理,介面代理)和Cglib代理(可以在記憶體動態的建立物件,而不需要實現介面,它屬於動態代理的範疇)
靜態代理
靜態代理模式基本介紹
靜態代理在使用時,需要定義介面或者父類,被代理物件(即目標物件)與代理物件一起實現相同的介面或者是繼承相同的父類
應用例項
具體要求:一個老師生病了,要讓另一個老師去替他上課
實現:
- 定義一個介面:ITeacherDao
- 目標物件TeacherDao實現介面ITeacherDao
- 使用靜態代理方式,需要在代理物件TeacherDaoProxy中也實現ITeacherDao
- 呼叫的時候通過待用代理物件的方法來呼叫目標物件
- 特別提醒:代理物件與目標物件要實現相同的介面,然後通過呼叫相同的方法來呼叫目標物件的方法
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();
}
}
靜態代理優缺點分析:
- 優點:在不修改目標物件的功能前提下,能通過代理物件對目標功能進行擴充套件
- 缺點:因為代理物件需要實現與目標物件實現一項的介面,所以會有很多代理類
- 一旦介面增加方法,目標物件與代理物件都需要維護
動態代理
動態代理模式基本介紹
- 代理物件,不需要實現介面,但是目標物件要實現介面,否則不能用動態代理
- 代理物件的生成,是利用JDK的API, 動態的在記憶體中構建代理物件
- 動態代理也叫做:JDK代理、介面代理
JDK中生成代理物件的API
- 代理類所在包:java.lang.reflect.Proxy
- 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代理模式的基本介紹
- 靜態代理和JDK代理模式都要求目標物件是實現一個介面,但是有時候目標物件是一個單獨的物件,並沒有實現任何介面,這個時候可以使用目標物件子類來實現代理,這就是Cglib代理
- Cglib代理也叫做子類代理,他是在記憶體中構建一個子類物件從而實現對咪表物件的功能拓展,有些書也將Cglib代理歸屬到動態代理
- Cglib是一個強大的高效能的程式碼生成包,它可以在執行期擴充套件Java類和實現Java介面,它廣泛的被許多AOP的框架使用,例如Soring AOP實現方法攔截
- 在AOP程式設計中如何選擇代理模式:
- 目標物件需要實現介面,使用JDK代理
- 目標物件不需要實現介面,使用Cglib代理
- Cglib包的底層是通過使用位元組碼處理框架ASM來轉換位元組碼並生成新的類
Cglib代理模式實現步驟
- 需要引入cglib的jar檔案
- 在記憶體中動態構建子類,注意代理類不能為final,否則報錯java.lang.IllegalArgumentException
- 目標物件的方法如果為final/static,那麼就不貴被攔截,就是不會執行目標物件的額外業務方法
代理模式的變體
1.防火牆代理
內網通過代理穿透防火牆,實現對公網的訪問
2.快取代理
比如:當請求圖片檔案等資源時,先到快取代理取,如果取到資源ok,取不到再到公網或者資料庫取,然後快取
3.遠端代理
遠端物件的本地代表,通過它可以把遠端物件當本地物件來呼叫。 遠端代理通過網路和真正的遠端物件溝通訊息
3.同步代理:主要使用再多執行緒程式設計中,完成多執行緒間同步工作