再也不怕aop的原理了


1 aop是什么

  java的核心思想是面向對象,aop是面向切面編程.是對面向對象的一個補充,簡單通俗的理解下aop,假設我們上樓梯,我的目標是三樓,我直接朝我的三樓直接過去,但是我可以在二樓的時候去朋友那里玩下,坐下其他的,然后再去三樓

這也不會影響我們的目標去三樓,那么去朋友那里玩,相當於增加的生活的樂趣去那里玩.相當於增強了我們的活動.

  那么aop可以做啥呢,可以進行日志記錄,可以進行事務管理,可以進行安全控制,可以進行異常處理,可以進行性能統計

2 理解幾個關鍵字

切面

 關注點形成的類,就叫切面(類)!

 面向切面編程,就是指對很多功能都有的重復的代碼抽取,再在運行的時候網業務方法上動態植入“切面類代碼”

切點

執行目標對象方法,動態植入切面代碼。

可以通過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。

通知

在對象上面執行的內容

 

3 aop的實現原理:

aop的底層實現是代理模式加反射.

反射就不用多講了,代理模式分為多種,靜態代理和動態代理,動態代理面又分為jdk動態代理和cglib動態代理

代理模式的好處:可以防止對方得到我們真實的方法;

靜態代理的實現方式:

package com.cxy.cyclicBarrier;

/**
 * Created by Administrator on 2017/4/24.
 */
public interface IUserDao {
    void save();
}
package com.cxy.cyclicBarrier;

/**
 * Created by Administrator on 2019/4/24.
 */
public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("已經保存數據");
    }
}

代理類:

package com.cxy.cyclicBarrier;

/**
 * Created by Administrator on 2019/4/24.
 */



public class UserDaoProxy  implements IUserDao{
    private IUserDao target;

    public UserDaoProxy(IUserDao iuserDao) {
        this.target = iuserDao;
    }

    public void save() {
        System.out.println("開啟事物...");
        target.save();
        System.out.println("關閉事物...");
    }

    public static void main(String[] args) {
        UserDao userDao=new UserDao();
        UserDaoProxy userDaoProxy=new UserDaoProxy(userDao );
        userDaoProxy.save();
    }

}

在最后可以看到執行結果:

結果分析:

 

靜態代理:

由程序員創建或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委托類的關系在運行前就確定了。

然后再看jdk動態代理:

package com.cxy.cyclicBarrier;

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

// 每次生成動態代理類對象時,實現了InvocationHandler接口的調用處理器對象
public class InvocationHandlerImpl implements InvocationHandler {
    private Object target;// 這其實業務實現類對象,用來調用具體的業務方法
    // 通過構造函數傳入目標對象
    public InvocationHandlerImpl(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("調用開始處理");
        result = method.invoke(target, args);
        System.out.println("調用結束處理");
        return result;
    }

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // 被代理對象
        IUserDao userDao = new UserDao();
        InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
        ClassLoader loader = userDao.getClass().getClassLoader();
        Class<?>[] interfaces = userDao.getClass().getInterfaces();
        // 主要裝載器、一組接口及調用處理動態代理實例
        IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
        newProxyInstance.save();
    }

}

結果分析:

jdk動態代理:

1. 通過實現InvocationHandler接口創建自己的調用處理器 IvocationHandler handler = new InvocationHandlerImpl(…);

2. 通過為Proxy類指定ClassLoader對象和一組interface創建動態代理類Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});

3. 通過反射機制獲取動態代理類的構造函數,其參數類型是調用處理器接口類型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

4. 通過構造函數創建代理類實例,此時需將調用處理器對象作為參數被傳入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

cglib代理:

package com.cxy.cyclicBarrier;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {
    private Object targetObject;
    // 這里的目標類型為Object,則可以接受任意一種參數作為被代理類,實現了動態代理
    public Object getInstance(Object target) {
        // 設置需要創建子類的類
        this.targetObject = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("開啟事物");
        Object result = proxy.invoke(targetObject, args);
        System.out.println("關閉事物");
        // 返回代理對象
        return result;
    }


    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
        userDao.save();
    }
}

執行結果:

在執行上面代碼前需要引入asm包

原理:利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

4 區別比較:
CGLIB動態代理與JDK動態區別

java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。

而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

Spring中。

1)、如果目標對象實現了接口,默認情況下會采用JDK的動態代理實現AOP

2)、如果目標對象實現了接口,可以強制使用CGLIB實現AOP

3)、如果目標對象沒有實現了接口,必須采用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

JDK動態代理只能對實現了接口的類生成代理,而不能針對類 。
CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法 。
因為是繼承,所以該類或方法最好不要聲明成final ,final可以阻止繼承和多態。

5 aop的使用方式:

 1) 注解方式:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>  開啟事物注解權限
@Aspect                            指定一個類為切面類        
@Pointcut("execution(* com.xhz.service.UserService.add(..))")  指定切入點表達式
@Before("pointCut_()")                前置通知: 目標方法之前執行
@After("pointCut_()")                后置通知:目標方法之后執行(始終執行)
@AfterReturning("pointCut_()")         返回后通知: 執行方法結束前執行(異常不執行)
@AfterThrowing("pointCut_()")            異常通知:  出現異常時候執行
@Around("pointCut_()")                環繞通知: 環繞目標方法執行
 
@Component
@Aspect
public class AopLog {
 
    // 前置通知
    @Before("execution(* com.cxy.service.UserService.add(..))")
    public void begin() {
        System.out.println("前置通知");
    }
 
    //
    // 后置通知
    @After("execution(* com.cxy.service.UserService.add(..))")
    public void commit() {
        System.out.println("后置通知");
    }
 
    // 運行通知
    @AfterReturning("execution(* com.cxy.service.UserService.add(..))")
    public void returning() {
        System.out.println("運行通知");
    }
 
    // 異常通知
    @AfterThrowing("execution(* com.cxy.service.UserService.add(..))")
    public void afterThrowing() {
        System.out.println("異常通知");
    }
 
    // 環繞通知
    @Around("execution(* com.cxy.service.UserService.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("環繞通知開始");
        proceedingJoinPoint.proceed();
        System.out.println("環繞通知結束");
    }
}
 

2)xml方式:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
 
    <!-- dao 實例 -->
 
    <bean id="userService" class="com.cxy.service.UserService"></bean>
    <!-- 切面類 -->
    <bean id="aop" class="com.cxy.aop2.AopLog2"></bean>
    <!-- Aop配置 -->
    <aop:config>
        <!-- 定義一個切入點表達式: 攔截哪些方法 -->
        <aop:pointcut expression="execution(* com.cxy.service.UserService.*(..))"
            id="pt" />
        <!-- 切面 -->
        <aop:aspect ref="aop">
            <!-- 環繞通知 -->
            <aop:around method="around" pointcut-ref="pt" />
            <!-- 前置通知: 在目標方法調用前執行 -->
            <aop:before method="begin" pointcut-ref="pt" />
            <!-- 后置通知: -->
            <aop:after method="after" pointcut-ref="pt" />
            <!-- 返回后通知 -->
            <aop:after-returning method="afterReturning"
                pointcut-ref="pt" />
            <!-- 異常通知 -->
            <aop:after-throwing method="afterThrowing"
                pointcut-ref="pt" />
        </aop:aspect>
    </aop:config>
 
</beans>
public class AopLog2 {
 
    // 前置通知
    public void begin() {
        System.out.println("前置通知");
    }
 
    //
    // 后置通知
    public void commit() {
        System.out.println("后置通知");
    }
 
    // 運行通知
    public void returning() {
        System.out.println("運行通知");
    }
 
    // 異常通知
    public void afterThrowing() {
        System.out.println("異常通知");
    }
 
    // 環繞通知
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("環繞通知開始");
        proceedingJoinPoint.proceed();
        System.out.println("環繞通知結束");
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM