Spring筆記(8)——AOP

1 AOP介紹

  • AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術;
  • AOP是OOP(面向對象編程)的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型;
  • 利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率;
  • AOP採取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼;
  • 經典應用:事務管理、性能監視、安全檢查、緩存 、日誌等;
  • Spring AOP使用純Java實現,不需要專門的編譯過程和類加載器,在運行期通過代理方式向目標類織入增強代碼;
  • AspectJ是一個基於Java語言的AOP框架,Spring2.0開始,Spring AOP引入對Aspect的支持,AspectJ擴展了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入

1.1 AOP 實現原理:代理機制

  1. 接口 + 實現類 : Spring 採用 jdk 的動態代理 Proxy;
  2. 實現類: 採用 cglib字節碼 增強;

1.2 AOP 術語

  • target:目標類,需要被代理的類。例如:UserService
  • Joinpoint(連接點):所謂連接點是指那些可能被攔截到的方法。
  • PointCut 切入點:已經被增強的連接點。例如:addUser()
  • advice 通知/增強,增強代碼。例如:after、before
  • Weaving(織入):是指把增強advice應用到目標對象target來創建新的代理對象proxy的過程;
  • proxy 代理類
  • Aspect(切面): 是切入點pointcut和通知advice的結合;

  • 一個線是一個特殊的面,一個切入點和一個通知,組成成一個特殊的面。

2 手動代理, 通過 JDK Proxy 類

  • IUserService
package com.tzb.service;

import com.tzb.model.User;

public interface IUserService {
    public void add();
    public void add(User user);

    // 切面編程
    public void addUser();
    public void updateUser();
    public void delete();
}

  • UserServiceImpl
package com.tzb.service;

import com.tzb.dao.IUserDao;
import com.tzb.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("myUserService")
public class UserServiceImpl implements IUserService {

    @Autowired // spring 會自動往 userDao 賦值,不需要 get/set
    private IUserDao userDao;

    private String name;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void add() {
        System.out.println("service 創建用戶:" + name);
    }

    @Override
    public void add(User user) {
        System.out.println("service 添加用戶:"+ user);

        // 調用 dao
        userDao.add(user);
    }

    @Override
    public void addUser() {
        System.out.println("添加用戶。。。");
    }

    @Override
    public void updateUser() {
        System.out.println("更新用戶。。。");
    }

    @Override
    public void delete() {
        System.out.println("刪除用戶。。。");
    }

    public UserServiceImpl() {
        System.out.println("UserServiceImpl 被調用");
    }
}

  • 切面類 MyAspect
package com.tzb.service;

/**
 * 切面類,增強代碼與切入點的集合
 */
public class MyAspect {

    public void before() {
        System.out.println("開啓事務");
    }

    public void after() {
        System.out.println("提交事務");
    }
}

  • MyBeanFactory
package com.tzb.service;


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

public class MyBeanFactory {

    public static IUserService createUserService() {

        // 1. 創建目標對象 target
        final IUserService userService = new UserServiceImpl();

        // 2. 聲明切面類對象
        final MyAspect aspect = new MyAspect();

        // 3. 把切面類2個方法,應用到目標類
        // 創建代理
        IUserService serviceProxy = (IUserService) Proxy.newProxyInstance(
                MyBeanFactory.class.getClassLoader(),
                userService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        // 開啓事務
                        aspect.before();

                        // 內部類訪問外部成員變量,userService 加 final
                        Object retObj = method.invoke(userService, args);

                        System.out.println("攔截返回值:" + retObj);

                        // 提交事務
                        aspect.after();

                        // 返回值是業務方法的返回值
                        return retObj;
                    }
                }
        );

        return serviceProxy;
    }

}

  • 測試
  @Test
    public void test1() {
        // 自己實現 AOP 編程,使用 JDK 代理來實現
        IUserService userService = MyBeanFactory.createUserService();

        userService.delete();

        userService.addUser();

        userService.updateUser();
    }

在這裏插入圖片描述
在這裏插入圖片描述

3 cglib 增強字節碼

  • 沒有接口,只有實現類。
  • 採用字節碼增強框架 cglib,在運行時創建目標類的子類,從而對目標類進行增強。

  • StuService
package com.tzb.service;

public class StuService {

    public void delete() {
        System.out.println("刪除學生信息");
    }

    public void update() {
        System.out.println("更新學生信息");
    }

    public void add() {
        System.out.println("添加學生信息");
    }
}

  • MyBeanFactory
package com.tzb.service;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyBeanFactory {

    /**
     * cglib 實現代理
     */
    public static StuService createStuService() {

        // 1. 創建目標對象 target
        final StuService stuService = new StuService();

        // 2. 聲明切面類對象
        final MyAspect aspect = new MyAspect();

        // 3. 創建增強對象
        Enhancer enhancer = new Enhancer();

        // 設置父類
        enhancer.setSuperclass(stuService.getClass());

        // 設置回調【攔截】
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // proxy 代理對象是 StuService 的子類

                //System.out.println(methodProxy);

                aspect.before();

                // 放行方法
                //Object retObj = method.invoke(stuService, args);
                Object retObj = methodProxy.invokeSuper(proxy, args);

                System.out.println("攔截...");

                aspect.after();

                return retObj;
            }
        });

        // 創建代理對象
        StuService serviceProxy = (StuService) enhancer.create();

        // System.out.println("代理對象:" + serviceProxy);
        return serviceProxy;
    }

}


  • 測試
  @Test
    public void test1() {

        // 實現 AOP 編程,使用 cglib 代理來實現
        StuService ss = MyBeanFactory.createStuService();

        ss.add();
        ss.update();
        ss.delete();
    }

在這裏插入圖片描述

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