如何自己實現 Spring AOP?
以下內容來自公眾號逆鋒起筆,關注每日干貨及時送達
轉自:張喜碩
鏈接:https://segmentfault.com/a/1190000019148468
正好遇到了一道這樣的題:拋開Spring來說,如何自己實現Spring AOP?就喜歡這樣的題,能把那些天天寫增刪改查從來不思考的人給PK下去,今天就和大家一切學習代理模式與Spring AOP。
| 代理與裝飾器
場景描述
寧靜的午后,來到咖啡館,想喝一杯咖啡。
基礎實現
public?interface?Coffee?{/*** 打印當前咖啡的原材料,即咖啡里有什么*/void printMaterial();}
public class BitterCoffee implements Coffee {@Overridepublic void printMaterial() {System.out.println("咖啡");}}
public class Main {public static void main(String[] args) {Coffee coffee = new BitterCoffee();coffee.printMaterial();}}

裝飾器模式
/*** 糖裝飾器,用來給咖啡加糖*/public class SugarDecorator implements Coffee {/*** 持有的咖啡對象*/private final Coffee coffee;public SugarDecorator(Coffee coffee) {this.coffee = coffee;}@Overridepublic void printMaterial() {System.out.println("糖");this.coffee.printMaterial();}}
SugarDecorator為咖啡加糖,最后把加好糖的咖啡給我。public class Main {public static void main(String[] args) {Coffee coffee = new BitterCoffee();coffee = new SugarDecorator(coffee);coffee.printMaterial();}}

Coffee coffee = new BitterCoffee(); // 點了一杯苦咖啡coffee?=?new?SugarDecorator(coffee);???????//?給咖啡加了點糖
代理模式
iPad來到了咖啡館,準備享受一個寧靜的下午。public class CoffeeWithSugar implements Coffee {private final Coffee coffee;public CoffeeWithSugar() {this.coffee = new BitterCoffee();}@Overridepublic void printMaterial() {System.out.println("糖");this.coffee.printMaterial();}}
public class Main {public static void main(String[] args) {Coffee coffee = new CoffeeWithSugar();coffee.printMaterial();}}

差別
Coffee coffee = new BitterCoffee();coffee = new SugarDecorator(coffee);
Coffee coffee = new CoffeeWithSugar();
批評

Java中的接口可以讓我們符合依賴倒置原則進行開發(fā),降低耦合。用抽象類可以嗎?可以。用類繼承可以嗎?也可以。| AOP
Spring AOP息息相關。AOP:Aspect Oriented Programming,面向切面編程,是面向對象編程的補充。如果你不明白這句話,好好去學學面向對象就知道為什么了。Spring AOP的實現就是代理模式。場景
public interface SMSService {void sendMessage();}public class SMSServiceImpl implements SMSService {@Overridepublic void sendMessage() {System.out.println("【夢云智】您正在執(zhí)行重置密碼操作,您的驗證碼為:1234,5分鐘內有效,請不要將驗證碼轉發(fā)給他人。");}}
public class Main {public static void main(String[] args) {SMSService smsService = new SMSServiceImpl();smsService.sendMessage();smsService.sendMessage();}}
費用統計
Spring的思路,肯定是聲明一個切面,來切發(fā)短信的方法,然后在切面內統計短信費用。Spring來說,如何自己實現Spring AOP?JDK動態(tài)代理
InvocationHandler接口。Debug的時候都會跳轉到invoke方法。public class MoneyCountInvocationHandler implements InvocationHandler {/*** 被代理的目標*/private final Object target;/*** 內部存儲錢的總數*/private Double moneyCount;public MoneyCountInvocationHandler(Object target) {this.target = target;this.moneyCount = 0.0;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = method.invoke(target, args);moneyCount += 0.07;System.out.println("發(fā)送短信成功,共花了:" + moneyCount + "元");return result;}}
smsService替換為使用MoneyCountInvocationHandler處理的代理對象。public class Main {public static void main(String[] args) {SMSService smsService = new SMSServiceImpl();smsService = (SMSService) Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{SMSService.class},new MoneyCountInvocationHandler(smsService));smsService.sendMessage();smsService.sendMessage();}}

InvocationHandler中的invoke方法動態(tài)生成一個類,該類實現SMSService接口,代理對象,就是用這個類實例化的。
AOP實現
AOP是不是也不是難事?IOC容器初始化中,掃描包,去看看哪些個類需要生成代理對象,然后構造代理對象到容器中。invoke方法里,把統計費用的邏輯改成切面的邏輯不就好了嗎?不足分析
JDK的動態(tài)代理,是生成一個實現相應接口的代理類。但是Spring又不是只能通過接口注入。@Autowiredprivate Type xxxxx;
Spring的@Autowired是通過聲明的類型去容器里找符合的對象然后注進來的,接口是類型,類不也是類型嗎?@Autowiredprivate SMSService smsService;
@Autowiredprivate?SMSServiceImpl?smsService;
JDK動態(tài)代理針對直接注入類類型的,就代理不了。cglib動態(tài)代理
cglib。JDK動態(tài)代理解決不了的,統統交給cglib。@Autowiredprivate SMSServiceImpl smsService;
JDK動態(tài)代理解決不了。cglib怎么解決的呢?它會根據當前的類,動態(tài)生成一個子類,在子類中織入切面邏輯。cglib也不是萬能的,方法是final的,子類重寫不了,它當然也無計可施了。| 總結
IDEA 官方數據庫管理神器,比 Navicat 還香?(文末贈書)

評論
圖片
表情
