Java学习篇(六)—— Spring框架底层

由于博主时间已经所剩无几,这样匆忙的学习实属无奈之举,有时间的朋友还是分开学习两个项目会更好,两个项目放在了资料一和二。但行其路,莫问前程。

基础

Spring是一个家族,可以做Web开发,微服务开发,分布式开发。对应的Spring的技术包括:Spring Cloud,Spring Web等。其它所有技术都是在Spring Framework上发展的,Spring Boot用来简化开发。Spring 5.x架构图如下所示:

image

Spring容器和Android的Application很相似,Application管理Activity、Fragment和Service,而Spring容器则是管理Controllor(准确来说是Bean);Activity在响应用户的点击,Controllor在响应客户端的连接;Activity的数据来源是Room和ViewModel,Controllor的数据来源是Service和Repository层 ;Activity显示UI并且处理用户操作,Controller则处理用户请求。
因为Android是一个UI应用框架,Activity 是整个用户界面和交互的中心,所以它成为生命周期的主体。 而在Spring中,Spring 是一个 IoC+AOP 框架,它的核心工作是创建、管理和销毁对象。

Core Container

Spring 容器本质就是一个 Java 对象,运行在 JVM 的堆内存中,和你写的普通对象一样,区别在于:它负责管理其他 Bean 对象,具体实现类就是ApplicationContextBeanFactory

Spring Core Container 是 Spring 的核心部分,提供了最基础的功能,比如对象的创建、管理、配置与依赖注入(DI)。 JavaGuide对它的介绍是这样的:

spring-core:Spring 框架基本的核心工具类。

spring-beans:提供对 bean 的创建、配置和管理等功能的支持。

spring-context:提供对国际化、事件传播、资源加载等功能的支持。

spring-expression:提供对表达式语言(Spring Expression Language) SpEL 的支持,只依赖于 core 模块,不依赖于其他模块,可以单独使用。

Core模块提供的BeanFactory是最基础的IoC容器接口,只支持最基本的Bean创建与依赖注入。Context 模块提供的ApplicationContext是BeanFactory的子接口,功能更强大,还提供了国际化、事件发布机制、Bean 自动装配、注解支持与 Spring AOP、事务管理等集成更方便,所以实际开发中我们常说的Spring 容器,几乎都是指ApplicationContext

一个项目会有几个容器?一个容器里同一个Bean类只会有一个实例吗?
在上面xml配置中,bookService 和 userService 注入的是同一个 dataSource 实例,bookService和userService里面的 dataSource 指向同一块堆内存 。Spring 默认情况下,每个 都是单例模式,一个名字只对应一个实例,想要同一个类的不同实例,可以使用不同的名字。

多个实例注入同一个对象,并且同时修改怎么办?
如果该 Bean 是无状态,只提供纯业务逻辑或者线程安全的服务,通常不会有问题。如果 Bean 有状态(成员变量)且被多个对象并发修改,可能出现线程安全问题和数据污染。尽量在设计时就不要在单例 Bean 中存储会被修改的共享状态,把状态放到方法参数或局部变量中,保证线程安全。或者使用ThreadLocal或使用同步机制确保线程安全。或者使用:

@Component
@Scope("prototype")
public class SharedService {
    // 每次注入都是新实例
}

这样每一次注入的都是新的实例。

IoC 控制反转

全称是Inversion of Control,控制反转,将对象的构建与管理控制权给了容器。我们不需要再手动的new一个对象,只需要声明对象,再给对象一个setter方法,IoC容器就会自动将对象注入。这样做有一个很直观的好处,多个类之间可以实现对象的共享,我们只需要一个对象实例,就可以在不同的类里使用。

什么是注入依赖?
一个对象要使用另一个对象就是依赖,注入依赖是Spring容器可以自动的将对象的依赖实例化然后传给对象。注入依赖可以有两种方式实现:1. .xml文件配置。 2. 注释。

文件配置

<beans>
  <bean id="dataSource" class="HikariDataSource" />
  <bean id="bookService" class="BookService">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <bean id="userService" class="UserService">
    <property name="dataSource" ref="dataSource" />
  </bean>

上面是一个.xml配置的代码, Spring通过解析这个xml文件,在容器里创建这三个对象,并且做好依赖关系。就是实例化了一个对象,对象的类型是class,对象的名字是id。

使用Java语言相当于:

HikariDataSource dataSource = new HikariDataSource();
<property name="xxx" ref="xxx" />就是调用了它所属对象的setter方法,将成员变量赋值为ref对应的实例。所以上面的代码相当于:
HikariDataSource dataSource = new HikariDataSource();

BookService bookService = new BookService();
bookService.setDataSource(dataSource);

UserService userService = new UserService();
userService.setDataSource(dataSource);

想象一下,一个容器需要xml文件来配置,一个项目只有一个容器,一个项目却有多个模块,每一个模块很多Java文件,一个文件又有很多依赖关系。那么我们所需要人工配置的xml实在太庞大了,所以出现了注解方法来代替xml文件。

注解

注解方法依赖于Core Container中的Bean和context,Bean代指的就是那些被 IoC 容器所管理的对象,context是负责解析注解、处理注解、根据注解做事 ,对于上面的Java语句,使用注解只需要在BookService里面使用注释即可:

@Component  // 把 BookService 注册成 Bean
public class BookService {

    @Autowired  // 让 Spring 自动注入 DataSource
    private DataSource dataSource;

    public void test() {
        System.out.println("BookService 使用数据源:" + dataSource);
    }
}

需要说明的是,注解只是一个标签,本身并不具备任何功能,整个Spring的核心实现,注解起到的只是标签作用,当我们使用反射获取注解后,就可以根据不同的注解对不同的class进行处理,然后统一放在容器里。

注解方式 作用
@Component 标准组件,业务逻辑、工具类等
@Service 标记 Service 层(业务层),逻辑处理
@Repository 标记 DAO 层(数据访问层),可与事务整合
@Controller 标记控制器层,处理前端请求
@RestController 标记 REST 风格控制器,返回 JSON
@Configuration 标记配置类,本身也是一个 Bean
@Bean 标记方法,方法返回值是一个 Bean

Java反射机制

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。

想要理解反射,就要先知道Java到底是怎么加载一个类的, JVM本身不主动扫描磁盘找类,而是依赖 ClassLoader类加载器去加载 .class 文件,ClassLoader 把字节码加载进来,JVM 把它转成 Class 对象,存在方法区。 加载完class之后,只是有了类的定义,没有类的实例,这时,通过new和getInstance()可以获取实例,区别在于:new是你自己写的代码,getInstance()是运行时决定的,当运行到这里时才有了这个实例。 它们本质上最终都调用 JVM 的对象创建逻辑:在堆上开辟内存、初始化成员变量、调用构造函数。
那么反射机制,就是通过一个实例,获取它的元数据,相当于前面的过程反了过来。

反射参与了Spring的什么环节?
通过上面对反射的了解可以知道,反射只能获取已经加载到了元空间的元数据,而加载到元空间这一步,则是由spring core和spring context实现的。Springl会ClassPath 扫描包路径,找到哪些类有 @Component、@Service 注解,然后用ClassLoader.loadClass()把它们加载进来得到Class对象。再通过反射机制,获取到Class对象的一系列方法、成员变量、注解等等,创建Bean对象,保存在容器的Beans Map中。需要注入对象实例时,先找容器中有没有类名称对应的实例,没有就通过Bean对象创建Bean实例,然后保存在另一个Map中,之后的注入只需要通过在实例Map中查找就可以了。
上面的看完,应该对反射到底是怎么参与的,还会觉得抽象,接下来直接通过summer-spring项目来学习怎么使用ApplicationContext + BeanFactory + 反射 + 映射实现了Spring框架。

Context

context就是容器,它的任务是解析注解,管理Bean对象。下面是summer-context的代码,可以大致反映出context的处理流程,但是和Spring框架有所不同的是,summer-spring只考虑单例的情况,率先把所有的Bean都实例化,并且放在了BeanDefinition里。而Spring框架除了singleton之外,支持一次引用一个实例,并且没有把实例放在BeanDefinition里,而是放在了singletonMap里。 Spring 的单例 Bean,默认在 ApplicationContext启动阶段就全部实例化,不等到第一次调用时才实例化,除了这种方式外,Spring还支持懒加载,在第一次访问时再创建实例。

public class AnnotationConfigApplicationContext implements ConfigurableApplicationContext {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    protected final PropertyResolver propertyResolver;
    protected final Map<String, BeanDefinition> beans;

    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
    private Set<String> creatingBeanNames;

    public AnnotationConfigApplicationContext(Class<?> configClass, PropertyResolver propertyResolver) {
        ApplicationContextUtils.setApplicationContext(this);

        this.propertyResolver = propertyResolver;

        // 扫描获取所有Bean的Class类型:
        final Set<String> beanClassNames = scanForClassNames(configClass);

        // 创建Bean的定义:
        this.beans = createBeanDefinitions(beanClassNames);

        // 创建BeanName检测循环依赖:
        this.creatingBeanNames = new HashSet<>();

        // 创建@Configuration类型的Bean:
        this.beans.values().stream()
                // 过滤出@Configuration:
                .filter(this::isConfigurationDefinition).sorted().map(def -> {
                    createBeanAsEarlySingleton(def);
                    return def.getName();
                }).collect(Collectors.toList());

        // 创建BeanPostProcessor类型的Bean:
        List<BeanPostProcessor> processors = this.beans.values().stream()
                // 过滤出BeanPostProcessor:
                .filter(this::isBeanPostProcessorDefinition)
                // 排序:
                .sorted()
                // instantiate and collect:
                .map(def -> {
                    return (BeanPostProcessor) createBeanAsEarlySingleton(def);
                }).collect(Collectors.toList());
        this.beanPostProcessors.addAll(processors);

        // 创建其他普通Bean:
        createNormalBeans();

        // 通过字段和set方法注入依赖:
        this.beans.values().forEach(def -> {
            injectBean(def);
        });

        // 调用init方法:
        this.beans.values().forEach(def -> {
            initBean(def);
        });

        if (logger.isDebugEnabled()) {
            this.beans.values().stream().sorted().forEach(def -> {
                logger.debug("bean initialized: {}", def);
            });
        }
    }

Bean的生命周期

summer-context很清晰的展示了一个Bean的生命周期: 实例化 ➔ 依赖注入 ➔ 初始化 ➔ 使用。

image

准备阶段

准备阶段的任务是扫描出所有的Bean,并构建BeanDefinition。首先通过scanForClassNames(configClass);扫描@ComponentScan("my.package")里的扫描包;如果没有,则扫描注解所在的包,找到ClassPath的/my/package目录下的所有.class文件,获取所有的class类型,保存在Set beanClassNames中。
通过Class.forName()将字节码加载到JVM中,使用反射机制判断是否是@component,如果是则创建BeanDefinition,再扫描@configuration注解,为@bean创建BeanDefinition。至此,所有的BeanDefinition创建完成。

Map<String, BeanDefinition> createBeanDefinitions(Set<String> classNameSet) {
    Map<String, BeanDefinition> defs = new HashMap<>();
    for (String className : classNameSet) {
        // 获取Class:
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new BeanCreationException(e);
        }
        if (clazz.isAnnotation() || clazz.isEnum() || clazz.isInterface() || clazz.isRecord()) {
            continue;
        }
        // 是否标注@Component?
        Component component = ClassUtils.findAnnotation(clazz, Component.class);
        if (component != null) {
            logger.atDebug().log("found component: {}", clazz.getName());
            int mod = clazz.getModifiers();
            if (Modifier.isAbstract(mod)) {
                throw new BeanDefinitionException("@Component class " + clazz.getName() + " must not be abstract.");
            }
            if (Modifier.isPrivate(mod)) {
                throw new BeanDefinitionException("@Component class " + clazz.getName() + " must not be private.");
            }

            String beanName = ClassUtils.getBeanName(clazz);
            var def = new BeanDefinition(beanName, clazz, getSuitableConstructor(clazz), getOrder(clazz), clazz.isAnnotationPresent(Primary.class),
                    // named init / destroy method:
                    null, null,
                    // init method:
                    ClassUtils.findAnnotationMethod(clazz, PostConstruct.class),
                    // destroy method:
                    ClassUtils.findAnnotationMethod(clazz, PreDestroy.class));
            addBeanDefinitions(defs, def);
            logger.atDebug().log("define bean: {}", def);

            Configuration configuration = ClassUtils.findAnnotation(clazz, Configuration.class);
            if (configuration != null) {
                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                    throw new BeanDefinitionException("@Configuration class '" + clazz.getName() + "' cannot be BeanPostProcessor.");
                }
                scanFactoryMethods(beanName, clazz, defs);
            }
        }
    }
    return defs;
}

Bean实例化

25-26行的代码块在根据BeanDefinition实例化Bean,代码里没有SinglelonMap,summer-spring把实例放在了BeanDefinition里。作者将注入分为了强依赖注入——构造注入、工厂注入和弱依赖注入——setter注入、字段注入,区分的依据就是Bean的创建与注入是否可以区分开。先实例化强依赖注入,再实例化普通的Bean。
JavaGuide中推荐使用构造注入,因为这样可以防止空指针,为成员变量赋初始值。

注入弱依赖

强依赖注入的注入不可以单独进行,必须在创建Bean的时候就注入,由@Value和@Autowired注解的注入则可以单独注入。下面的代码可以清楚的看到@Autowired是怎么实现注入的,如果是字段注入,则把字段field赋值为depends,如果是setter方法,则使用method.invoke()调用该方法,把depends当做参数传入进去。

// acc:当前正在注入的字段或者方法
void tryInjectProperties(BeanDefinition def, Class<?> clazz, Object bean, AccessibleObject acc) throws ReflectiveOperationException {
    Value value = acc.getAnnotation(Value.class);
    Autowired autowired = acc.getAnnotation(Autowired.class);
    // 反射获取acc是不是@value或者@Autowired
    if (value == null && autowired == null) {
        return;
    }

    Field field = null;
    Method method = null;
    if (acc instanceof Field f) {
        checkFieldOrMethod(f);
        f.setAccessible(true);
        field = f;
    }
    if (acc instanceof Method m) {
        checkFieldOrMethod(m);
        if (m.getParameters().length != 1) {
            throw new BeanDefinitionException(
                    String.format("Cannot inject a non-setter method %s for bean '%s': %s", m.getName(), def.getName(), def.getBeanClass().getName()));
        }
        m.setAccessible(true);
        method = m;
    }

    String accessibleName = field != null ? field.getName() : method.getName();
    Class<?> accessibleType = field != null ? field.getType() : method.getParameterTypes()[0];

    if (value != null && autowired != null) {
        throw new BeanCreationException(String.format("Cannot specify both @Autowired and @Value when inject %s.%s for bean '%s': %s",
                clazz.getSimpleName(), accessibleName, def.getName(), def.getBeanClass().getName()));
    }

    // @Value注入:
    if (value != null) {
        // 读取配置文件属性,赋值给value
        Object propValue = this.propertyResolver.getRequiredProperty(value.value(), accessibleType);
        if (field != null) {
            logger.atDebug().log("Field injection: {}.{} = {}", def.getBeanClass().getName(), accessibleName, propValue);
            field.set(bean, propValue);
        }
        if (method != null) {
            logger.atDebug().log("Method injection: {}.{} ({})", def.getBeanClass().getName(), accessibleName, propValue);
            method.invoke(bean, propValue);
        }
    }

    // @Autowired注入:
    if (autowired != null) {
        String name = autowired.name();
        boolean required = autowired.value();
        // 获取bean实例,调用setter方法注入
        Object depends = name.isEmpty() ? findBean(accessibleType) : findBean(name, accessibleType);
        if (required && depends == null) {
            throw new UnsatisfiedDependencyException(String.format("Dependency bean not found when inject %s.%s for bean '%s': %s", clazz.getSimpleName(),
                    accessibleName, def.getName(), def.getBeanClass().getName()));
        }
        if (depends != null) {
            if (field != null) {
                logger.atDebug().log("Field injection: {}.{} = {}", def.getBeanClass().getName(), accessibleName, depends);
                field.set(bean, depends);
            }
            if (method != null) {
                logger.atDebug().log("Mield injection: {}.{} ({})", def.getBeanClass().getName(), accessibleName, depends);
                method.invoke(bean, depends);
            }
        }
    }
}

Bean初始化

至此Bean完成了实例化,但并不意味着Bean完成了初始化,每一个Bean都需要经过InitializingBean、@PostConstruct、BeanPostProcessor做初始化和增强。这些是spring的钩子机制,@PostConstruct和InitializingBean 叫做钩子的初始化,因为这个方法之后就开始进行BeanPostProcessor.postProcessAfterInitialization()钩子增强。可以用于AOP 动态代理(事务、日志、权限等)、自动注入某些逻辑(如 @Autowired)、修改 bean 的属性或行为(如转换器)。

void initBean(BeanDefinition def) {
    // 获取Bean实例,或被代理的原始实例:
    final Object beanInstance = getProxiedInstance(def);

    // 调用init方法:
    callMethod(beanInstance, def.getInitMethod(), def.getInitMethodName());

    // 调用BeanPostProcessor.postProcessAfterInitialization():
    beanPostProcessors.forEach(beanPostProcessor -> {
        Object processedInstance = beanPostProcessor.postProcessAfterInitialization(def.getInstance(), def.getName());
        if (processedInstance != def.getInstance()) {
            logger.atDebug().log("BeanPostProcessor {} return different bean from {} to {}.", beanPostProcessor.getClass().getSimpleName(),
                    def.getInstance().getClass().getName(), processedInstance.getClass().getName());
            def.setInstance(processedInstance);
        }
    });
}

理论上到了这里,已经实现了IoC容器,BeanPostProcessor是一种特殊Bean,它的作用是根据条件替换某些Bean,常见的用法是将Bean替换为代理的Bean。

被替换的Bean去了哪里?
因为在钩子增强之前,Bean的注入依赖已经完成,所以原来的Bean可能还有引用关系,那么就会被保存起来。但是如果已经没有引用关系了,那么就有可能会被JVM回收。如果是代理替换,那么原来的Bean会保存在代理的target字段,仍然可以得到。

AOP代理后替换的Bean的注入依赖还在吗?
代理对象只是包装,不做依赖注入,原来的Bean的注入依赖仍然注入在原来的Bean里, 业务逻辑仍然是原来的Bean在做的。

@Bean
BeanPostProcessor createProxy() {
    return new BeanPostProcessor() {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            // 实现事务功能:
            if (bean instanceof UserService u) {
                return new UserServiceProxy(u);
            }
            return bean;
        }
    };
}
}

BeanDefinition

前面context一直在讲BeanDefinition,下面看看BeanDefinition到底记录了什么。

public class BeanDefinition {
    // 全局唯一的Bean Name:
    String name;

    // Bean的声明类型:
    Class<?> beanClass;

    // Bean的实例:
    Object instance = null;

    // 构造方法/null:
    Constructor<?> constructor;

    // 工厂方法名称/null:
    String factoryName;

    // 工厂方法/null:
    Method factoryMethod;

    // Bean的顺序:
    int order;

    // 是否标识@Primary:
    boolean primary;

    // init/destroy方法名称:
    String initMethodName;
    String destroyMethodName;

    // init/destroy方法:
    Method initMethod;
    Method destroyMethod;
}

其实就是用反射机制,把元数据包装了一下,保存在了一个对象里。

AOP

面向切面编程,十分的抽象,官方的解释是:

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

看到这里就理解了,其实本质就是代理,使用代理把业务无关的代码包装一下,之后的Bean全都用代理包装,用BeanProxy来实现业务,还能够DIY事务管理、日志管理等等。 AOP是在BeanPostProcessor.postProcessAfterInitialization()之后时包装代理的,所以Spring的AOP自动代理器实现了 BeanPostProcessor接口,重写了postProcessAfterInitialization()方法。

image
image

基础

JavaGuide里给出了一些AOP的术语:

image

下面通过一个例子,来看这些属于究竟是什么:

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void register(String username) {
        System.out.println("UserService: 正在注册用户 -> " + username);
    }

    public void login(String username) {
        System.out.println("UserService: 正在登录用户 -> " + username);
    }
}
package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

    // 切入点:匹配 com.example.demo.service 包下的所有 public 方法
    @Pointcut("execution(public * com.example.demo.service.*.*(..))")
    public void serviceMethods() {}

    // 通知:在方法调用之前执行
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("[Before] 方法:" + joinPoint.getSignature().getName() + " 被调用,参数:" + java.util.Arrays.toString(joinPoint.getArgs()));
    }

    // 通知:在方法调用后正常返回时执行
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("[AfterReturning] 方法:" + joinPoint.getSignature().getName() + " 执行完成");
    }

    // 通知:在方法抛出异常时执行
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        System.out.println("[AfterThrowing] 方法:" + joinPoint.getSignature().getName() + " 抛出异常:" + ex.getMessage());
    }
}

所属类的任何方法都可以当作是连接点,切入点就是一定范围内的所有连接点,@pointcut("")里的字符串就是决定选择哪些连接点的条件表达式,之后每一次遇到连接点,都会执行对应方法的通知。@Pointcut必须标注在一个方法上。@Before, @AfterReturning就叫做通知,围绕着方法调用的生命周期可以有不同的通知。一个切入点和通知叫切面,我们上面的代码就是一个切面,当然可以有很多个切面。

如何实现AOP

AOP的底层实现包括两种:JDK动态代理+InvocationHandler和CGLIB动态代理,Spring AOP会自动根据情况选择,有接口时优先使用 JDK 动态代理,无接口时使用CGLIB动态代理。两者的区别在于GCLIB直接继承目标类,重写所有 public/protected 方法,通过生成子类的方式创建代理对象,JDK动态代理则是通过代理接口实现。

public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

public class AroundInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 拦截标记了@Polite的方法返回值:
        if (method.getAnnotation(Polite.class) != null) {
            String ret = (String) method.invoke(proxy, args);
            if (ret.endsWith(".")) {
                ret = ret.substring(0, ret.length() - 1) + "!";
            }
            return ret;
        }
        return method.invoke(proxy, args);
    }
}

public abstract class BeforeInvocationHandlerAdapter implements InvocationHandler {

    public abstract void before(Object proxy, Method method, Object[] args);

    @Override
    public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(proxy, method, args);
        return method.invoke(proxy, args);
    }
}

public abstract class AfterInvocationHandlerAdapter implements InvocationHandler {
    // after允许修改方法返回值:
    public abstract Object after(Object proxy, Object returnValue, Method method, Object[] args);

    @Override
    public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = method.invoke(proxy, args);
        return after(proxy, ret, method, args);
    }
}

public class AroundProxyBeanPostProcessor implements BeanPostProcessor {
    Map<String, Object> originBeans = new HashMap<>();

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        // 检测@Around注解:
        Around anno = beanClass.getAnnotation(Around.class);
        if (anno != null) {
            String handlerName;
            try {
                // 这个handlerName就是切入点的方法名字
                handlerName = (String) anno.annotationType().getMethod("value").invoke(anno);
            } catch (ReflectiveOperationException e) {
                throw new AopConfigException();
            }
            Object proxy = createProxy(beanClass, bean, handlerName);
            originBeans.put(beanName, bean);
            return proxy;
        } else {
            return bean;
        }
    }

    Object createProxy(Class<?> beanClass, Object bean, String handlerName) {
        ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextUtils.getRequiredApplicationContext();
        // 获取切入点方法的BeanDEfinition
        BeanDefinition def = ctx.findBeanDefinition(handlerName);
        if (def == null) {
            throw new AopConfigException();
        }
        Object handlerBean = def.getInstance();
        if (handlerBean == null) {
            handlerBean = ctx.createBeanAsEarlySingleton(def);
        }
        if (handlerBean instanceof InvocationHandler handler) {
            return ProxyResolver.getInstance().createProxy(bean, handler);
        } else {
            throw new AopConfigException();
        }
    }

    @Override
    public Object postProcessOnSetProperty(Object bean, String beanName) {
        Object origin = this.originBeans.get(beanName);
        return origin != null ? origin : bean;
    }
}

// 使用
@Component
@Around("aroundInvocationHandler")
public class OriginBean {

    @Value("${customer.name}")
    public String name;

    @Polite
    public String hello() {
        return "Hello, " + name + ".";
    }

    public String morning() {
        return "Morning, " + name + ".";
    }
}
public class ProxyResolver {
    // ByteBuddy实例:
    ByteBuddy byteBuddy = new ByteBuddy();

    // 传入原始Bean、拦截器,返回代理后的实例:
    public <T> T createProxy(T bean, InvocationHandler handler) {
        // 目标Bean的Class类型:
        Class<?> targetClass = bean.getClass();
        // 动态创建Proxy的Class:
        Class<?> proxyClass = this.byteBuddy
                // 子类用默认无参数构造方法:
                .subclass(targetClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR)
                // 拦截所有public方法:
                .method(ElementMatchers.isPublic()).intercept(InvocationHandlerAdapter.of(
                        // 新的拦截器实例:
                        new InvocationHandler() {
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                // 将方法调用代理至原始Bean:
                                return handler.invoke(bean, method, args);
                            }
                        }))
                // 生成字节码:
                .make()
                // 加载字节码:
                .load(targetClass.getClassLoader()).getLoaded();
        // 创建Proxy实例:
        Object proxy;
        try {
            proxy = proxyClass.getConstructor().newInstance();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return (T) proxy;
    }
}

上面的代码是summer spring的实现,但是这个和Spring的实现差距很大,Spring是自动扫描,然后将所有的拦截器放在一个代理里执行的。上述代码想要实现@before需要先写一个BeforeInvocationHandlerAdapter实现InvocationHandler接口,然后在切入点添加@before("BeforeInvocationHandlerAdapter"),最后创建代理。创建代理时,使用自定义的@before注解的value找到对应的Bean实例handler,将handlerBean和targetBean一起构建代理, 所有通过代理调用的方法,都会经过 handlerBean,handlerBean 决定是否、何时、怎么去调用 targetBean 的方法。

资料

posted @ 2025-07-12 14:49  ZCry  阅读(44)  评论(0)    收藏  举报