手写spring AOP

背景

基于上次手写一个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注解方法存储至容器。
  1. 扫描Bean的时候,检索Bean方法上有没有自定义的标注AOP注解(@AOPTarget)注解,代表需要代理的类
  2. 在IOC扫描Bean的时候,检索Bean内部是否包含注解其类型为Around注明的Class。并选择性进行IOC是否需要代理
  3. 在需要代理的场景下进行切面的调用整合。执行前后进行控制。
  4. 本次只实现了通过一个自定义标识注解,来定位需要代理的类,没有实现像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 实现思路:手写mini springIoc - xiaohugg

源代码:spring-ioc: 手写简易的spring

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值