手动实现一个迷你版的AOP(实战增强版)(下)

简介: 手动实现一个迷你版的AOP(实战增强版)(下)

Cglib底层是如何生成字节码文件的


ASM


对于需要手动操纵字节码的需求,可以使用ASM,它可以直接生产 .class字节码文件,也可以在类被加载入JVM之前动态修改类行为。ASM的应用场景有AOP(Cglib就是基于ASM)、热部署、修改其他jar包中的类等。


整体的操作流程图如下所示:


image.png


先通过ClassReader读取编译好的.class文件


其通过访问者模式(Visitor)对字节码进行修改,常见的Visitor类有:对方法进行修改的MethodVisitor,或者对变量进行修改的FieldVisitor等


通过ClassWriter重新构建编译修改后的字节码文件、或者将修改后的字节码文件输出到文件中


如何自己实现一个简单版本的AOP


首先需要定义相关的注解:


package org.idea.spring.aop.version1.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @Author linhao
 * @Date created in 3:49 下午 2021/5/6
 */
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {
    String value() default "";
}


package org.idea.spring.aop.version1.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @Author linhao
 * @Date created in 3:42 下午 2021/5/6
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
    String value();
}


package org.idea.spring.aop.version1.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @Author linhao
 * @Date created in 3:43 下午 2021/5/6
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {
    String value();
}


package org.idea.spring.aop.version1.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @Author linhao
 * @Date created in 3:41 下午 2021/5/6
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {
    String value() default "";
}


接下来为自己定义的这些注解添砖加瓦,组合使用到一个Aspect的切面当中去:


package org.idea.spring.aop.version1.aspect;
import org.idea.spring.aop.version1.annotation.After;
import org.idea.spring.aop.version1.annotation.Aspect;
import org.idea.spring.aop.version1.annotation.Before;
import org.idea.spring.aop.version1.annotation.Pointcut;
import java.lang.reflect.Method;
/**
 * 切面
 *
 * @Author linhao
 * @Date created in 3:43 下午 2021/5/6
 */
@Aspect
public class MyAspect {
    @Pointcut("org.idea.spring.aop.version1.test.*.*(..)")
    public void pointCut(){
    }
    @Before("pointCut()")
    public void doBefore(Method method, Object object){
        System.out.println("doBefore");
    }
    @After("pointCut()")
    public void doAfter(Method method, Object object){
        System.out.println("doAfter");
    }
}


同时补充一个测试使用的方法


package org.idea.spring.aop.version1.test;
/**
 * @Author linhao
 * @Date created in 3:44 下午 2021/5/6
 */
public class TestMethod {
    public void doTest(){
        System.out.println("do test");
    }
}


最后是一个核型的AspectLoader加载器代码


package org.idea.spring.aop.version1;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.idea.spring.aop.version1.annotation.After;
import org.idea.spring.aop.version1.annotation.Aspect;
import org.idea.spring.aop.version1.annotation.Before;
import org.idea.spring.aop.version1.annotation.Pointcut;
import org.idea.spring.aop.version1.test.TestMethod;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @Author linhao
 * @Date created in 3:51 下午 2021/5/6
 */
public class AspectLoader {
    /**
     * 配置扫描aop的aspect基础包路径
     */
    public static final String PACKAGE_NAME = "org.idea.spring.aop";
    /**
     * 模拟ioc容器
     */
    public Map<String, Object> beanContainer = new HashMap<>();
    public AspectLoader() {
        this.beanContainer.put("TestMethod", new TestMethod());
    }
    public static void main(String[] args) {
        AspectLoader aspectLoader = new AspectLoader();
        aspectLoader.init();
        TestMethod testMethod = (TestMethod) aspectLoader.beanContainer.get("TestMethod");
        testMethod.doTest();
    }
    /**
     * 初始化aop的配置相关
     */
    private void init() {
        try {
            //获取切面点aspect
            List<Class> targetsWithAspectJAnnotationList = this.getAspectClass();
            for (Class targetsWithAspectJAnnotation : targetsWithAspectJAnnotationList) {
                Method beforeMethod = this.getBeforeMethod(targetsWithAspectJAnnotation);
                Pointcut pointcut = (Pointcut) this.getMethodAnnotation(targetsWithAspectJAnnotation, Pointcut.class);
                Method afterMethod = this.getAfterMethod(targetsWithAspectJAnnotation);
                List<Class> classList = this.getClassFromPackage(AspectLoader.class, pointcut.value().substring(0, pointcut.value().indexOf("*") - 1));
                for (Class sourceClass : classList) {
                    Object aspectObject = targetsWithAspectJAnnotation.newInstance();
                    Enhancer enhancer = new Enhancer();
                    enhancer.setSuperclass(sourceClass);
                    enhancer.setCallback(new MethodInterceptor() {
                        @Override
                        public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                            beforeMethod.invoke(aspectObject, method, obj);
                            methodProxy.invokeSuper(obj, objects);
                            afterMethod.invoke(aspectObject,method,obj);
                            return obj;
                        }
                    });
                    Object proxyObj = enhancer.create();
                    this.beanContainer.put(sourceClass.getSimpleName(), proxyObj);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
    private List<Class> getAspectClass() throws ClassNotFoundException, IOException {
        final ClassPath classPath = ClassPath.from(AspectLoader.class.getClassLoader());
        List<Class> aspectClass = new ArrayList<>();
        ImmutableSet<ClassPath.ClassInfo> clazz = classPath.getAllClasses();
        List<ClassPath.ClassInfo> list = clazz.asList();
        for (ClassPath.ClassInfo classInfo : list) {
            if (classInfo.getName() != null && classInfo.getPackageName().contains(PACKAGE_NAME)) {
                Class clazzTemp = Class.forName(classInfo.getName());
                if (clazzTemp.isAnnotationPresent(Aspect.class)) {
                    aspectClass.add(clazzTemp);
                }
            }
        }
        return aspectClass;
    }
    /**
     * 获取指定包名下边的所有类
     *
     * @param source
     * @param packageName
     * @return
     * @throws Exception
     */
    private List<Class> getClassFromPackage(Class source, String packageName) {
        List<Class> classList = new ArrayList<>();
        final ClassPath classPath;
        try {
            classPath = ClassPath.from(source.getClassLoader());
            ImmutableSet<ClassPath.ClassInfo> clazz = classPath.getAllClasses();
            List<ClassPath.ClassInfo> list = clazz.asList();
            for (ClassPath.ClassInfo classInfo : list) {
                if (classInfo.getName() != null && classInfo.getPackageName().contains(packageName)) {
                    classList.add(Class.forName(classInfo.getName()));
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return classList;
    }
    private Annotation getMethodAnnotation(Class source, Class annotationClass) {
        Method[] methods = source.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(annotationClass)) {
                Annotation[] beforeArr = method.getAnnotationsByType(annotationClass);
                if (beforeArr.length > 0) {
                    return beforeArr[0];
                }
            }
        }
        return null;
    }
    private Method getBeforeMethod(Class source) {
        Method[] methods = source.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Before.class)) {
                return method;
            }
        }
        return null;
    }
    private Method getAfterMethod(Class source) {
        Method[] methods = source.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(After.class)) {
                return method;
            }
        }
        return null;
    }
}


本文相关代码地址:


https://gitee.com/IdeaHome_admin/spring-framework-learn/tree/master/spring-aop

相关文章
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
3月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
3月前
|
XML Java 数据库
Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用
这篇文章是Spring5框架的实战教程,详细解释了AOP的关键术语,包括连接点、切入点、通知、切面,并展示了如何使用AspectJ注解来开发AOP实例,包括切入点表达式的编写、增强方法的配置、代理对象的创建和优先级设置,以及如何通过注解方式实现完全的AOP配置。
|
6月前
|
存储 消息中间件 Java
Java多线程实战-异步操作日志记录解决方案(AOP+注解+多线程)
Java多线程实战-异步操作日志记录解决方案(AOP+注解+多线程)
|
6月前
|
安全 前端开发 Java
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)3
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
114 0
|
6月前
|
Java 数据库连接 API
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)2
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
85 0
|
6月前
|
存储 Java 数据库连接
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)1
Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
88 0
|
6月前
|
前端开发 Java 数据库
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(下)
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(下)
106 0
|
6月前
|
Java 数据库连接 数据库
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(中)
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(中)
325 0
|
6月前
|
Java 关系型数据库 MySQL
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战(上)
【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换&读写分离】—— 案例实战