理解Spring的AOP和IOC實現(xiàn)原理

1.AOP

AOP(面向切面)是一種編程范式蝠引,提供從另一個角度來考慮程序結(jié)構(gòu)以完善面向?qū)ο缶幊蹋∣OP)。
AOP為開發(fā)者提供了一種描述橫切關注點的機制董栽,并能夠自動將橫切關注點織入到面向?qū)ο蟮能浖到y(tǒng)中,從而實現(xiàn)了橫切關注點的模塊化。
AOP能夠?qū)⒛切┡c業(yè)務無關杉畜,卻為業(yè)務模塊所共同調(diào)用的邏輯或責任,例如事務處理假栓、日志管理寻行、權(quán)限控制等,封裝起來匾荆,便于減少系統(tǒng)的重復代碼拌蜘,降低模塊間的耦合度,并有利于未來的可操作性和可維護性牙丽。

使用AOP的好處

  • 降低模塊的耦合度
  • 使系統(tǒng)容易擴展
  • 提高代碼復用性

AOP的基本概念

  • 連接點(JoinPoint):需要在程序中插入橫切關注點的點简卧,連接點可能是在類初始化、方法調(diào)用烤芦、字段調(diào)用或處理異常等等举娩。Spring中只支持方法執(zhí)行連接點。
  • 切入點(Pointcut):一組相關連接點的集合构罗。
  • 通知(Advice):在連接點上執(zhí)行的行為铜涉,增強提供了在AOP中需要在切入點所選擇的連接點處進行擴展現(xiàn)有行為的手段。包括前置增強(before advice)遂唧、后置增強 (after advice)芙代、環(huán)繞增強 (around advice)。
  • 切面(Aspect):通知和切入點的結(jié)合盖彭。
  • 織入(Weaving):織入是一個過程纹烹,是將切面應用到目標對象從而創(chuàng)建出AOP代理對象的過程。
  • 代理(Proxy):通過代理方式來對目標對象應用切面召边。AOP代理可以用JDK動態(tài)代理或CGLIB代理實現(xiàn)铺呵。
  • 目標對象(Target):需要被織入關注點的對象。即被代理的對象隧熙。


實現(xiàn)AOP的主要設計模式就是動態(tài)代理片挂。
Spring的動態(tài)代理有兩種:一是JDK的動態(tài)代理;另一個是cglib動態(tài)代理贱鼻。

JDK動態(tài)代理模擬
JDK動態(tài)代理的兩個核心接口(類)分別是InvocationHandler和Proxy宴卖。注意:只能代理接口。

public class TimeHandler implements InvocationHandler {  
      
    // 目標對象  
    private Object targetObject;  
    
    public TimeHandler(Object targetObject){
          this.targetObject = targetObject;
    }
    @Override  
    //關聯(lián)的這個實現(xiàn)類的方法被調(diào)用時將被執(zhí)行  
    /*InvocationHandler接口的方法邻悬,proxy表示代理症昏,method表示原對象被調(diào)用的方法,      
        args表示方法的參數(shù)*/  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
        Object ret=null;  
        try{  
            System.out.println("方法之前:"+System.currentTimeMillis());    
            //調(diào)用目標方法  
            ret=method.invoke(targetObject, args);  
            System.out.println("方法之后:"+System.currentTimeMillis());  
        }catch(Exception e){  
            e.printStackTrace();  
            System.out.println("error");  
            throw e;  
        }  
        return ret;  
    }  
  
} 

TimeHandler 類實現(xiàn)了InvocationHandler接口父丰。實現(xiàn)核心方法invoke肝谭,共有3個參數(shù)掘宪。第一個參數(shù) 生成的代理類實例,第二個參數(shù) 目標對象的方法攘烛,第三個參數(shù) 方法的參數(shù)值數(shù)組魏滚。

public class ProxyUtil {
    
    @SuppressWarnings("unchecked")
    public static <T> T proxyOne(ClassLoader loader,Class<?>[] clz,InvocationHandler handler){
        return (T)Proxy.newProxyInstance(loader, clz, handler);
    }
}

ProxyUtil 類簡單封裝了一下Proxy.newProxyInstance()方法。該方法也有3個參數(shù)坟漱。第一個參數(shù)產(chǎn)生代理對象的類加載器鼠次,第二個參數(shù)目標對象的接口數(shù)組,第三個參數(shù)就是實現(xiàn)InvocationHandler接口的類實例芋齿。

public interface UserManager {
    public void addUser(String userId, String userName);
}
public class UserManagerImpl implements UserManager {
    @Override
    public void addUser(String userId, String userName) {
        System.out.println("addUser(id:"+userId+",name:"+userName+")");
    }

}
public static void main(String[] args) {
         UserManager um=new UserManagerImpl(); 
         LogHandler log =new LogHandler(um); 
     um=ProxyUtil.proxyOne(um.getClass().getClassLoader(), 
                 um.getClass().getInterfaces(), log);
         
       TimeHandler time = new TimeHandler(um);
       um=ProxyUtil.proxyOne(um.getClass().getClassLoader(), 
                 um.getClass().getInterfaces(), time);
         
         um.addUser("1111", "張三");
    }

為了演示需要腥寇,這邊又增加了一個LogHandler,跟TimeHandler代碼一樣觅捆。

CGLIB動態(tài)代理模擬
CGLIB動態(tài)代理的兩個核心接口(類)分別是MethodInterceptor和Enhancer赦役。是不是跟JDK動態(tài)代理很相似,用法也差不多栅炒。但CGLIB可以代理類和接口掂摔。注意:不能代理final類。

public class TimeInterceptor implements MethodInterceptor {
    private Object target;  
    public TimeInterceptor(Object target) {
        this.target = target;
    }
    @Override
    public Object intercept(Object proxy, Method method, 
            Object[] args, MethodProxy invocation) throws Throwable {
        System.out.println("方法之前:"+System.currentTimeMillis());
        Object ret = invocation.invoke(target, args); 
        System.out.println("方法之后:"+System.currentTimeMillis());
        
        return ret;
    }
}

intercept方法4個參數(shù)赢赊。1.生成的代理類實例乙漓。2.被代理對象的方法引用。3.方法參數(shù)值數(shù)組释移。4.代理類對方法的代理引用簇秒。

public class ProxyUtil {
    @SuppressWarnings("unchecked")
    public static <T> T proxyOne(Class<?> clz,MethodInterceptor interceptor){
        return (T)Enhancer.create(clz, interceptor);
    }

}

Enhancer類是CGLib中的字節(jié)碼增強器。

public class UserManage {
    public void addUser(String userId, String userName) {
        System.out.println("addUser(id:"+userId+",name:"+userName+")");
    }
}


public static void main(String[] args) {
        UserManage um = new UserManage();
        TimeInterceptor time = new TimeInterceptor(um);
        um = ProxyUtil.proxyOne(um.getClass(), time);
        um.addUser("111", "老王");
    }

2.IOC

IOC(控制反轉(zhuǎn))就是依賴倒置原則的一種代碼設計思路秀鞭。就是把原先在代碼里面需要實現(xiàn)的對象創(chuàng)建、對象之間的依賴扛禽,反轉(zhuǎn)給容器來幫忙實現(xiàn)锋边。
Spring IOC容器通過xml,注解等其它方式配置類及類之間的依賴關系,完成了對象的創(chuàng)建和依賴的管理注入编曼。實現(xiàn)IOC的主要設計模式是工廠模式豆巨。


使用IOC的好處

  • 集中管理,實現(xiàn)類的可配置和易管理掐场。
  • 降低了類與類之間的耦合度往扔。

簡單模擬IOC

public interface BeanFactory {
    Object getBean(String id);  
}

public class ClassPathXmlApplicationContext implements BeanFactory {
    //容器,用來存放注入的Bean  
    private Map<String, Object> container = new HashMap<String, Object>();  

    //解析xml文件熊户,通過反射將配置的bean放到container中  
    public ClassPathXmlApplicationContext(String fileName) throws Exception{  
        SAXBuilder sb = new SAXBuilder(); 

        Document doc 
        =sb.build(ClassPathXmlApplicationContext.class.getResource("/"+fileName));
       
        Element root = doc.getRootElement();  
        List<Element> list = XPath.selectNodes(root, "/beans/bean");  

        for (int i = 0; i < list.size(); i++) {              
           Element bean = list.get(i);  
           String id = bean.getAttributeValue("id");  
           String clazz = bean.getAttributeValue("class");  
           Object o = Class.forName(clazz).newInstance();  
           container.put(id, o);  
          }  
    }
    @Override  
    public Object getBean(String id) {        
        return container.get(id);  
    }  

}

需要導入 jdom.jar包萍膛。

<?xml version="1.0" encoding="UTF-8"?>  
<beans>  
  <bean id="people" class="com.ioc.People" />  
  <bean id="chicken" class="com.ioc.Chicken" />  
  <bean id="dog" class="com.ioc.Dog" />  
</beans>  
public interface Animal {
     void say();  
}

public class Dog implements Animal {
    @Override
    public void say() {
         System.out.println("汪汪"); 
    }
}
public class Chicken implements Animal {
    @Override
    public void say() {
        System.out.println("雞你很美");  
    }
}
public class People {
    public void info(){
        System.out.println("小明-23歲");
    }
}
public static void main(String[] args) throws Exception {  

        //加載配置文件  
        BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");  

        Object os = f.getBean("dog");  
        Animal dog = (Animal)os;  
        dog.say();  

        Object op = f.getBean("chicken");  
        Animal chicken = (Animal)op;  
        chicken.say();  

        Object p = f.getBean("people");  
        People people= (Animal)p;  
        people.info();  
    } 

參考資料
我對AOP的理解
源碼解讀Spring IOC原理

關注課間指針,更多精彩等你

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嚷堡,一起剝皮案震驚了整個濱河市蝗罗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖串塑,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沼琉,死亡現(xiàn)場離奇詭異,居然都是意外死亡桩匪,警方通過查閱死者的電腦和手機打瘪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來傻昙,“玉大人闺骚,你說我怎么就攤上這事∥葚埃” “怎么了葛碧?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長过吻。 經(jīng)常有香客問我进泼,道長,這世上最難降的妖魔是什么纤虽? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任乳绕,我火速辦了婚禮,結(jié)果婚禮上逼纸,老公的妹妹穿的比我還像新娘洋措。我一直安慰自己,他們只是感情好杰刽,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布菠发。 她就那樣靜靜地躺著,像睡著了一般贺嫂。 火紅的嫁衣襯著肌膚如雪滓鸠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天第喳,我揣著相機與錄音糜俗,去河邊找鬼。 笑死曲饱,一個胖子當著我的面吹牛悠抹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扩淀,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼楔敌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了引矩?” 一聲冷哼從身側(cè)響起梁丘,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤侵浸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后氛谜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掏觉,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年值漫,在試婚紗的時候發(fā)現(xiàn)自己被綠了澳腹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡杨何,死狀恐怖酱塔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情危虱,我是刑警寧澤羊娃,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站埃跷,受9級特大地震影響蕊玷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弥雹,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一垃帅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剪勿,春花似錦贸诚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至头朱,卻和暖如春媒怯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背髓窜。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留欺殿,地道東北人寄纵。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像脖苏,于是被迫代替她去往敵國和親程拭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容