九 Spring-AOP切面編程(1)

目錄

1. Web MVC發(fā)展史歷程
2.Spring概要
3.Spring-依賴注入概要(IOC)
4.屬性注入的三種實(shí)現(xiàn)方式
5.Spring-IoC XML裝配
6.Spring-XML設(shè)置Bean的值
7.Spring-IoC 注解(1)
8.Spring-IoC 注解(2)
9.Spring-AOP切面編程(1)
10.Spring-AOP切面編程(2)
11.Spring-AOP切面編程(3)
未完待續(xù)...

一、概要

軟件開發(fā)一直在尋求一種高效匿醒、護(hù)展戏蔑、維護(hù)的方式椭赋。

面向?qū)ο蟮奶攸c(diǎn)是繼承摩桶、多態(tài)和封裝赴精。而封裝的核心就是將功能分散到不同的對象中去胖喳,這在軟件設(shè)計(jì)中往往稱為職責(zé)分配以清。也就是說儿普,讓不同的類設(shè)計(jì)不同的方法。這樣代碼就分散到一個(gè)個(gè)的類中去了掷倔。這樣做的好處是降低了代碼的復(fù)雜程度眉孩,使類的復(fù)用性增加。但是新的問題又來了勒葱,在分散代碼的同時(shí)浪汪,也增加了代碼的重復(fù)性。什么意思呢凛虽?比如說死遭,我們在兩個(gè)類中,可能都需要在每個(gè)方法中做日志凯旋。按面向?qū)ο蟮脑O(shè)計(jì)方法呀潭,我們就必須在兩個(gè)類的方法中都加入日志的內(nèi)容。也許他們是完全相同的至非,但就是因?yàn)槊嫦驅(qū)ο蟮脑O(shè)計(jì)讓類與類之間無法聯(lián)系钠署,而不能將這些重復(fù)的代碼統(tǒng)一起來。

也許有人會(huì)說荒椭,那好辦啊谐鼎,我們可以將這段代碼寫在一個(gè)獨(dú)立的類獨(dú)立的方法里,然后再在這兩個(gè)類中調(diào)用趣惠。但是狸棍,這樣一來,這兩個(gè)類跟我們上面提到的獨(dú)立的類就有耦合了味悄,它的改變會(huì)影響這兩個(gè)類草戈。那么,有沒有什么辦法侍瑟,能讓我們在需要的時(shí)候猾瘸,隨意地加入代碼呢?這種在運(yùn)行時(shí),動(dòng)態(tài)地將代碼切入到類的指定方法牵触、指定位置上的編程思想就是面向切面的編程,我們把切入到指定類指定方法的代碼片段稱為切面咐低,而切入到哪些類揽思、哪些方法則叫切入點(diǎn)。有了AOP见擦,我們就可以把幾個(gè)類共有的代碼钉汗,抽取到一個(gè)切片中,等到需要時(shí)再切入對象中去鲤屡,從而改變其原有的行為损痰。

參考資料

二、面向切面編程

1酒来、什么切面編程

面向切面編程(也叫面向方面):Aspect Oriented Programming(AOP)卢未。利用AOP可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低堰汉,提高程序的可重用性辽社,同時(shí)提高了開發(fā)的效率。AOP是OOP的補(bǔ)充和完善

2翘鸭、主要解決的問題

  1. 解決代碼復(fù)用的問題

  2. 解決關(guān)注點(diǎn)分離

  • 水平分離: 例如 控制層—服務(wù)層—數(shù)據(jù)庫

  • 垂直分離: 例如 用戶模塊—商品模塊

  • 關(guān)注點(diǎn)分離(功能): 業(yè)務(wù)和非業(yè)務(wù)

3滴铅、主要的應(yīng)用場景

  1. 日志記錄
  2. 性能統(tǒng)計(jì)
  3. 安全控制
  4. 事務(wù)處理
  5. 異常處理
  6. 權(quán)限處理等等

三、實(shí)例講解

1就乓、原始代碼

  1. 需求
    比如我們要寫一個(gè)UserService汉匙,在調(diào)用保存用戶和刪除用戶的時(shí)候需要去打開數(shù)據(jù)庫和開啟事務(wù)
  2. 示例代碼


    image
  3. 說明
    通過上面的代碼我們發(fā)現(xiàn)打開數(shù)據(jù),開啟事務(wù),提交事務(wù)生蚁,關(guān)閉數(shù)據(jù)庫是重復(fù)的代碼噩翠,有重復(fù)的代碼我們怎么辦?
    通過類封裝成方法提取重復(fù)的代碼啊!(抽取成類的方式我們稱之為:縱向抽取)

2、提取類封裝成方法

  1. 說明
    這個(gè)時(shí)候我們新建一個(gè)DBManager類 定義四個(gè)方法
  2. 示例代碼
    public class DBManager {
        public void open() {
            System.out.println("打開數(shù)據(jù)庫...");
        }
        public void colse() {
            System.out.println("關(guān)閉數(shù)據(jù)庫...");
        }
        public void begin() {
            System.out.println("開啟事務(wù)");
        }
        public void commit() {
            System.out.println("提交事務(wù)");
        }
    }
    
    public class UserService {
        private DBManager manager = new DBManager();
        public void save() {
            manager.open();
            manager.begin();
            System.out.println("保存用戶信息");
            manager.commit();
            manager.colse();
        }
        public void delete() {
            manager.open();
            manager.begin();
            System.out.println("刪除用戶");
            manager.commit();
            manager.colse();
        }
    }
    
  3. 說明
    通過上面的案例解決了代碼重復(fù)性的問題,但同時(shí)也會(huì)帶來另外兩個(gè)問題:
    • 耦合度:會(huì)造成封裝類和業(yè)務(wù)類的耦合,
    • 侵入性強(qiáng):被我們提取的邏輯代碼還是融合到業(yè)務(wù)邏輯中
      但是守伸,這種做法相比最原始的代碼寫法绎秒,已經(jīng)有了一些的改進(jìn),那么有沒有一種方案能解決耦合度,侵入性強(qiáng)的問題,
      答案就是代理模式

四尼摹、代理模式

1见芹、代理模式概要

代理模式是一種非常好理解的一種設(shè)計(jì)模式,舉幾個(gè)簡單的代理的例子

  • 比如玩游戲升級太麻煩了,這個(gè)是時(shí)候我們可以去請代練幫我們升級,那這個(gè)代練其實(shí)就是一個(gè)代理
  • 比如我們回家過年買不到火車票,通常加價(jià)請第三方的一些黃牛幫我們?nèi)ベI
  • 歌星或者明星都有一個(gè)自己的經(jīng)紀(jì)人蠢涝,這個(gè)經(jīng)紀(jì)人就是他們的代理人玄呛,當(dāng)我們需要找明星表演時(shí),不能直接找到該明星和二,只能是找明星的經(jīng)紀(jì)人

讓代練幫我們升級,我們可能就想下副本,讓黃牛幫我們買票,我們就的目的就是想回家,明星讓經(jīng)紀(jì)人接拍電影,明星只是需要去拍電影就行了徘铝。無論是游戲代練,黃牛,還是經(jīng)紀(jì)人其它他們其實(shí)都是在幫我們在做事(做一些我們不想做,或者做不來的事情),但并不能把所有的事情都幫我們做了,比如黃牛幫我們買點(diǎn)票了,回家你還的自己回吧,

2、分類

  1. 靜態(tài)
    由程序員創(chuàng)建代理類或特定工具自動(dòng)生成源代碼再對其編譯。在程序運(yùn)行前代理類的.class文件就已經(jīng)存在了惕它。
  2. 動(dòng)態(tài)
    在程序運(yùn)行時(shí)運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成怕午。

3、作用

代理解決的問題當(dāng)兩個(gè)類需要通信時(shí)淹魄,引入第三方代理類郁惜,將兩個(gè)類的關(guān)系解耦,讓我們只了解代理類即可甲锡,而且代理的出現(xiàn)還可以讓我們完成與另一個(gè)類之間的關(guān)系的統(tǒng)一管理兆蕉,但必須,代理類和委托類要實(shí)現(xiàn)相同的接口缤沦,因?yàn)榇碚嬲{(diào)用的還是委托類的方法

五虎韵、靜態(tài)代理

1、說明

2缸废、示例代碼

  1. 業(yè)務(wù)代碼
    /**
     * 簡單業(yè)務(wù)層接口
     */
    public interface UserService{
        public void saveUser();
    }
    /**
     * 業(yè)務(wù)層實(shí)現(xiàn)類,實(shí)現(xiàn)save方法
     */
    public class UserServiceImpl implements UserService{
        @Override
        public void saveUser() {
            System.out.println("2:保存用戶信息");
        }
    }
    
  2. 代理類
    /**
     * 代理類
     */
    public class UserServiceProxy implements UserService{
        private UserService userService;
        public UserServiceProxy(UserService userService) {
            super();
            this.userService = userService;
        }
        public void open(){
            System.out.println("1:打開數(shù)據(jù)庫連接");
        }
        public void close(){
            System.out.println("3:關(guān)閉數(shù)據(jù)庫連接");
        }
        @Override
        public void saveUser() {
            this.open();
            userService.saveUser();
            this.close();
        }
    }
    
  3. 測試代碼
    /**
     * 測試類
     */
    public class TestProxy {
        public static void main(String[] args) {
            UserService userService =new UserServiceProxy(new UserServiceImpl());
            userService.saveUser();
        }
    }
    

3包蓝、分析

1、優(yōu)點(diǎn)

  1. 代理使客戶端不需要知道實(shí)現(xiàn)類是什么呆奕,怎么做的养晋,而客戶端只需知道代理即可(解耦合)

2、缺點(diǎn)

  1. 代理類和委托類實(shí)現(xiàn)了相同的接口梁钾,代理類通過委托類實(shí)現(xiàn)了相同的方法绳泉。這樣就出現(xiàn)了大量的代碼重復(fù)。如果接口增加一個(gè)方法姆泻,除了所有實(shí)現(xiàn)類需要實(shí)現(xiàn)這個(gè)方法外零酪,所有代理類也需要實(shí)現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度
  2. 代理對象只服務(wù)于一種類型的對象拇勃,如果要服務(wù)多類型的對象四苇。勢必要為每一種對象都進(jìn)行代理,靜態(tài)代理在程序規(guī)模稍大時(shí)就無法勝任了
    由于每個(gè)代理類只能為一個(gè)接口服務(wù)方咆,這樣程序開發(fā)中必然會(huì)產(chǎn)生許多的代理類
    所以我們就會(huì)想能不能通過一個(gè)代理類完成全部的代理功能?月腋,那么我們就需要用動(dòng)態(tài)代理

六、JDK動(dòng)態(tài)代理

1瓣赂、概念

JDK動(dòng)態(tài)代理所用到的代理類在程序調(diào)用到代理類對象時(shí)才由JVM真正創(chuàng)建榆骚,JVM根據(jù)傳進(jìn)來的業(yè)務(wù)實(shí)現(xiàn)類對象 以及 方法名 ,動(dòng)態(tài)地創(chuàng)建了一個(gè)代理類并執(zhí)行煌集,然后通過該代理類對象進(jìn)行方法調(diào)用妓肢。我們需要做的,只需指定代理類的預(yù)處理苫纤、調(diào)用后操作即可

目前Java開發(fā)包中包含了對動(dòng)態(tài)代理的支持碉钠,但是其實(shí)現(xiàn)只支持對接口的的實(shí)現(xiàn)纲缓。 其實(shí)現(xiàn)主要通過java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。

  • Proxy類主要用來獲取動(dòng)態(tài)代理對象喊废,

  • InvocationHandler接口用來約束調(diào)用者實(shí)現(xiàn)

2祝高、重要類介紹

2.1、Proxy

  1. 說明
    這是 Java 動(dòng)態(tài)代理機(jī)制的主類污筷,它提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其對象
  2. 核心方法
    返回值 方法 說明
    static InvocationHandler getInvocationHandler(Object proxy) 方法返回指定接口的代理類的實(shí)例褂策,這些接口將調(diào)用方法調(diào)用到指定的調(diào)用處理程序。
    static Class getProxyClass(ClassLoader loader, Class[] interfaces) 用于獲取關(guān)聯(lián)于指定類裝載器和一組接口的動(dòng)態(tài)代理類的類對象
    static boolean isProxyClass(Class cls) 該方法用于判斷指定類對象是否是一個(gè)動(dòng)態(tài)代理類
    staticObject newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 用于為指定類裝載器颓屑、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類實(shí)例

2.2、InvocationHandler

  1. 說明
    這是調(diào)用處理器接口耿焊,它自定義了一個(gè) invoke 方法揪惦,用于集中處理在動(dòng)態(tài)代理類對象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對委托類的代理訪問
  2. 語法
    // 該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類上的所有方法調(diào)用罗侯。器腋,第二個(gè)參數(shù)是被調(diào)用的方法對象
    // 第三個(gè)方法是調(diào)用參數(shù)。調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上發(fā)射執(zhí)行
    Object invoke(Object proxy, Method method, Object[] args)
    
  3. 參數(shù)說明
    • proxy
      第一個(gè)參數(shù)既是代理類實(shí)例
    • method
      被調(diào)用的方法對象
    • args
      調(diào)用參數(shù)钩杰。調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上發(fā)射執(zhí)行

3纫塌、示例代碼

3.1、聲明代理類

  1. 說明
    動(dòng)態(tài)代理類只能代理接口(不支持抽象類)讲弄,代理類都需要實(shí)現(xiàn)InvocationHandler類措左,實(shí)現(xiàn)invoke方法。該invoke方法就是調(diào)用被代理接口的所有方法時(shí)需要調(diào)用的避除,該invoke方法返回的值是被代理接口的一個(gè)實(shí)現(xiàn)類
  2. 示例代碼
    // 第一步  定義代理類,實(shí)現(xiàn)InvocationHandler接口
     public class DynamicProxy implements InvocationHandler {
        //代理目標(biāo)對象
        private Object target;
        public Object newProxyInstance(Object object) {
         this.target = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader()
                , object.getClass().getInterfaces(),
                this);
     }
        /**
         * 關(guān)聯(lián)的這個(gè)實(shí)現(xiàn)類的方法被調(diào)用時(shí)將被執(zhí)行
         * @param proxy  定義代理類的類的實(shí)例
         * @param method 代理類要實(shí)現(xiàn)的接口列表
         * @param args   指派方法調(diào)用的調(diào)用處理程序
         * @return 返回執(zhí)行的目標(biāo)對象
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("====start====");
            /**
             * 打印方法的參數(shù)
             */
            if (args != null && 0 != args.length) {
                for (Object arg : args) {
                    System.out.println(arg);
                }
            }
            System.out.println("====方法被執(zhí)行前====");
            Object invoke = null;
            try {
                String name = method.getName();
                if (name.equals("add") || name.equals("delete")) {
                    // 核心方法被執(zhí)行
                    invoke = method.invoke(target, args);
                    System.out.println("====方法被執(zhí)行后====");
                }
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                System.out.println("====方法出錯(cuò)誤了====");
            }
            System.out.println("====end====");
            return invoke;
        }
     }
    

3.2 聲明代理接口與代理類

  1. 說明
    jdk動(dòng)態(tài)代理只能代理接口,所以第一步先聲明接口,然后在定義個(gè)實(shí)現(xiàn)類
  2. 示例代碼
     //代理接口類
     public interface UserDao {
       void add(String name);
       void delete(String uid);
     }
    public interface ShopDao {
        boolean addShop();
    }
    
     //代理接口的實(shí)現(xiàn)類
     public class UserDaoImpl implements UserDao {
        @Override
        public void add(String name) {
            System.out.println("add:===" + name);
        }
        @Override
        public void delete(String uid) {
            System.out.println("delete" + uid);
     }
    public class ShopDaoImpl implements ShopDao {
        @Override
        public boolean addShop() {
            System.out.println("核心方法====>添加商品信息");
            return false;
        }
    }     
    
  3. 測試代碼
    public class TestProxy {
        public static void main(String[] args) {
            DynamicProxy proxy = new DynamicProxy();
            UserDao userDao = (UserDao) proxy.newProxyInstance(new UserDaoImpl());
            userDao.add();
            userDao.delete();
            ShopDao shopDao = (ShopDao) proxy.newProxyInstance(new ShopDaoImpl());
            shopDao.addShop();
        }
    }
    

3.3怎披、總結(jié)

可以看到,我們可以通過DynamicProxy代理不同類型的對象瓶摆,如果我們把對外的接口都通過動(dòng)態(tài)代理來實(shí)現(xiàn)凉逛,那么所有的函數(shù)調(diào)用最終都會(huì)經(jīng)過invoke函數(shù)的轉(zhuǎn)發(fā),因此我們就可以在這里做一些自己想做的操作

七群井、CGLib動(dòng)態(tài)代理

1状飞、說明

JDK實(shí)現(xiàn)動(dòng)態(tài)代理需要實(shí)現(xiàn)類通過接口定義業(yè)務(wù)方法,對于沒有接口的類书斜,如何實(shí)現(xiàn)動(dòng)態(tài)代理呢诬辈,這就需要CGLib了。CGLib采用了非常底層的字節(jié)碼技術(shù)菩佑,其原理是通過字節(jié)碼技術(shù)為一個(gè)類創(chuàng)建子類自晰,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢織入橫切邏輯稍坯。JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)酬荞。

因?yàn)槭堑谌降乃行枰獙?dǎo)入第三方的jar包

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.10</version>
</dependency> 

2搓劫、示例代碼

  1. 定義代理類
    //代理對象
    public class  AccountDaoImpl{  
        public void add(String name) {
               System.out.println("add:===" + name);
           }
    }  
    
  2. 定義動(dòng)態(tài)代理類代理類
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    /**
     * 實(shí)現(xiàn)了一個(gè)方法攔截器接口
     */
    public class CglibProxy implements MethodInterceptor {
        // 實(shí)例化動(dòng)態(tài)代碼生成器
        private Enhancer enhancer = new Enhancer();
        /**
         * 動(dòng)態(tài)生成一個(gè)新的類,使用父類的無參構(gòu)造方法創(chuàng)建一個(gè)指定了特定回調(diào)的代理實(shí)例
         *
         * @param clazz 被代理對象
         * @return
         */
        public Object getProxy(Class clazz) {
            //設(shè)置需要?jiǎng)?chuàng)建子類的類
            enhancer.setSuperclass(clazz);
            //設(shè)置回調(diào)方法
            enhancer.setCallback(this);
            //通過字節(jié)碼技術(shù)動(dòng)態(tài)創(chuàng)建子類實(shí)例
            return enhancer.create();
        }
        /**
         * 實(shí)現(xiàn)MethodInterceptor接口方法
         * 攔截被代理對象的方法
         *
         * @param obj    代理類實(shí)例
         * @param method 被代理類所調(diào)用的被代理的方法
         * @param args   參數(shù)值列表
         * @param proxy  生成的代理類對方法的代理引用
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                                MethodProxy proxy) throws Throwable {
            System.out.println(obj.getClass().getName());
            System.out.println("前置代理");
            //通過代理類調(diào)用父類中的方法
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("后置代理");
            System.out.println(proxy.getClass().getName());
            return result;
        }
    }
    
  3. 測試
    public class TestCglib {
        public static void main(String[] args) {
            CglibProxy proxy = new CglibProxy();
            //通過生成子類的方式創(chuàng)建代理類
            UserDaoImpl dao = (UserDaoImpl) proxy.getProxy(UserDaoImpl.class);
            dao.add();
        }
    }
    

八混巧、總結(jié)

1枪向、動(dòng)態(tài)代理和靜態(tài)代理相比較,

最大的好處就是接口中聲明的所有的方法都被轉(zhuǎn)移到一個(gè)集中的方法中去處理咧党,就是invocke()方法.這樣在接口中聲明的方法比較多的情況下我們可以進(jìn)行靈活處理秘蛔,而不需要像靜態(tài)代理那樣每一個(gè)方法進(jìn)行中轉(zhuǎn)。

2傍衡、CGLib與JDK動(dòng)態(tài)代理的區(qū)別

  1. 原理
    • java動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類深员,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。
    • 而cglib動(dòng)態(tài)代理是利用asm開源包蛙埂,對代理對象類的class文件加載進(jìn)來倦畅,通過修改其字節(jié)碼生成子類來處理
  2. JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別
    • JDK動(dòng)態(tài)代理只能對實(shí)現(xiàn)了接口的類生成代理,而不能針對類
    • CGLIB是針對類實(shí)現(xiàn)代理绣的,主要是對指定的類生成一個(gè)子類叠赐,覆蓋其中的方法,因?yàn)槭抢^承屡江,所以該類或方法最好不要聲明成final
  3. 速度上
    • 在jdk6以后sun公司對JDK動(dòng)態(tài)代理進(jìn)行優(yōu)化,在調(diào)用次數(shù)較少的情況下芭概,JDK代理效率高于CGLIB代理效率
    • 只有當(dāng)進(jìn)行大量調(diào)用的時(shí)候,jdk6和jdk7比CGLIB代理效率低一點(diǎn)惩嘉,但是到j(luò)dk8的時(shí)候罢洲,jdk代理效率高于CGLIB代理

3、實(shí)現(xiàn)原理

  1. 通過實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器宏怔;
  2. 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創(chuàng)建動(dòng)態(tài)代理類奏路;
  3. 通過反射機(jī)制獲得動(dòng)態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型是調(diào)用處理器接口類型臊诊;
  4. 通過構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例鸽粉,構(gòu)造時(shí)調(diào)用處理器對象作為參數(shù)被傳入。
    // InvocationHandlerImpl 實(shí)現(xiàn)了 InvocationHandler 接口抓艳,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā)
    // 其內(nèi)部通常包含指向委托類實(shí)例的引用触机,用于真正執(zhí)行分派轉(zhuǎn)發(fā)過來的方法調(diào)用
    InvocationHandler handler = new InvocationHandlerImpl(..); 
    // 通過 Proxy 為包括 Interface 接口在內(nèi)的一組接口動(dòng)態(tài)創(chuàng)建代理類的類對象
    Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 
    // 通過反射從生成的類對象獲得構(gòu)造函數(shù)對象
    Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 
    // 通過構(gòu)造函數(shù)對象創(chuàng)建動(dòng)態(tài)代理類實(shí)例
    Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
    

4、注意事項(xiàng)

  1. 包:如果所代理的接口都是 public 的玷或,那么它將被定義在頂層包(即包路徑為空)儡首,如果所代理的接口中有非 public 的接口(因?yàn)榻涌诓荒鼙欢x為 protect 或 private,所以除 public 之外就是默認(rèn)的 package 訪問級別)偏友,那么它將被定義在該接口所在包(假設(shè)代理了 com.xxx.xxx 包中的某非 public 接口 A蔬胯,那么新生成的代理類所在的包就是 com.xxx.xxx),這樣設(shè)計(jì)的目的是為了最大程度的保證動(dòng)態(tài)代理類不會(huì)因?yàn)榘芾淼膯栴}而無法被成功定義并訪問位他;
  2. 類修飾符:該代理類具有 final 和 public 修飾符氛濒,意味著它可以被所有的類訪問产场,但是不能被再度繼承;
  3. 類名:格式是“proxyN”舞竿,其中 N 是一個(gè)逐一遞增的阿拉伯?dāng)?shù)字京景,代表 Proxy 類第 N 次生成的動(dòng)態(tài)代理類,值得注意的一點(diǎn)是骗奖,并不是每次調(diào)用 Proxy 的靜態(tài)方法創(chuàng)建動(dòng)態(tài)代理類都會(huì)使得 N 值增加确徙,原因是如果對同一組接口(包括接口排列的順序相同)試圖重復(fù)創(chuàng)建動(dòng)態(tài)代理類,它會(huì)很聰明地返回先前已經(jīng)創(chuàng)建好的代理類的類對象执桌,而不會(huì)再嘗試去創(chuàng)建一個(gè)全新的代理類鄙皇,這樣可以節(jié)省不必要的代碼重復(fù)生成,提高了代理類的創(chuàng)建效率仰挣。

三育苟、AOP實(shí)現(xiàn)原理

實(shí)現(xiàn)AOP的技術(shù),主要分為兩大類:

一是采用動(dòng)態(tài)代理技術(shù)椎木,代理能干嘛?代理可以幫我們增強(qiáng)對象的行為博烂!使用動(dòng)態(tài)代理實(shí)質(zhì)上就是調(diào)用時(shí)攔截對象方法香椎,對方法進(jìn)行改造、增強(qiáng)禽篱!用通俗的話來講就是在核心方法執(zhí)行前后,執(zhí)行特定的代碼

二是采用靜態(tài)織入的方式畜伐,引入特定的語法創(chuàng)建“方面”,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼躺率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玛界,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子悼吱,更是在濱河造成了極大的恐慌慎框,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件后添,死亡現(xiàn)場離奇詭異笨枯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)遇西,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門馅精,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粱檀,你說我怎么就攤上這事洲敢。” “怎么了茄蚯?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵压彭,是天一觀的道長睦优。 經(jīng)常有香客問我,道長哮塞,這世上最難降的妖魔是什么刨秆? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮忆畅,結(jié)果婚禮上衡未,老公的妹妹穿的比我還像新娘。我一直安慰自己家凯,他們只是感情好缓醋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绊诲,像睡著了一般送粱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掂之,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天抗俄,我揣著相機(jī)與錄音,去河邊找鬼世舰。 笑死动雹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的跟压。 我是一名探鬼主播胰蝠,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼震蒋!你這毒婦竟也來了茸塞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤查剖,失蹤者是張志新(化名)和其女友劉穎钾虐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笋庄,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡禾唁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了无切。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荡短。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哆键,靈堂內(nèi)的尸體忽然破棺而出掘托,到底是詐尸還是另有隱情,我是刑警寧澤籍嘹,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布闪盔,位于F島的核電站弯院,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏泪掀。R本人自食惡果不足惜听绳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望异赫。 院中可真熱鬧椅挣,春花似錦、人聲如沸塔拳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靠抑。三九已至量九,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颂碧,已是汗流浹背荠列。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留载城,地道東北人弯予。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像个曙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子受楼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

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

  • 使用 動(dòng)態(tài)代理有兩個(gè)對象艳汽,目標(biāo)對象和代理對象猴贰。使用JDK動(dòng)態(tài)代理,目標(biāo)對象必須實(shí)現(xiàn)一個(gè)接口河狐。 原理 要查看JDK動(dòng)...
    xiaoming_he閱讀 502評論 0 1
  • 多態(tài) 任何域的訪問操作都將有編譯器解析米绕,如果某個(gè)方法是靜態(tài)的,它的行為就不具有多態(tài)性 java默認(rèn)對象的銷毀順序與...
    yueyue_projects閱讀 942評論 0 1
  • 一 學(xué)習(xí)大綱 1. 動(dòng)態(tài)代理設(shè)計(jì)模式(JDK和cglib) 2. AOP詳解 3. AOP中幾種...
    落云和楓閱讀 540評論 0 0
  • (一)Java部分 1馋艺、列舉出JAVA中6個(gè)比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,101評論 0 62
  • 二胎捐祠,中年女性之殤 最近身邊朋友最多的話題是什么碱鳞,當(dāng)然是二胎。而且你有沒有發(fā)現(xiàn)踱蛀,忙著生二胎的都是年近40的中年婦女...
    鳳凰來儀_0b59閱讀 504評論 1 0