1. 程式人生 > >手寫實現Spring AOP

手寫實現Spring AOP

Spring IOC、AOP可謂是spring中兩大王牌
上次我們手寫了IOC,那麼今天我們來手寫一下AOP

我是學習他的寫法,然後將一些我不懂的程式碼進行了註釋解釋,應該可以更加適合小白學習吧。

一.準備

首先我們都知道Spring AOP的核心技術是動態代理,但是在這個demo中用的不是Jdj1.8的動態代理方法。
用的是cgib的動態代理方式。
那麼這二者有什麼區別呢?

JDK動態代理: 只能代理實現了介面的類
沒有實現介面的類不能實現JDK動態代理。

Cglib代理: 針對類來實現代理,對指定目標
產生一個子類 通過方法攔截技術攔截所有父類方法的呼叫。

二.思路

1 掃描 aop 包, 獲取 aspect 的類

2 根據 切點 獲取該切點的 類 和 方法

3 根據配置的 類 和 方法 為該類生成一個代理物件

4 將改代理物件放入 bean Map 中

5 呼叫的時候 將代理物件 轉換成需要的物件

三.具體程式碼

註解:

package com.example.writeaopdemo.annotion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import
java.lang.annotation.Target; /** * @author Jet */ @Retention(RetentionPolicy.RUNTIME) //保留時間長短 @Target(value = {ElementType.TYPE})//使用範圍、介面、類、列舉、註解 public @interface Aspect { } package com.example.writeaopdemo.annotion; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import
java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 方法切入點 */ @Retention(RetentionPolicy.RUNTIME)//保留時間長短 @Target(value = {ElementType.METHOD}) //使用範圍 方法 public @interface PointCut { /** * 全類名_方法名 * @return */ String value(); }

測試bean測試Aspect

package com.example.writeaopdemo.domain;

import com.example.writeaopdemo.annotion.Aspect;
import com.example.writeaopdemo.annotion.PointCut;
import com.example.writeaopdemo.proxy.AbsMethodAdvance;
@Aspect
public class TestAspect extends AbsMethodAdvance {
    /**
     *全類名 方法名
     */
    @PointCut("com.example.writeaopdemo.domain.Test_doSomeThing")
    public void testAspect(){

    }


    @Override
    public void doBefore() {
            System.out.println("do before");
    }

    @Override
    public void doAfter() {
        System.out.println("do after");
    }
}

代理類(在建立代理物件的同時攔截方法的執行實現before和after邏輯)

package com.example.writeaopdemo.proxy;

import com.example.writeaopdemo.util.StringUtils;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


import java.lang.reflect.Method;

/**
 * @author Jet
 */
public abstract class AbsMethodAdvance implements MethodInterceptor {
    /**
     * 要被代理的物件
     */
    private Object targetObject;

    /**
     * 被代理的方法名
     */
    private String proxyMethodName;

    public Object createProxyObject(Object target){
        this.targetObject = target;
        //該類用於生成代理物件
        Enhancer enhancer = new Enhancer();
        //設定目標類為代理物件的父類
        enhancer.setSuperclass(this.targetObject.getClass());
        //設定回撥用物件為本身
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable{
        Object result;

        String proxyMethod = getProxyMethodName();

        if(StringUtils.isNotBlank(proxyMethod) && proxyMethod.equals(method.getName())){
            doBefore();
        }

        //執行攔截的方法
        result = methodProxy.invokeSuper(proxy,args);

        if(StringUtils.isNotBlank(proxyMethod) && proxyMethod.equals(method.getName())){
            doAfter();
        }
        return result;
    }
    public abstract void doBefore();

    public abstract void doAfter();

    public String getProxyMethodName(){
        return proxyMethodName;
    }
    public void setProxyMethodName(String proxyMethodName){
        this.proxyMethodName = proxyMethodName;
    }
}

載入類

package com.example.writeaopdemo.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author Jet
 */
public class ClassUtil {
    /**
     * 獲取類載入器
     *
     * @return
     */
    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    /**
     * 載入類
     * 需要提供類名是否初始化標誌
     * 初始化是指知否執行靜態程式碼塊
     *
     * @param className
     * @param isInitialized
     * @return
     */
    public static Class<?> loadClass(String className, boolean isInitialized) {
        Class<?> cls;
        try {
            cls = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return cls;
    }

    /**
     * 載入類(預設將初始化類)
     *
     * @param className
     * @return
     */
    public static Class<?> loadClass(String className) {
        return loadClass(className, true);
    }

    public static Set<Class<?>> getClassSet(String packageName) throws IOException {
        Set<Class<?>> classSet = new HashSet<>();
        try {
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
            //Enumeration相當於老闆迭代器
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (url != null) {
                    String protocol = url.getProtocol();//獲得URL的協議
                    if (protocol.equals("file")) {
                        //轉碼
                        String packagePath = URLDecoder.decode(url.getFile(), "UTF-8");//轉碼為utf-8的格式
                        addClass(classSet, packagePath, packageName);
                    } else if (protocol.equals("jar")) {
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();//解析Jar檔案
                        if (jarURLConnection != null) {
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if (jarFile != null) {
                                Enumeration<JarEntry> jarEntries = jarFile.entries();
                                while (jarEntries.hasMoreElements()) {
                                    JarEntry jarEntry = jarEntries.nextElement();
                                    String jarEntryName = jarEntry.getName();
                                    if (jarEntryName.endsWith(".class")) {
                                        String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                        doAddClass(classSet, className);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw e;
        }
        return classSet;
    }
    private static void addClass(Set<Class<?>> classSet,String packagePath,String packageName){
       // System.out.println("packageName: " + packageName);
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class") || file.isDirectory());
            }
        });

        for(File file : files){
            String fileName = file.getName();
            if(file.isFile()){
                String className = fileName.substring(0,fileName.lastIndexOf("."));

                if(StringUtils.isNotBlank(packageName)){
                    className = packageName + "." + className;
                }
                //新增
                doAddClass(classSet,className);
            }else{
                //子目錄
                String subPackagePath = fileName;
                if(StringUtils.isNotBlank(packagePath)){
                    subPackagePath = subPackagePath + "/" + subPackagePath;
                }
                String subPackageName = fileName;
                if(StringUtils.isNotBlank(packageName)){
                    subPackageName = packageName + "." + subPackageName;
                }
                addClass(classSet,subPackagePath,subPackageName);
            }
        }
    }
    private static void doAddClass(Set<Class<?>> classSet,String className){
        Class<?> cls = loadClass(className,false);
        classSet.add(cls);
    }
}

通過反射機制建立例項呼叫方法和設定成員變數的值

package com.example.writeaopdemo.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionUtil {
    /**
     * 建立例項
     */
    public static Object newInstance(Class<?> cls){
        Object instance;

        try{
            instance = cls.newInstance();
        }catch (Exception e){
            throw new RuntimeException();
        }
        return instance;
    }

    /**
     * 建立例項 根據類名
     */
    public static Object newInstance(String className){
        Class<?> cls = ClassUtil.loadClass(className);
        return newInstance(cls);
    }

    /**
     * 呼叫方法
     */
    public static Object invokeMethod(Object obj, Method method,Object... args){
        Object result;
        try{
            method.setAccessible(true);
            result = method.invoke(obj,args);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 設定成員變數的值
     */
    public static void setField(Object obj, Field field,Object value){
        try{
            field.setAccessible(true);
            field.set(obj,value);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

字串處理類

package com.example.writeaopdemo.util;

/**
 * @author Jet
 */
public class StringUtils {
    public static boolean isNotBlank(String str){
        if(str != null && str.trim().length() > 0){
            return true;
        }else{
            return false;
        }
    }
    public static boolean isBlank(String str){
        return !isNotBlank(str);
    }
}

找到切點切面設定代理初始化容器類

package com.example.writeaopdemo;

import com.example.writeaopdemo.annotion.Aspect;
import com.example.writeaopdemo.annotion.PointCut;
import com.example.writeaopdemo.proxy.AbsMethodAdvance;
import com.example.writeaopdemo.util.ClassUtil;
import com.example.writeaopdemo.util.ReflectionUtil;
import org.apache.tools.ant.taskdefs.EchoXML;

import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Jet
 */
public class ApplicationContext {
    /**
     * 存放代理類的集合
     */
    public static ConcurrentHashMap<String,Object> proxyBeanMap = new ConcurrentHashMap<String, Object>();
    static {
            initAopBeanMap("com.example.writeaopdemo.domain");
    }

    /**
     * 初始化容器
     * @param basePath
     */
    public static void initAopBeanMap(String basePath){
        try{
            Set<Class<?>> classSet = ClassUtil.getClassSet(basePath);
            for(Class clazz : classSet){
                if(clazz.isAnnotationPresent(Aspect.class)){
                    Method[] methods = clazz.getMethods();
                    for(Method method : methods){
                        if(method.isAnnotationPresent(PointCut.class)){
                            //找到切點
                            PointCut pointCut = (PointCut)method.getAnnotations()[0];
                            String pointCutStr = pointCut.value();
                            //System.out.println("pointCutStr:" + pointCutStr);
                            String[] pointCutArr = pointCutStr.split("_");
                            //被代理的類名
                            String className = pointCutArr[0];
                            //System.out.println("className:" + className);
                            //被代理的方法名
                            String methodName = pointCutArr[1];
                           // System.out.println("methodName:" + methodName);

                            //根據切點 建立被代理物件
                            Object targeObj = ReflectionUtil.newInstance(className);
                            //根據切面類建立代理者
                            AbsMethodAdvance proxyer = (AbsMethodAdvance)ReflectionUtil.newInstance(clazz);
                            //設定代理的方法
                            proxyer.setProxyMethodName(methodName);

                            Object object = proxyer.createProxyObject(targeObj);

                            if(object != null){
                                proxyBeanMap.put(targeObj.getClass().getSimpleName().toLowerCase(),object);
                            }
                        }
                    }

                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

Main類執行

import com.example.writeaopdemo.ApplicationContext;
import com.example.writeaopdemo.domain.Test;

import java.util.concurrent.ConcurrentHashMap;

public class Main {
    public static void main(String[] args){
        //模擬容器初始化
        ApplicationContext applicationContext = new ApplicationContext();
        ConcurrentHashMap<String,Object> proxyBeanMap = ApplicationContext.proxyBeanMap;
        //生成代理物件,預設為該類名的小寫
        Test test =(Test)proxyBeanMap.get("test");
        test.doSomeThing();
        System.out.println("------------");
        test.doWithNotProxy();
    }
}

相關推薦

實現Spring AOP

Spring IOC、AOP可謂是spring中兩大王牌 上次我們手寫了IOC,那麼今天我們來手寫一下AOP 我是學習他的寫法,然後將一些我不懂的程式碼進行了註釋解釋,應該可以更加適合小白學習吧。 一.準備 首先我們都知道Spring AOP的核心技

實現Spring IOC

Spring中有兩大核心內容: 一.IOC 二.AOP 今天我手寫實現了IOC,來總結一下IOC的原理和實現方式 首先IOC底層所用到的技術 1>xml配置檔案 2>dom4j解析xml 3>工廠設計模式 4>反射 5&

透徹理解Spring事務設計思想之實現

數據庫操作 cal 了解 hashmap 個數 這一 use action 管道 前言 事務,是描述一組操作的抽象,比如對數據庫的一組操作,要麽全部成功,要麽全部失敗。事務具有4個特性:Atomicity(原子性),Consistency(一致性),Isolation(隔離

學習Spring原始碼:實現spinrgmvc

1.關於spring ioc容器:      spring ioc容器是什麼呢?    我們可以理解為將東西存放起來的東西。比如將java物件存放在ioc容器中。        簡單的說就是  ioc容器等於  ====>>>  Map<Strin

系列】透徹理解Spring事務設計思想之實現

事務,是描述一組操作的抽象,比如對資料庫的一組操作,要麼全部成功,要麼全部失敗。事務具有4個特性:Atomicity(原子性),Consistency(一致性),Isolation(隔離性),Durability(永續性)。在實際開發中,我們對事務應用最多就是在資料庫操作這

一個Spring框架(不含AOP

spring 手寫分三個階段: 1.配置階段: web.xml配置 servlet初始化 2.初始化階段: 載入配置檔案 ioc容器初始化 掃描相關的類 類例項化,並注入ioc容器 將url路徑和相關method進行對映關聯 3執行階段 dopost作為入

使用aspectJ實現Spring AOP的兩種方式

classpath .org 導入 ntc www. 之前 oid 方式 public 方式一:基於aspectJ的XML配置 方式二:基於aspectJ的註解方式 基於aspectJ的XML配置 1) 引入相關jar包 2) 創建Spr

理解數據庫連接池底層原理之實現

ring cda color 要去 分配 .com 管理 roc tex 前言 數據庫連接池的基本思想是:為數據庫連接建立一個“緩沖池”,預先在池中放入一定數量的數據庫連接管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向數據庫申請資

Spring AOP實現:Spring AOP攔截器呼叫的實現

上次我說到關於ProxyFactoryBean物件的getObject方法返回了一個代理物件。剩下的AOP實現過程就和jdk的代理模式相同。通過呼叫這個代理物件的方法(這個方法和目標物件是相同的),但是實際上是呼叫了invoke方法,通過反射來實現方法的增強。 關於jdk如何實現代理模式的反

react深入 - 實現react-redux api

簡介:簡單實現react-redux基礎api react-redux api回顧 <Provider store>把store放在context裡,所有子元件可以直接拿到store資料 使元件層級中的 connect() 方法都能夠獲得 Redux store 根元件應該巢狀在 &

系列】純實現一個高可用的RPC

前言 在實際後臺服務開發中,比如訂單服務(開發者A負責)需要呼叫商品服務(開發者B負責),那麼開發者B會和A約定呼叫API,以介面的形式提供給A。通常都是B把API上傳到Maven私服,然後B開始寫API的實現,A只需要引入API依賴進行開發即可。 訂單

【原創】自己實現Boost序列化簡易版

設計思路   在與多個系統進行網路互動時,序列化是不可缺少的技術。編寫一個C++語言的序列化實現,是練習運用模板超程式設計的絕佳案例,理解C++模板是如何"面向編譯期程式設計"的(業內好像沒有這個說法)。序列化物件處理基礎資料型別和類型別,boost的序列化功能劃分得更細緻,基本支援了C++語言

實現滿足 Promise/A+ 規範的 Promise

最近看了 Promise/A+ 的規範,嘗試實現了一個滿足 promises-aplus-tests 測試的 Promise 類,在實現規範的過程中,對於 Promise 本身也加深了理解,這篇文章就將我的實現過程分享出來。 本文的程式碼倉庫在這裡,歡迎 Star~。 前置知識 Prom

理解資料庫連線池底層原理之實現

前言 資料庫連線池的基本思想是:為資料庫連線建立一個“緩衝池”,預先在池中放入一定數量的資料庫連線管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向資料庫申請資源,釋放資源帶來的效能損耗。在如今的分散式系統當中,系統的QPS瓶頸往往就

Java動態代理概述和實現動態代理

一:前提 Spring中最重要的兩種思想:控制反轉IOC(Inversion of Control)和麵向切面程式設計AOP(Aspect-Oriented Programming),而AOP最重要的原理就是動態代理,今天我們談一下動態代理。動態代理顧名思義是代替別人做某些

對HashMap的思考及實現

前言HashMap是Java中常用的集合,而且HashMap的一些思想,對於我們平時解決業務上的一些問題,在思路上有幫助,基於此,本篇部落格將分析HashMap底層設計思想,並手寫一個迷你版的HashMap! 對HashMap的思考 HashMap底層資料結構 第

從 0 開始一個 Spring MVC 框架,向高手進階

Spring框架對於Java後端程式設計師來說再熟悉不過了,以前只知道它用的反射實現的,但瞭解之後才知道有很多巧妙的設計在裡面。如果不看Spring的原始碼,你將會失去一次和大師學習的機會:它的程式碼規範,設計思想很值得學習。 我們程式設計師大部分人都是野路子,不懂什麼

Anroid分析Andfix原理實現

前言          目前市面上對於熱修復一線網際網路企業大概分為三家:1、阿里  2、騰訊  3、美團 而這三家公司提供的開源庫,給了我們android開發者一些答案,今天我們瞭解一下阿里的andfix,目前andfix已經在16底停止維護了,新推出的是sophix,兼

實現RPC框架(帶註冊中心)

可能這個圖不太準確,但是大體意思就是服務端在註冊中心中註冊服務,客戶端在註冊中心獲取服務地址進行呼叫,中間可能還會有一些LB等: 定義一個註冊服務的頂層介面IRegistryCenter: package dgb.nospring.myrpc.regist

實現HashMap程式碼

瞭解HashMap底層=陣列+連結串列    HashMap底層原始碼通過 連結串列法 來解決hash衝突,找到hash值對應位置不為空,維護一個連結串列    ThreadLocal底層原始碼,ThreadLocalMap中通過 線性探測 解決hash衝突,找到hash值