spring AOP-動態代理

AOP

AOP:面向切面的編程。
OOP編程是面向對象的編程方法,各個對象之間進行信息交互,實現業務邏輯,是橫向的實現方法,而AOP是OOP方法的補充,是一種縱向邏輯的處理。如:當業務邏輯已經基本編寫完成,但是客戶提出一個新需求,需要對所有業務進行一種安全審計的記錄,以用於各種管理的認證,這個時候如果繼續使用OOP編程的話,需要增加公共類,子類再去繼承;或者增加一個接口,然後各個子類再去實現接口方法,就需要編寫大量大量來處理,這樣我們的代碼就會變的更加臃腫,不利於後期的升級維護。
在這裏插入圖片描述
對象之間是oop編程,對於oop無法處理的縱向信息使用aop切面編程。
如果要了解aop,就需要了解動態代理技術

動態代理

spring的動態代理可以通過基於接口的代理和cglb代理實現

接口代理實現

下面通過一個例子來實現接口動態代理

package com.example.inf;

/*
 * 接口信息
 * */
public interface IUserDao {
    //增加
    void addUser();
    //刪除
    void deleteUser();
}

接口實現

package com.example.inf.impl;

import com.example.inf.IUserDao;

public class UserDaoImp implements IUserDao {
    public void addUser() {
        System.out.println("新增用戶方法");
    }

    public void deleteUser() {
        System.out.println("刪除用戶方法");
    }
}

正常業務完成後,需要增加對代碼的權限和日誌記錄,因此增加增強類處理方法

package com.example.aspect;

public class Myaspect {
    public  void check_Permissions(){
        System.out.println("檢查數據權限");
    }

    public void log(){
        System.out.println("打印日誌");
    }
}

增加代理類,用於增強接口方法處理

package com.example.proxy;

import com.example.aspect.Myaspect;
import com.example.inf.IUserDao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
* 創建代理類
* */
public class jdkProxy {

    private IUserDao userDao;

    public IUserDao getUserDao(final IUserDao userDao)
    {
        this.userDao=userDao;

        return (IUserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Myaspect myaspect=new Myaspect();
                        myaspect.check_Permissions();
                        Object obj=method.invoke(userDao,args);
                        myaspect.log();
                        return obj;
                    }
                });
    }
}

可以看到是通過Proxy.newProxyInstance方法來實現,它的方法體如下:

  /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     * 返回一個代理類的實例方法
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * 要增強的類
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * 要增強類的接口
     * @param   h the invocation handler to dispatch method invocations to
     * 要增強的方法
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given {@code loader} is {@code null} and
     *               the caller's class loader is not {@code null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               {@code RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface, {@code intf},
     *               the caller's class loader is not the same as or an
     *               ancestor of the class loader for {@code intf} and
     *               invocation of {@link SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to {@code intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               {@link SecurityManager#checkPermission s.checkPermission} with
     *               {@code ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}, or
     *          if the invocation handler, {@code h}, is
     *          {@code null}
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

測試

package com.example.ui;

import com.example.inf.IUserDao;
import com.example.inf.impl.UserDaoImp;
import com.example.proxy.jdkProxy;

public class Client {
    public static void main(String[] args) {
        //創建代理類
        jdkProxy jdkProxy=new jdkProxy();
        //創建目標對象
        IUserDao userDao=new UserDaoImp();
        //從代理對象獲取增強後的目標對象
        IUserDao userDaoZengQ=jdkProxy.getUserDao(userDao);
        //執行增強後的方法
        userDaoZengQ.addUser();
        userDaoZengQ.deleteUser();
    }
}

輸出:
檢查數據權限
新增用戶方法
打印日誌
檢查數據權限
刪除用戶方法
打印日誌
可以看出基於接口的動態代理類通過反射方法確實對我們的方法進行了加強,實現了目的,但是這種方法有侷限性,如果要增強的方法不是接口的實現類,就不能使用了。

cglib代理

CGLIB是一個高性能開源的代碼生成包,採用底層的字節碼技術,對指定的目標類生成一個子類,並對子類進行增強。
下面通過一個例子來演示CGLIB動態代理
增加一個具體類,具體類中沒實現接口。

package com.example.inf;

public class PeopleDao {

    public void eat(){
        System.out.println("吃飯");
    }

    public void sleep(){
        System.out.println("睡覺");
    }
}

增加一個代理類enhancer.create

package com.example.proxy;
import com.example.aspect.Myaspect;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy {

    public Object CreateProxy(final Object target){
        Enhancer enhancer=new Enhancer();
        return (Object)enhancer.create(target.getClass(), new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Myaspect myaspect=new Myaspect();
                myaspect.check_Permissions();
                Object obj= method.invoke(target,objects);
                myaspect.log();
                return obj;
            }
        });
    }
}

測試類

package com.example.ui;

import com.example.inf.PeopleDao;
import com.example.proxy.CglibProxy;

public class CglibClient {
    public static void main(String[] args) {
        //創建代理對象
        CglibProxy cglibProxy=new CglibProxy();
        //創建目標對象
        PeopleDao peopleDao=new PeopleDao();
        //獲取增強後的目標對象
        PeopleDao peopleDaoZengQ=(PeopleDao) cglibProxy.CreateProxy(peopleDao);
        //調用方法
        peopleDaoZengQ.eat();
        peopleDaoZengQ.sleep();
    }
}

輸出
檢查數據權限
吃飯
打印日誌
檢查數據權限
睡覺
打印日誌

總結

可以看出基於接口的Proxy和cglib的enhancer.create方法創建的動態代理可以實現動態增強方法。
理解動態代理可以理解後面的AOP方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章