【spring 】spring aop 代理之ProxyFactoryBean

1. ProxyFactoryBean样例

业务接口ISubject.java:

package com.test.springaop;

public interface ISubject {
    
    
    void request();
}

业务类 SubjectImpl.java:

package com.test.springaop;

public class SubjectImpl implements  ISubject{
    
    
    @Override
    public void request() {
    
    
        System.out.println("target request ..");
    }
}

通知(Advice),TargetBeforeAdvice.java:

package com.test.springaop;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class TargetBeforeAdvice implements MethodBeforeAdvice {
    
    
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
    
    
        System.out.println("target before");
    }
}

Main方法,MainClass.java:

package com.test.springaop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainClass {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        //getBean的入参是代理bean,但是获得结果确实一个ISubject类型
        ISubject subject = ((ISubject) context.getBean("proxyFactoryBean"));
        subject.request();
    }
}

spring配置aop.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces">
            <value>com.test.springaop.ISubject</value>
        </property>
        <property name="target">
            <bean class="com.test.springaop.SubjectImpl"/>
        </property>
        <property name="interceptorNames">
            <list>
                <value>advisor</value>
            </list>
        </property>
    </bean>

    <!--DefaultPointcutAdvisor 使用的切点为True单件,即匹配任何方法都为true,也就是为所有方法增强 -->
    <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice">
            <bean class="com.test.springaop.TargetBeforeAdvice"/>
        </property>
    </bean>

</beans>

执行结果:

target before
target request ..

1.1 ProxyFactoryBean的缺陷

有2个缺陷:

  • 如果我们只能指定单一的Bean的AOP, 如果多个Bean需要创建多个ProxyFactoryBean 。

    target属性只能填一个目标对象,不支持多个

  • 我们的拦截器的粒度只控制到了类级别,类中所有的方法都进行了拦截。
    假设ISubject接口还有很多方法:

    public interface ISubject {
          
          
        void request();
        void request2();
        void request3();
    }
    

    ProxyFactoryBean会作用在所有的方法上,粒度太粗了,比如我不想在request3()上生效的话,则不支持。

2. Spring Aop

2.1 Spring 通知类型

通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口。

Spring 通知按照在目标类方法的连接点位置,可以分为以下五种类型,下表 所示:

名称 说明
org.springframework.aop.MethodBeforeAdvice(前置通知) 在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
org.springframework.aop.AfterReturningAdvice(后置通知) 在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
org.aopalliance.intercept.MethodInterceptor(环绕通知) 在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。
org.springframework.aop.ThrowsAdvice(异常通知) 在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。
org.springframework.aop.IntroductionInterceptor(引介通知) 在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。

我们上面的例子用的是MethodBeforeAdvice

2.2 声明式 Spring AOP

Spring 创建一个 AOP 代理的基本方法是使用 org.springframework.aop.framework.ProxyFactoryBean,这个类对应的切入点和通知提供了完整的控制能力,并可以生成指定的内容。

ProxyFactoryBean 类中的常用可配置属性如下表 所示:

  • target 代理的目标对象
    注意:只能填一个目标对象,不支持多个,这也是ProxyFactoryBean用法的致命问题,如果多个Bean需要创建多个ProxyFactoryBean。

  • proxyInterfaces 代理要实现的接口,如果有多个接口,则可以使用以下格式赋值:

    <list>
        <value ></value>
        ...
    </list>
    
  • proxyTargetClass 是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理

  • interceptorNames 需要植入目标的 Advice,支持多个

  • singleton 返回的代理是否为单例,默认为 true(返回单实例)

  • optimize 当设置为 true 时,强制使用 CGLIB

3. ProxyFactoryBean

我们先来回顾一下Spring使用aop的过程:

  • 配置好ProxyFactoryBean,给ProxyFactoryBean设置一个beanId

  • 然后通过ac.getBean(beanId),就取得被ProxyFactoryBean代理的对象,不是ProxyFactoryBean自身

    ac.getBean(&beanId),才能取得ProxyFactoryBean对象

顾名思义ProxyFactoryBean,本质上是一个FactoryBean,因此上述原因得以解释。

FactoryBean的特性可以参见 《beanFactory与FactoryBean的区别》

3.1 原理

参见《Spring深度解析-12、AOP代理对象创建原理-ProxyFactoryBean》中<ProxyFactoryBean创建AopProxy源码解析>章节

猜你喜欢

转载自blog.csdn.net/m0_45406092/article/details/115202433
今日推荐