Spring靜態(tài)代理和動(dòng)態(tài)代理代碼詳解
本節(jié)要點(diǎn):
Java靜態(tài)代理
Jdk動(dòng)態(tài)代理
1 面向?qū)ο笤O(shè)計(jì)思想遇到的問題
在傳統(tǒng)OOP編程里以對(duì)象為核心,并通過對(duì)象之間的協(xié)作來形成一個(gè)完整的軟件功能,由于對(duì)象可以繼承,因此我們可以把具有相同功能或相同特征的屬性抽象到一個(gè)層次分明的類結(jié)構(gòu)體系中。隨著軟件規(guī)范的不斷擴(kuò)大,專業(yè)化分工越來越系列,以及OOP應(yīng)用實(shí)踐的不斷增多,隨之也暴露了一些OOP無法很好解決的問題。
現(xiàn)在假設(shè)系統(tǒng)中有三段完全相似的代碼,這些代碼通常會(huì)采用“復(fù)制”、“粘貼”方式來完成,通過這種方式開發(fā)出來的軟件如圖所示:

可能讀者已經(jīng)發(fā)現(xiàn)了這種做法的不足之處,如果有一天,藍(lán)色背景的代碼需要修改,那是不是要同時(shí)修改三個(gè)地方?如果不僅僅是這三個(gè)地方包含這段代碼,而是100個(gè),甚至是1000個(gè)地方,那會(huì)是什么后果?
記錄日志在代碼中無處不在---先來看一個(gè)例子:
為了跟蹤應(yīng)用程序的運(yùn)行過程,很多方法都需要記錄日志信息。我們一般這樣寫:
//log4j的使用見文章“l(fā)og4j介紹”
import org.apache.log4j.Logger;
public class Person {
private Logger logger = Logger.getLogger(Person.class);
public void sleep(){
logger.info(“開始執(zhí)行時(shí)間:“ + new Date());
System.out.println("睡覺中");
logger.info(“執(zhí)行結(jié)束時(shí)間:” + new Date());
}
public void eating(){
logger.info("開始執(zhí)行時(shí)間:“ + new Date()");
System.out.println("正在吃飯中");
logger.info("“執(zhí)行結(jié)束時(shí)間:” + new Date()");
}
}
提問:弊端在哪里?
l混淆了業(yè)務(wù)方法本身的職責(zé)
l維護(hù)工作量巨大
2解決方案1
靜態(tài)代理:
1、需要知道核心類(被代理類)是哪一個(gè)類,并且有什么方法。
2、非核心的代碼需要重復(fù)寫多次,顯得代碼的結(jié)構(gòu)臃腫,形成代碼冗余。
3、非核心類(代理類)需要實(shí)現(xiàn)核心類(被代理類)實(shí)現(xiàn)的接口,也就是他們需要實(shí)現(xiàn)共同的接口,但是以核心類實(shí)現(xiàn)的接口(被代理類)為準(zhǔn)。
l目地是將業(yè)務(wù)代碼與日志代碼完全分離,實(shí)現(xiàn)松散耦合.
l代理對(duì)象與被代理對(duì)象必須實(shí)現(xiàn)同一接口,在代理對(duì)象中實(shí)現(xiàn)與日志記錄的相關(guān)服務(wù),并在需要的時(shí)候呼叫被代理對(duì)象,而被代理對(duì)象只保留業(yè)務(wù)代碼.
靜態(tài)代理的實(shí)現(xiàn)
1)定義接口:
public interface IPerson {
public abstract void sleep();
public abstract void eating();
}
2) 被代理類
public class Person implements IPerson {
public void sleep(){
System.out.println("睡覺中");
}
public void eating(){
System.out.println("正在吃飯中");
}
}
3) 代理類
import org.apache.log4j.Logger;
public class PersonProxy implements IPerson {
private IPerson person;
private Logger logger = Logger.getLogger(PersonProxy.class);
public PersonProxy(IPerson person) {
this.person = person;
}
public void eating() {
logger.info("開始執(zhí)行時(shí)間:“ + new Date()");
person.eating();
logger.info("“執(zhí)行結(jié)束時(shí)間:” + new Date()");
}
public void sleep() {
logger.info("開始執(zhí)行時(shí)間:“ + new Date()");
person.sleep();
logger.info("“執(zhí)行結(jié)束時(shí)間:” + new Date()");
}
}
4) 測(cè)試類
package com.aptech.aop2;
public class PersonTest {
public static void main(String[] args) {
IPerson proxy = new PersonProxy(new Person());
proxy.eating();
proxy.sleep();
}
}
靜態(tài)代理的弊端:
一個(gè)代理接口只能服務(wù)于一種類型的對(duì)象.對(duì)于稍大點(diǎn)的項(xiàng)目根本無法勝任.
3 解決方案2-動(dòng)態(tài)代理
InvocationHandler:每一個(gè)動(dòng)態(tài)代理類都必須實(shí)現(xiàn)InvocationHandler這個(gè)接口,并且每個(gè)代理類的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,當(dāng)我們通過代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的invoke方法來進(jìn)行調(diào)用。
在JDK1.3之后加入了可協(xié)助開發(fā)的動(dòng)態(tài)代理功能.不必為特定對(duì)象與方法編寫特定的代理對(duì)象,使用動(dòng)態(tài)代理,可以使得一個(gè)處理者(Handler)服務(wù)于各個(gè)對(duì)象.
一個(gè)處理者的類設(shè)計(jì)必須實(shí)現(xiàn)java.lang.reflect.InvocationHandler接口.
通過InvocationHandler接口實(shí)現(xiàn)的動(dòng)態(tài)代理只能代理接口的實(shí)現(xiàn)類.
動(dòng)態(tài)代理實(shí)現(xiàn)
1) 處理者(Handler)
public class DynaProxyHandler implements InvocationHandler {
private Logger logger = Logger.getLogger(DynaProxyHandler.class);
private Object target;
//被代理對(duì)象
public void setTarget(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
logger.info("執(zhí)行開始時(shí)間:" + new Date());
Object result = method.invoke(target, args);
logger.info("執(zhí)行結(jié)束時(shí)間:" + new Date());
return result;
//返回method執(zhí)行結(jié)果
}
}
2) 生產(chǎn)代理對(duì)象的工廠
import java.lang.reflect.Proxy;
public class DynaProxyFactory {
//obj為被代理對(duì)象
public static Object getProxy(Object obj){
DynaProxyHandler handler = new DynaProxyHandler();
handler.setTarget(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
3) 測(cè)試類
public class PersonTest {
public static void main(String[] args) {
IPerson person = (IPerson) DynaProxyFactory.getProxy(new Person());
//返回代理類,代理類是JVM在內(nèi)存中動(dòng)態(tài)創(chuàng)建的,該類實(shí)現(xiàn)傳入的接口數(shù)組的全部接口(的全部方法).
person.eating();
person.sleep();
}
}
總結(jié)
以上就是本文關(guān)于Spring靜態(tài)代理和動(dòng)態(tài)代理代碼詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
SpringMVC攔截器實(shí)現(xiàn)單點(diǎn)登錄
Java編程實(shí)現(xiàn)springMVC簡(jiǎn)單登錄實(shí)例
如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
Java創(chuàng)建型設(shè)計(jì)模式之建造者模式詳解
建造者模式是Java中一種創(chuàng)建型設(shè)計(jì)模式,它的主要目的是將一個(gè)復(fù)雜對(duì)象的構(gòu)建過程分解為多個(gè)簡(jiǎn)單對(duì)象的構(gòu)建過程,本文將詳細(xì)介紹Java中的建造者模式,包括它的定義、結(jié)構(gòu)、實(shí)現(xiàn)方法以及應(yīng)用場(chǎng)景等方面,希望對(duì)大家有所幫助2023-05-05
MyBatis批量插入數(shù)據(jù)到Oracle數(shù)據(jù)庫中的兩種方式(實(shí)例代碼)
本文通過實(shí)例代碼給大家分享了MyBatis批量插入數(shù)據(jù)到Oracle數(shù)據(jù)庫中的兩種方式,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-09-09
Intellij IDEA 2017新特性之Spring Boot相關(guān)特征介紹
Intellij IDEA 2017.2.2版本針對(duì)Springboot設(shè)置了一些特性,本篇文章給大家簡(jiǎn)單介紹一下如何使用這些特性,需要的朋友參考下吧2018-01-01
深入分析@Resource和@Autowired注解區(qū)別
這篇文章主要為大家介紹了深入分析@Resource和@Autowired注解區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04

