手写实现Spring AOP

Spring IOC、AOP可谓是spring中两大王牌
上次我们手写了IOC,那么今天我们来手写一下AOP
感谢这篇博主的手写AOP博文(https://blog.csdn.net/qq_35704236/article/details/79997182)
我是学习他的写法,然后将一些我不懂的代码进行了注释解释,应该可以更加适合小白学习吧。

一.准备

首先我们都知道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> getClassSet(String packageName) throws IOException {
        Set> classSet = new HashSet<>();
        try {
            Enumeration 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 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> 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> 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 proxyBeanMap = new ConcurrentHashMap();
    static {
            initAopBeanMap("com.example.writeaopdemo.domain");
    }

    /** * 初始化容器 * @param basePath */
    public static void initAopBeanMap(String basePath){
        try{
            Set> 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 proxyBeanMap = ApplicationContext.proxyBeanMap;
        //生成代理对象,默认为该类名的小写
        Test test =(Test)proxyBeanMap.get("test");
        test.doSomeThing();
        System.out.println("------------");
        test.doWithNotProxy();
    }
}

全部代码地址:https://github.com/jet0605/writeAop

你可能感兴趣的:(Java,spring)