Spring AOP(二) — 底层组件

 Spring AOP 是通过动态代理的方式来实现,主要是通过Pointcut、Advice、Advisor及ProxyFactoryBean 等接口来创建代理对象。

在IoC容器中,Advice 是一个bean(这样可以在通知中使用其他的bean),而Pointcut虽然不是一个Bean,但是它所在的Aspect(切面)是一个bean。

推荐Spring AOP在IoC容器中通过@AspectJ注解的形式来使用。

1 Pointcut

Pointcut 接口定义了一个或一组连接点的集合,用于匹配符合的方法。可以被复用,通常与通知(Advice)一起使用。

该接口通常不是由开发者直接实现,而是通过Spring AOP提供的切点表达式来定义(例如,@Pointcut)。

图 Pointcut 接口 UML

1.1 MethodMatcher 与 ClassFilter

切点由这两部分组成。

ClassFilter: 限制切入点或引入到给定目标类集的筛选器。

MethodMatcher: 检查目标方法是否符合Advice的条件。

图 ClassFilter 接口 UML

图 MethodMatcher 接口 UML

MethodMatcher 可以用来匹配静态及动态(匹配特定调用参数)的方法。

如果isRuntime() 方法返回false,表示只匹配静态方法。那么将调用matches带两个参数的方法。

如果 isRuntime() 返回true 并且matches带两个参数的方法返回true,那么matches带三个参数的方法将会被调用。

1.2 静态与动态Pointcut

静态切点:用于匹配方法及目标类,不能匹配特定的参数。实际开发中,大都是静态切点。Spring只需对静态切点求值一次。

动态切点:匹配方法的静态信息及参数值信息。性能耗费比静态切点大得多。Spring需要每次对它进行求值,且不能缓存结果。代表类是ControlFlowPointcut。

1.2.1 AspectJExpressionPointcut

是用于表示基于AspectJ表达式的切点的类型。是Spring AOP对AspectJ切点表达式支持的一部分。允许你使用AspectJ风格的切点来定义切点。

图 AspectJExpressionPointcut UML

图 AspectJExpressionPointcut 实现Pointcut接口的方法

AspectJExpressionPointcut 通过表达式来生成对应的ClassFilter与MethodMatcher。

2 Advice

每一个Advice 在IoC容器中都是一个bean,一个Advice实例可以被所有的目标对象访问,也可以让不同的目标对象访问该Advice的不同实例。这种模式成为Per-class 及 Per-instance 模式。

Per-class: 是最常用的类型,只对方法及参数器作用。适合那些不需要依赖目标类状态或不需要引入新的状态的情况,即不是引入。

Per-instance: 适合引入、支持混合。该类通知可以为目标类添加新的状态。

2.1 Advice 的类型

Advice的类型有Before、After(After returning及After throwing)及Around。“引入”也是一个特殊的Advice。Spring 为这些类型提供了不同的接口。

2.1.1 Before Advice

图 MethodBeforeAdvice UML

AspectJMethodBeforeAdvice 是封装了AspectJ 的Before Advice的一个通知。

图 AspectJMethodBeforeAdvice UML

2.1.2 Around Advice

org.aopalliance.intercept.MethodInterceptor 接口常用来实现Around 通知。允许你在运行时拦截并修改方法调用。

图 MethodInterceptor UML

其实现类需实现invoke方法,并调用MethodInvocation的proceed方法来执行需要增强的方法。

class TracingInterceptor implements MethodInterceptor {

    Object invoke(MethodInvocation i) throws Throwable {

      System.out.println("method "+i.getMethod()+" is called on "+

                         i.getThis()+" with args "+i.getArguments());

      Object ret=i.proceed();

      System.out.println("method "+i.getMethod()+" returns "+ret);

      return ret;

    }

  }

2.1.3 Introduction Advice

“Introduction” 引入允许向目标对象动态地添加新的接口和方法,从而改变其类型。在创建代理对象时,代理对象会实现新的接口。

DeclareParentsAdvisor 用于实现@DeclareParents 注解的引入操作。

图 DeclareParentsAdvisor UML

typePatternClassFilter 用来过滤哪些目标类需要被引入。而advice则是用来实现引入的接口。getInterfaces则是返回需要被引入的接口。在其一个构造器中,传入给advice字段的类型IntroductionInterceptor。

图 IntroductionInterceptor UML

implementsInterface方法用于判断当前Advice是否实现了给定的接口。

3 ProxyFactoryBean

是Spring AOP中用于创建代理对象的一个工厂类。允许用户通过配置的方式来创建AOP代理。

@Component
public class CustomBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("------------------自定义before通知------------------");
        System.out.println("方法名:" + method.getName());
        System.out.println("参数:" + Arrays.asList(args));
        System.out.println("目标对象:" + target);

    }
}

@Service
public class UserService {
    public void showInfo() {
        System.out.println("userService");
    }

    public void showName(String name) {
        System.out.println("name:" + name);
    }
}

@Configuration
@ComponentScan(basePackages = {"aop2.factory"})
public class FactoryConfiguration {

    @Bean
    public ProxyFactoryBean userServiceProxy(UserService userService) {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(userService);
        proxyFactoryBean.setInterceptorNames("customBeforeAdvice");
        return proxyFactoryBean;
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(FactoryConfiguration.class);
        UserService proxyObj = (UserService) context.getBean("userServiceProxy");
        proxyObj.showInfo();
        proxyObj.showName("黄先生");
    }
}

可以设置ProxyFactoryBean的属性来控制生产代理对象的机制。

proxyTargetClass

如果为true,则强制使用CGLIB代理。

optimize

控制是否将主动优化应用通过CGLIB创建的代理。

forzen

一旦对象被初始化,其配置就不能被修改。

exposeProxy

为true时,原始代理对象会被注入到目标对象中。这样目标对象就可以通过某种机制访问到代理对象自身。

proxyInterfaces

指定代理对象应该实现的接口。

interceptorNames

指定应该应用到代理对象上的通知名称。

sigleon

用于指定代理是否时单例。

表 ProxyFactoryBean 的属性

3.1 ProxyFactory

如果不想在IoC容器中使用AOP,则可以使用ProxyFactory 来创建代理。

ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值