文章 62
浏览 15135
手写spring AOP

手写spring AOP

image.png

背景

基于上次手写一个 mini 版的 Spring IoC,考虑把 AOP 也给接入进来

AOP 回顾

AOP 概念

AOP(Aspect Oriented Programming)是面向切面编程。是 OOP 面向对象编程思想的一种补充。

OOP 通过继承,封装,多态等概念构建一个对象的层级结构。构建的是一个纵向的关系。面对横向的问题,实现起来比较复杂,比如日志的输出。使用面向对象的思想,每个类都需要增加日志打印的相关代码。但是使用 aop 就可以很简单的解决这个问题。

aop 将影响了多个类的公共行为(如日志打印)封装为一个可重用模块,定义为一个切面(aspect)。切面中包括切入点,通知,连接点等概念。

切入点:就是需要做切面处理的位置,可以通过 @PointCut 中的 execution 值指定某个包,某个类或者某个方法。同时也可以使用自定义注解标注。

通知:包括 5 种,分别是前置,后置,返回,异常,环绕通知。分别定义增强代码执行的时机。

连接点:是可以用来做为切入点的位置。是程序执行的某个位置,可以为程序执行前,也可以是执行后或者抛出异常等一些时机点

aop 作用是降低程序的耦合度,提高可重用性和开发效率

AOP 简单使用

?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring-aop</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Aspect
@Component
@EnableAspectJAutoProxy
public class MyServiceAspect {

    @Pointcut("execution(* com.itxiongmao.service.*..*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(){
        System.out.println("前置方法执行了.....");
    }
    @After("pointCut()")
    public void after(){
        System.out.println("after方法执行了.....");
    }
  
    @AfterThrowing("pointCut()")
    public void afterThrowing(){
        System.out.println("afterThrowing方法执行了.....");
    }

    @AfterReturning("pointCut()")
    public void afterReturning(){
        System.out.println("afterReturning方法执行了.....");
    }
  
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature)proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        Parameter[] parameters = method.getParameters();
        Object proceed = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        return proceed;
    }
}

Spring aop 的底层原理-动态代理

Spring AOP 实现是依赖动态代理

**jdk:当被代理类有接口的时候 **

cglib:asm 字节码技术,没有接口就是使用 cglib,也可以强制使用 cglib

JDK 代理

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

public class JdkProxyDemo<T> {
    T obj;
    //target:被代理类
    public JdkProxyDemo(T target){
        this.obj = target;
    }
    public T getInstance(){
        return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("方法执行前执行");
                      try {
                          Object invoke = method.invoke(obj, args);
                          System.out.println("方法执行后执行");
                          return invoke;
                      }catch (Exception e){
                          System.out.println("抛出了异常执行");
                          return null;
                      }

                    }
                });
    }
}

CGLIB 动态代理

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class SampleClass {
    public void test(){
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SampleClass.class);
        enhancer.setCallback(new MethodInterceptor() {
                                 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                                     System.out.println("before method run...");
                                     Object result = methodProxy.invokeSuper(o, objects);
                                     System.out.println("after method run...");
                                     return result;
                                 }
                             }
        );
        SampleClass sample = (SampleClass) enhancer.create();
        sample.test();
    }
}

手写 AOP

自定义 AOP 大致思路

  1. 创建自己的 Around 注解,注解参数为 Class。创建 Aop 方法存储容器。本处仅实现了环绕通知。
  2. 在 IoC 创建 Bean 之前,优先扫描包下所有类的所有方法,把包含 Around 注解方法存储至容器。
  3. 扫描 Bean 的时候,检索 Bean 方法上有没有自定义的标注 AOP 注解(@AOPTarget)注解,代表需要代理的类
  4. 在 IoC 扫描 Bean 的时候,检索 Bean 内部是否包含注解其类型为 Around 注明的 Class。并选择性进行 IoC 是否需要代理
  5. 在需要代理的场景下进行切面的调用整合。执行前后进行控制。
  6. 本次只实现了通过一个自定义标识注解,来定位需要代理的类,没有实现像 Spring execution 通过表达式来实现

代码包架构

自定义 Aspect 切面注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 切面注解
 */
//这个注解可以作用在运行期
@Retention(RetentionPolicy.RUNTIME)
//指定该注解可以作用在类上
@Target(ElementType.TYPE)
public @interface Aspect {

}

自定义环绕通知注解 Around

import java.lang.annotation.*;

//这个注解可以作用在运行期
@Retention(RetentionPolicy.RUNTIME)
//指定该注解可以作用在类上
@Target(ElementType.METHOD)
public @interface Around {
    String execution() default "";

    Class<?> executionClass() default AopTarget.class;
}

自定义 Aop 代理标识注解 AopTarget 注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用于标记哪些方法需要被代理
 */
@Retention(RetentionPolicy.RUNTIME)
//指定该注解可以作用在类上
@Target(ElementType.METHOD)
public @interface AopTarget {
}

自定义 AOP 参数类 ProceedingJoinPoint

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.reflect.Method;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProceedingJoinPoint {
    //因为待会需要调用目标方法 这个就是目标方法
    private Method method;
    //方法参数
    private Object[] args;
    //对象
    private Object obj;

    /**
     * 被代理类会执行的业务方法
     * @return Object 业务返回值
     * @throws Throwable
     */
    public Object proceed() throws Throwable{
        return method.invoke(obj,args);
    }
}

自定义 AOP 切面类

import org.springframework.aop.annotation.AopTarget;
import org.springframework.aop.annotation.Around;
import org.springframework.aop.annotation.Aspect;
import org.springframework.aop.point.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAop {
    @Around(executionClass = AopTarget.class)
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("前置通知");
        Object proceed = joinPoint.proceed();
        System.out.println("后置通知");
        return proceed;
    }
}

IoC 扫描添加 Aop 类

package org.springframework.container;

import lombok.SneakyThrows;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.aop.JdkProxyDemo;
import org.springframework.aop.annotation.AopTarget;
import org.springframework.aop.annotation.Around;
import org.springframework.aop.annotation.Aspect;
import org.springframework.stereotype.*;
import org.springframework.xml.XmlParser;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

/**
 * @Version 1.0
 * @Author huqiang
 * @Description ClassPathXmlApplicationContext
 * @Date 2023/11/29 11:27
 **/
public class ClassPathXmlApplicationContext {

    private static final Logger logger = LogManager.getLogger(ClassPathXmlApplicationContext.class);

    /**
     * spring的ioc名字作为key
     */
    private final Map<String, Object> iocNameContainer = new ConcurrentHashMap<>();
    /**
     * spring的class作为key
     */
    private final Map<Class<?>, Object> iocClassContainer = new ConcurrentHashMap<>();

    /**
     * 根据接口,获取接口下的实现类
     * 类似 context.getBean(UserService.class)
     */
    private final Map<Class<?>, List<Object>> iocInterfacesContainer = new ConcurrentHashMap<>();

    private final Set<String> classFiles = new HashSet<>();

    /**
     * 标注了Aop切面集合
     */
    private final Set<Class<?>> aspectSet = new CopyOnWriteArraySet<>();
    /**
     * key 对应的class value 对应的List<String> methodName
     */
    private final Map<Class<?>, List<String>> aopClassMap = new ConcurrentHashMap<>();
    private final Map<Class<?>, Set<Class<?>>> aopTarget = new ConcurrentHashMap<>();

    /**
     * 提前注入的class
     */
    private final Set<Class<?>> beforeDi = new HashSet<>();

    private final String xmlPath;

    public ClassPathXmlApplicationContext(String xmlPath) {
        this.xmlPath = xmlPath;
        refresh();
    }

    @SneakyThrows
    private void refresh() {
        //解析componentScanPath 包扫描路径
        String componentScanPath = XmlParser.parse(xmlPath);

        //获取包扫描路径的class文件路径
        File file = findClassPath(componentScanPath);

        //获取.class文件结尾的包全路径名
        findClassFiles(file, componentScanPath, classFiles);

        //反射
        newInstance(classFiles);

        //处理AOP
        doAop();

        //实现对象的属性的依赖注入
        doDI();


        logger.fatal("iocNameContainer {}", iocNameContainer);
        logger.fatal("iocClassContainer {}", iocClassContainer);
        logger.fatal("iocInterfacesContainer {}", iocInterfacesContainer);
    }

    private void doAop() {
        if (aspectSet.isEmpty()) return;
        for (Class<?> aopClass : aspectSet) {
            Method[] methods = aopClass.getDeclaredMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Around.class)) {
                    Around annotation = method.getAnnotation(Around.class);
                    //这里只实现了用标记注解来实现定位到目标类方法  并未实现通过execution表达式来定位目标类方法
                    Class<?> aClass = annotation.executionClass();
                    //获取所有标注了AOPTarget注解的方法所在的类
                    Set<Class<?>> classes = aopTarget.getOrDefault(aClass, new HashSet<>());
                    if (classes.isEmpty()) {
                        return;
                    }
                    for (Class<?> targetClass : classes) {
                        if (aopClassMap.containsKey(targetClass)) {
                            //找到所有标记了aopTarget注解的方法  aopTarget注解只能标记再方法上,通过类找方法,一个类下有多个方法可标记
                            List<String> methodNames = aopClassMap.get(targetClass);
                            for (String methodName : methodNames) {

                                //!!!!! 很重要!!对于AOP代理的类,需要先填充注入属性,否则会出现异常
                                // Can not set com.xiaohu.springioc.dao.EmployeesRepository field com.xiaohu.springioc.service.impl.EmployeesServiceImpl.employeesRepository to com.sun.proxy.$Proxy12
                                //不加这段方法 就只能serviceimpl访问add方法引用repository引用,可以方法增强,
                                //但是无法从controller 一直往下传递,因为容器从原先bean引用换成了aop引用
                                //我们通过反射注入repository属性的时候,aop生成的代理对象找不到这个属性,所以就会报错
                                //所以对应AOP代理对象需要再更换引用的时候提前注入属性
                                doDiCommon(targetClass);
                                beforeDi.add(targetClass);
                                //生成代理对象
                                //targetClass:被需要切面代理的类 aopClass:标记Aspect的切面类  methodName:被需要切面代理的类下的methodName  method:切面类的方法 obj:被代理类的实例
                                JdkProxyDemo<Object> proxy = new JdkProxyDemo<>(targetClass, aopClass, methodName, method, iocClassContainer.get(targetClass));

                                Object instance = proxy.getInstance();
                                logger.fatal("生成代理对象 {}", instance.getClass().getSimpleName());

                                //替换ioc容器
                                iocClassContainer.put(targetClass, instance);

                                //替换名称容器
                                Annotation[] annotations = new Annotation[]{targetClass.getAnnotation(Component.class), targetClass.getAnnotation(Controller.class),
                                        targetClass.getAnnotation(Service.class), targetClass.getAnnotation(Repository.class)};
                                if (Arrays.stream(annotations).anyMatch(Objects::nonNull)) {
                                    String className = getBeanName(targetClass, annotations);
                                    iocNameContainer.put(className, instance);
                                }
                                //替换接口容器
                                Class<?>[] interfaces = targetClass.getInterfaces();
                                for (Class<?> anInterface : interfaces) {
                                    List<Object> childImplList = iocInterfacesContainer.get(anInterface);
                                    for (int i = 0; i < childImplList.size(); i++) {
                                        Object object = childImplList.get(i);
                                        if (object.getClass() == targetClass) {
                                            childImplList.set(i, instance);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void doDI() {
        Set<Map.Entry<Class<?>, Object>> entries = iocClassContainer.entrySet();
        entries.forEach(it -> doDiCommon(it.getKey()));
    }

    private void doDiCommon(Class<?> aclass) {
        //aop代理类,已提前注入,这里直接返回
        if (beforeDi.contains(aclass)) {
            return;
        }
        Field[] declaredFields = aclass.getDeclaredFields();
        Set<Field> hasAutowiredField = Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(Autowired.class)).collect(Collectors.toSet());
        hasAutowiredField.forEach(field -> {
            //依赖注入属性
            Autowired annotation = field.getAnnotation(Autowired.class);
            String value = annotation.value();
            Object bean;
            if ("".equals(value)) {
                //默认按类型获取
                Class<?> type = field.getType();
                bean = getBean(type);
                if (Objects.isNull(bean)) {
                    throw new IllegalStateException("获取不到 bean: " + type.getName());
                }
            } else {
                //按用户填写的beanName获取
                bean = iocNameContainer.getOrDefault(value, new IllegalArgumentException("找不到beanName: " + value));
            }
            try {
                field.setAccessible(true);
                field.set(iocClassContainer.get(aclass), bean);
            } catch (IllegalAccessException e) {
                logger.error("属性注入失败 {}", e.getMessage());
            }
        });
    }

    private static File findClassPath(String componentScanPath) {
        String path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath();
        String url = path + componentScanPath.replace(".", File.separator);
        // windows环境去除路径前面的 '/'
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            url = url.replaceFirst("/", "");
        }
        if (url.contains("test-classes")) {
            url = url.replace("test-classes", "classes");
        }
        return new File(url);
    }

    public static String getBeanName(Class<?> c, Annotation[] annotations) {
        try {
            Annotation annotation = Arrays.stream(annotations).filter(Objects::nonNull).collect(Collectors.toList()).get(0);
            Method valueMethod = annotation.annotationType().getDeclaredMethod("value");
            String value = (String) valueMethod.invoke(annotation);
            if (value != null && !value.isEmpty()) {
                return value;
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            // 处理异常: 可能是注解没有value()方法,或者其他反射调用错误
            logger.error("获取beanName 失败 {}", e.getMessage());
        }
        //没指定beanName 默认用类型首字母小写
        return Character.toLowerCase(c.getSimpleName().charAt(0)) + c.getSimpleName().substring(1);
    }

    private void putIoc(Class<?>[] interfaces, Object instance, String beanName, Class<?> c) {
        for (Class<?> anInterface : interfaces) {
            iocInterfacesContainer.computeIfAbsent(anInterface, k -> new ArrayList<>()).add(instance);
        }
        iocNameContainer.compute(beanName, (key, value) -> {
            if (value != null) {
                throw new IllegalStateException("Bean with name '" + beanName + "' already exists.");
            }
            return instance;
        });
        iocClassContainer.compute(c, (key, value) -> {
            if (value != null) {
                throw new IllegalStateException("Bean with class name '" + c.getSimpleName() + "' already exists.");
            }
            return instance;
        });
    }

    public Object getBean(String beanName) {
        return iocNameContainer.getOrDefault(beanName, null);
    }

    public <T> T getBean(Class<T> clazz) {
        //首先根据class获取,获取不到再通过接口获取
        if (iocClassContainer.containsKey(clazz)) {
            return clazz.cast(iocClassContainer.get(clazz));
        }
        List<Object> computed = iocInterfacesContainer.compute(clazz, (key, value) -> {
            if (value == null || value.isEmpty()) {
                return null;
            }
            if (value.size() > 1) {
                throw new IllegalArgumentException("只能获取到一个bean 但是获取到了 " + value.size() + "个相同类型的bean");
            }
            return value;
        });
        return computed == null ? null : clazz.cast(computed.get(0));
    }

    private void newInstance(Set<String> classFiles) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        for (String classFile : classFiles) {
            try {
                classFile = classFile.replace(File.separator, ".").replace(".class", "");
                Class<?> c = Class.forName(classFile);
                if (c.isAnnotationPresent(Aspect.class)) {
                    //有AOP切面注解 加到切面集合
                    aspectSet.add(c);
                }

                //如果类下的方法标注了AopTarget注解
                Method[] methods = c.getDeclaredMethods();
                for (Method method : methods) {
                    //如果想要增加其他AOP标记类,忘这个if增加一个if逻辑即可
                    if (method.isAnnotationPresent(AopTarget.class)) {
                        String methodName = method.getName();
                        aopClassMap.computeIfAbsent(c, k -> new ArrayList<>()).add(methodName);
                        aopTarget.computeIfAbsent(AopTarget.class, k -> new HashSet<>()).add(c);
                    }
                }
                Annotation[] annotations = new Annotation[]{c.getAnnotation(Component.class), c.getAnnotation(Controller.class),
                        c.getAnnotation(Service.class), c.getAnnotation(Repository.class)};
                if (Arrays.stream(annotations).anyMatch(Objects::nonNull)) {
                    String beanName = getBeanName(c, annotations);
                    Object instance = c.newInstance();
                    Class<?>[] interfaces = c.getInterfaces();
                    putIoc(interfaces, instance, beanName, c);
                }
            } catch (Exception e) {
                logger.error("构造bean失败 失败原因 {}", e.getMessage());
                throw e;
            }
        }
    }

    private void findClassFiles(File classFiles, String componentScanPath, Set<String> classNameList) {
        File[] files = classFiles.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.getName().endsWith(".class")) {
                    // 如果是.class文件,添加到列表
                    String fullPath = file.getAbsolutePath();
                    int index = fullPath.indexOf(componentScanPath.replace(".", File.separator));
                    if (index != -1) {
                        String filePath = fullPath.substring(index);
                        classNameList.add(filePath);
                    }
                } else if (file.isDirectory()) {
                    // 如果是目录,递归调用
                    findClassFiles(file, componentScanPath, classNameList);
                }
            }
        }
    }
}

特别需要注意的一点,因为 AOP 代理的原理就是通过 JDK 代理或者 chlib 代理,在原本 IoC 容器实现的时候,是先反射 Bean 实例,在反射填充注入 Bean 实例的属性,但是 AOP 最终被代理后会生成代理对象,会替换原本 IoC 容器真实 Bean 对象,导致原本的属性引用,无法反射注入,是因为代理对象并没有相应的属性字段,所以无法注入,·**field.set(iocClassContainer.get(aclass), bean);

这段代码会出现· // Can not set com.xiaohu.springioc.dao.EmployeesRepository field com.xiaohu.springioc.service.impl.EmployeesServiceImpl.employeesRepository to com.sun.proxy.$Proxy12 异常

解决办法:

  • 第一种解决办法: 将方法 doAop() 和·doDi()·方法调换顺序,这样可以解决,但是后面又会有新的问题,那就是只能通过被代理类去方法被增强的方法,才会触发 AOP,比如我们代理 ServiceImpl 的下的 add 方法,只有 容器 getBean ServiceImpl 这个实例.add 方法才会触发 Aop,然而如果我们是通过 controller 一直往下 MVC 传递去调 add 方法,就不会触发 AOP

所以就会出现只有通过 proxy 代理类去访问 add 方法,才会触发 AOP,而通过 controller 去访问 serviceImpl 就无法触发 AOP,因为 controller 和 proxy 之间没有引用

  • 第二种解决办法: 既然我们知道第一种解决的原因,那么我们只需要将 proxy 代理类和 controller 或者其他类构建一个引用关系即可,所以用到了 AOP 需要提前在构造 IoC 容器的时候,提前注入属性

在生成代理对象的时候,提前处理属性注入,只有 AOP 代理对象是这样,其他普通的 Bean 则依然保持反射出实例在属性注入

AOP 提前注入了,用一个提前注入集合 set 接收,后面就不需要再属性注入

测试

package com.xiaohu.springioc;
import com.xiaohu.springioc.controller.EmployeesController;
import org.springframework.container.ClassPathXmlApplicationContext;

public class TestSpringAop {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext("applicationContext.xml");
        EmployeesController employeesController = container.getBean(EmployeesController.class);
        employeesController.testAdd();
        employeesController.selectById(1);
    }
}

可以看到 serviceImpl 的 add 方法和 repository 的 selectById 也被增强了,至此一个简单的 aop 实现

结论

springAOP 的原理是通过 JDK 反射代理和 cglib 反射代理,通过解析注解表达式或者自定义切面标识注解,来定位到需要代理的类,类的全路径名和方法名,反射构造出实例,替换原先的 IoC 容器 bean

Spring IoC 实现思路:https://www.xiaohugg.top/articles/2023/11/30/1701348166909.html

源代码:https://gitee.com/xiaohu88/spring-ioc.git


标题:手写spring AOP
作者:xiaohugg
地址:https://xiaohugg.top/articles/2023/12/02/1701492272573.html

人民有信仰 民族有希望 国家有力量