Spring高手之路-Spring AOP

发布时间:2023年12月26日

目录

什么是AOP

Spring AOP有如下概念

补充:

AOP是如何实现的

Spring AOP 是通过代理模式实现的。

Spring AOP默认使用标准的JDK动态代理进行AOP代理。


什么是AOP

AOP(Aspect-Oriented Programming),即面向切面编程,用人话说就是把公共的逻辑抽出来,让开发者可以更专注于业务逻辑开发。

和IOC一样,AOP也指的是一种思想。AOP思想是OOP(Object-Oriented Programming)的补充。OOP是面向类和对象的,但是AOP则是面向不同切面的。一个切面可以横跨多个类和对象去操作,极大的丰富了开发者的使用方式,提高了开发效率。

譬如,一个订单的创建,可能需要以下步骤:

  1. 权限校验
  2. 事务管理
  3. 创建订单
  4. 日志打印

如果使用AOP思想,我们就可以把这四步当成四个“切面”,让业务人员专注开发第三个切面(业务),其他三个切面则是基础的通用逻辑,统一交给AOP封装和管理。

Spring AOP有如下概念

对于通知类型来说:

补充:

AOP是如何实现的

从Bean的初始化流程中来讲,Spring的AOP会在bean实例的实例化已完成,进行初始化后置处理时创建代理对象,即下面代码中的applyBeanPostProcessorsAfterInitialization部分。

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

    //...
    //检查Aware
    invokeAwareMethods(beanName, bean);
    
	//调用BeanPostProcessor的前置处理方法
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    //调用InitializingBean的afterPropertiesSet方法或自定义的初始化方法及自定义init-method方法
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    //调用BeanPostProcessor的后置处理方法
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

applyBeanPostProcessorsAfterInitialization中会遍历所有BeanPostProcessor,然后

调用其postProcessAfterInitialization方法,而AOP代理对象的创建就是AbstractAutoProxyCreator这个类的postProcessAfterInitialization方法中

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

这里面最重要的就是wrapIfNecessary方法了:

/**
 * 如果需要,对bean进行包装。
 *
 * @param bean 要包装的目标对象
 * @param beanName bean的名称
 * @param cacheKey 缓存键
 * @return 包装后的对象,可能是原始对象或代理对象
 */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 如果beanName不为null且在目标源bean集合中,则直接返回原始对象
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }

    // 如果缓存键对应的值为Boolean.FALSE,则直接返回原始对象
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }

    // 如果bean的类型为基础设施类,或者应跳过该类型的代理,则将缓存键对应的值设置为Boolean.FALSE并返回原始对象
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 如果存在advice,为bean创建代理对象
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        // 将缓存键对应的值设置为Boolean.TRUE
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理对象
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // 将代理对象的类型与缓存键关联起来
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    // 如果没有advice,将缓存键对应的值设置为Boolean.FALSE并返回原始对象
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

createProxy的主要作用是根据给定的bean类、bean名称、特定拦截器和目标源,创建代理对象:

/**
 * 根据给定的bean类、bean名称、特定拦截器和目标源,创建代理对象。
 *
 * @param beanClass 要代理的目标对象的类
 * @param beanName bean的名称
 * @param specificInterceptors 特定的拦截器数组
 * @param targetSource 目标源
 * @return 创建的代理对象
 */
protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

    // 如果beanFactory是ConfigurableListableBeanFactory的实例,将目标类暴露给它
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    // 创建ProxyFactory实例,并从当前代理创建器复制配置
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    // 如果不强制使用CGLIB代理目标类,根据条件决定是否使用CGLIB代理
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        } else {
            // 根据bean类评估代理接口
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 构建advisor数组
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 将advisors添加到ProxyFactory中
    proxyFactory.addAdvisors(advisors);
    // 设置目标源
    proxyFactory.setTargetSource(targetSource);
    // 定制ProxyFactory
    customizeProxyFactory(proxyFactory);

    // 设置代理是否冻结
    proxyFactory.setFrozen(this.freezeProxy);
    // 如果advisors已经预过滤,则设置ProxyFactory为预过滤状态
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // 获取代理对象,并使用指定的类加载器
    return proxyFactory.getProxy(getProxyClassLoader());
}

Spring AOP 是通过代理模式实现的。

具体有两种实现方式,一种是基于Java原生的动态代理,一种是基于cglib的动态代理。对应到代码中就是,这里面的Proxy有两种实现,分别是CglibAopProxy和JdkDynamicAopProxy。

Spring AOP默认使用标准的JDK动态代理进行AOP代理

这使得任何接口可以被代理。但是JDK动态代理有一个缺点,就是它不能代理没有接口的类。

所以Spring AOP就使用CGLIB代理没有接口的类。

文章来源:https://blog.csdn.net/qq_62262918/article/details/135197938
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。