Spring创建AOP代理并非只有@Aspect一种方式

开发 前端
ProxyFactoryBean与其他Spring FactoryBean实现一样,引入了一个间层。简单说如果你定义了一个名为foo的ProxyFactoryBean,那么引用foo的对象看不到ProxyFactoryBean实例本身,而是由ProxyFactoryBean中的getObject()方法实现创建的对象。

环境:Spring6.1.2


1. 简介

在Spring项目中,使用@Aspect注解定义切面(Aspect)并创建AOP(面向切面编程)代理是一种常见的做法,它主要用于实现跨多个类和方法的横切关注点(Cross-cutting Concerns)的模块化。下面是对使用@Aspect定义切面以及创建AOP代理的示例:

@Aspect
public class LogAspect {
  // 定义切点
  @Pointcut("execution(* com.pack..*.*(..))")
  public void log() {
  }
  // 前置通知
  @Before("log()")
  public void beforeLog() {
    System.out.println("记录日志Before...");
  }
  // 后置通知
  @After("log()")
  public void afterLog() {
    System.out.println("记录日志After");
  }
  // 异常通知
  @AfterThrowing(pointcut = "log()", throwing = "tx")
  public void ex(Throwable tx) {
    System.err.println("发生异常: " + tx.getMessage()) ;
  }
  // 环绕通知
  @Around("log() && args(name)")
  public Object around(ProceedingJoinPoint pjp, String name) throws Throwable {
    System.out.println("log before...") ;
    System.out.println("name = " + name) ;
    Object ret = pjp.proceed() ;
    System.out.println("log after...") ;
    return ret ;
  }
}

以上是一个简单的异常通知切面定义。在实际工作中绝大多数情况下都是通过上面的方式操作。

但是在某些场景下,你可能需要更细粒度的控制来创建代理对象,比如根据特定条件动态决定是否创建代理、自定义代理的创建过程或调整代理的行为。这时,使用ProxyFactoryBeanProxyFactory可以提供更大的灵活性。ProxyFactoryBean主要用于在Spring容器中配置和创建代理对象,而ProxyFactory则提供了编程式创建代理对象的能力。如果你需要在代码中动态地创建代理对象,而不是通过Spring容器来管理,那么使用ProxyFactory可能更合适。

接下来将详细介绍通过ProxyFactoryBean和ProxyFactory创建AOP代理对象。

2. 代理对象创建

2.1 ProxyFactoryBean创建代理

该类提供了对切入点、任何适用的建议及其顺序的完全控制。然而,如果您不需要这样的控制,也可以选择更简单的选项。

ProxyFactoryBean与其他Spring FactoryBean实现一样,引入了一个间层。简单说如果你定义了一个名为foo的ProxyFactoryBean,那么引用foo的对象看不到ProxyFactoryBean实例本身,而是由ProxyFactoryBean中的getObject()方法实现创建的对象。此方法创建一个AOP代理,用于包装目标对象。

ProxyFactoryBean很多关键的属性继承自ProxyConfig(Spring中所有aop代理工厂的超类)。这些关键属性结束如下:

ProxyFactoryBean proxy = new ProxyFactoryBean() ;
// 如果要代理的是目标类,而不是目标类的接口,则为True。如果该属性值设置为true,则创建CGLIB代理
proxy.setProxyTargetClass(false) ;
// 控制是否对通过CGLIB创建的代理应用积极优化。除非您完全理解相关AOP代理如何处理优化,否则不应该轻松地使用此设置。目前仅用于CGLIB代理。它对JDK动态代理没有影响。
proxy.setOptimize(false) ;
// 如果代理配置被冻结,则不再允许更改配置。无论是作为轻微的优化,还是当您不希望调用者在创建代理后能够操作代理(通过建议的接口)时,这都是有用的。此属性的默认值为false,因此允许更改(例如添加额外的通知)。
proxy.setFrozen(false) ;
// 确定是否应该在ThreadLocal中暴露当前代理,以便目标可以访问它。如果目标需要获取代理,而exposeProxy属性被设置为true,那么可以使用AopContext.currentProxy()方法。
proxy.setExposeProxy(false) ;
// 接口名称的字符串数组。如果没有提供,则使用目标类的CGLIB代理
proxy.setProxyInterfaces(new Class<?>[] {}) ;
// 要应用的Advisor、拦截器或其他Advice名称的字符串数组。点菜很重要,先到先得。也就是说,列表中的第一个拦截器是第一个能够拦截调用的。
proxy.setInterceptorNames("interceptor01") ;
// 不管getObject()方法被调用的频率如何,工厂是否应该返回一个对象。有几个FactoryBean实现提供了这样的方法。默认值为true
proxy.setSingleton(true) ;

以上是对ProxyFactoryBean创建代理对象时的核心配置说明。

完整使用案例如下:

public interface CommonDAO {}
  
public class PersonService {
  public void save() {
    System.out.println("save method invoke...") ;
  }
}
@Configuration
public class AppConfig {
  @Bean
  public MethodInterceptor logInterceptor() {
    return new MethodInterceptor() {
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("日志记录...") ;
        return invocation.proceed() ;
      }
    };
  }
  @Bean
  public ProxyFactoryBean personService() throws Exception {
    ProxyFactoryBean proxy = new ProxyFactoryBean() ;
    proxy.setProxyTargetClass(true) ;
    proxy.setTargetSource(new SingletonTargetSource(new PersonService())) ;
    proxy.setProxyInterfaces(new Class<?>[] {CommonDAO.class}) ;
    proxy.setInterceptorNames("logInterceptor") ;
    return proxy ;
  }
}

2.2 ProxyFactory创建代理

用Spring很容易通过编程创建AOP代理。这让你可以在不依赖Spring IoC的情况下使用Spring AOP。由目标对象实现的接口会自动被代理。如下示例:

public interface CommonDAO {}
public class PersonService {
  public void save() {
    System.out.println("save method invoke...") ;
  }
}


public static void main(String[] args) {
  ProxyFactory factory = new ProxyFactory(new PersonService()) ;
  factory.setProxyTargetClass(true) ;
  // 设置通知类(内部会自动的包装为Advisor)
  factory.addAdvice(new MethodInterceptor() {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("权限控制...") ;
      return invocation.proceed() ;
    }
  });
  factory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Advice getAdvice() {
      return new MethodInterceptor() {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
          System.out.println("日志记录...") ;
          return invocation.proceed() ;
        }
      } ;
    }
    @Override
    public Pointcut getPointcut() {
      return new StaticMethodMatcherPointcut() {
        @Override
        public boolean matches(Method method, Class<?> targetClass) {
          return method.getName().equals("save") ;
        }
      } ;
    }
  }) ;
  PersonService ps = (PersonService) factory.getProxy() ;
  ps.save() ;
}

以上是本篇文章的全部内容,希望对你有帮助。

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2023-09-27 08:01:14

数据推送事件

2013-05-22 15:31:07

AOP的CGlib实现

2023-07-27 08:14:29

2022-08-18 09:38:02

Spring跨域

2015-05-06 10:05:22

javajava框架spring aop

2023-08-02 10:48:47

SpringBean反射

2022-06-06 15:44:24

大数据数据分析思维模式

2022-02-14 10:30:37

Java方式框架

2019-08-20 14:13:12

工业物联网IIOT蓝牙

2009-12-25 16:27:30

MODEM接入方式

2023-01-26 23:46:15

2013-12-12 17:58:02

网络虚拟化叠加SDN

2009-02-26 10:29:00

2023-10-08 10:14:12

2023-03-07 15:08:57

2022-07-28 11:29:23

数据安全数据令牌化

2020-12-16 10:12:52

大数据小数据人工智能

2017-01-06 09:25:47

2012-02-01 10:18:23

编程

2018-07-09 14:52:05

点赞
收藏

51CTO技术栈公众号