【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)(中)

简介: 【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)(中)

DefaultAdvisorAdapterRegistry / AdvisorAdapterRegistry


public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
  通知器适配器集合
  private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
  // 默认就支持这几种类型的适配器
  public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
  }
  将通知封装为通知器
  @Override
  public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    if (adviceObject instanceof Advisor) {
      return (Advisor) adviceObject;
    }
    if (!(adviceObject instanceof Advice)) {
      throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    // 如果是MethodInterceptor类型,就根本不用适配器。DefaultPointcutAdvisor是天生处理这种有连接点得通知器的
    if (advice instanceof MethodInterceptor) {
      // So well-known it doesn't even need an adapter.
      return new DefaultPointcutAdvisor(advice);
    }
    // 这一步很显然了,就是校验看看这个advice是否是我们支持的这些类型(系统默认给出3中,但是我们也可以自己往里添加注册的)
    for (AdvisorAdapter adapter : this.adapters) {
      // Check that it is supported.
      if (adapter.supportsAdvice(advice)) {
        // 如果是支持的,也是被包装成了一个通用类型的DefaultPointcutAdvisor
        return new DefaultPointcutAdvisor(advice);
      }
    }
    throw new UnknownAdviceTypeException(advice);
  }
  获得通知器的通知
  @Override
  public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    if (advice instanceof MethodInterceptor) {
      interceptors.add((MethodInterceptor) advice);
    }
    // 从所支持的适配器中拿到拦截器通知器
    for (AdvisorAdapter adapter : this.adapters) {
      if (adapter.supportsAdvice(advice)) {
        // 这一步需要注意:一定要把从适配器中拿到MethodInterceptor类型的通知器
        interceptors.add(adapter.getInterceptor(advisor));
      }
    }
    if (interceptors.isEmpty()) {
      throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
  }
  注册通知适配器
  @Override
  public void registerAdvisorAdapter(AdvisorAdapter adapter) {
    this.adapters.add(adapter);
  }
}


下面看看具体的生成代理的两个方法:getSingletonInstance和newPrototypeInstance

getSingletonInstance:


  private synchronized Object getSingletonInstance() {
    // 如果是单例的,现在这里持有这个缓存  创建国就不会再创建了
    if (this.singletonInstance == null) {
      // 根据设置的targetName,去工厂里拿到这个bean对象(普通Bean被包装成SingletonTargetSource)
      this.targetSource = freshTargetSource();
      // 这一步是如果你手动没有去设置需要被代理的接口,Spring还是会去帮你找看你有没有实现啥接口,然后全部给你代理上。可见Spring的容错性是很强的
      if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
        // Rely on AOP infrastructure to tell us what interfaces to proxy.
        Class<?> targetClass = getTargetClass();
        if (targetClass == null) {
          throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
        }
        setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
      }
      // Initialize the shared singleton instance.
      super.setFrozen(this.freezeProxy);
      // createAopProxy()方法就是父类ProxyCreatorSupport的方法,它的具体原理在推荐的博文里已经讲过了,这里就不鳌诉了
      // 其中JdkDynamicAopProxy和CglibAopProxy对getProxy()方法的实现,也请参考前面分析
      this.singletonInstance = getProxy(createAopProxy());
    }
    return this.singletonInstance;
  }


newPrototypeInstance:

多例的生成,基本和单例差不多。只是没有缓存了、然后每次创建得事后都是copy一份配置出来,创建一个代理实例~~~有兴趣的自个看哈~


ProxyFactoryBean脱离IoC容器使用


虽然它大多数都是结合IoC容器一起使用,但是它脱离容器依然是可议单独使用的(毕竟生成代理得核心功能在父类ProxyCreatorSupport上,和容器无关的)。比如如下:

public class Main {
    public static void main(String[] args) {
        String pointcutExpression = "execution( int com.fsx.maintest.Person.run() )";
        // =============================================================
        //因为我们要使用AspectJ,所以此处采用AspectJProxyFactory,当然你也可以使用和容器相关的ProxyFactoryBean
        ProxyFactoryBean factory = new ProxyFactoryBean();
        factory.setTarget(new Person());
        //AspectJProxyFactory factory = new AspectJProxyFactory(new Person());
        //声明一个aspectj切点,一张切面
        AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
        cut.setExpression(pointcutExpression); // 设置切点表达式
        // 声明一个通知(此处使用环绕通知 MethodInterceptor )
        Advice advice = (MethodInterceptor) invocation -> {
            System.out.println("============>放行前拦截...");
            Object obj = invocation.proceed();
            System.out.println("============>放行后拦截...");
            return obj;
        };
        //切面=切点+通知
        // 它还有个构造函数:DefaultPointcutAdvisor(Advice advice); 用的切面就是Pointcut.TRUE,所以如果你要指定切面,请使用自己指定的构造函数
        // Pointcut.TRUE:表示啥都返回true,也就是说这个切面作用于所有的方法上/所有的方法
        // addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`,且使用的是Pointcut.TRUE切面,因此需要注意这些区别
        Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
        factory.addAdvisor(advisor);
        //Person p = factory.getProxy();
        Person p = (Person) factory.getObject();
        // 执行方法
        p.run();
        p.run(10);
        p.say();
        p.sayHi("Jack");
        p.say("Tom", 666);
    }
}
class Person {
    public int run() {
        System.out.println("我在run...");
        return 0;
    }
    public void run(int i) {
        System.out.println("我在run...<" + i + ">");
    }
    public void say() {
        System.out.println("我在say...");
    }
    public void sayHi(String name) {
        System.out.println("Hi," + name + ",你好");
    }
    public int say(String name, int i) {
        System.out.println(name + "----" + i);
        return 0;
    }
}


输出:

============>放行前拦截...
我在run...
============>放行后拦截...
我在run...<10>
我在say...
Hi,Jack,你好
Tom----666


ProxyFactory


它和Spring容器没啥关系,可议直接创建代理来使用:


    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory(new HelloServiceImpl());
        // 添加两个Advise,一个匿名内部类表示
        proxyFactory.addAdvice((AfterReturningAdvice) (returnValue, method, args1, target) ->
                System.out.println("AfterReturningAdvice method=" + method.getName()));
        proxyFactory.addAdvice(new LogMethodBeforeAdvice());
        HelloService proxy = (HelloService) proxyFactory.getProxy();
        proxy.hello();
    }
输出:
this is LogMethodBeforeAdvice
this is my method~~
AfterReturningAdvice method=hello


很显然它代理的Bean都是new出来的,所以比如HelloServiceImpl就不能和Spring IoC很好的结合了,所以一般都是Spring内部去使用。


public class ProxyFactory extends ProxyCreatorSupport {
  // 它提供了丰富的构造函数~~~
  public ProxyFactory() {
  }
  public ProxyFactory(Object target) {
    setTarget(target);
    setInterfaces(ClassUtils.getAllInterfaces(target));
  }
  public ProxyFactory(Class<?>... proxyInterfaces) {
    setInterfaces(proxyInterfaces);
  }
  public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
    addInterface(proxyInterface);
    addAdvice(interceptor);
  }
  public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {
    addInterface(proxyInterface);
    setTargetSource(targetSource);
  }
  // 创建代理的语句:调用父类ProxyCreatorSupport#createAopProxy 此处就不用再解释了
  public Object getProxy() {
    return createAopProxy().getProxy();
  }
  public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
  }
  // 主义这是个静态方法,可以一步到位,代理指定的接口
  public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {
    return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();
  }
  public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {
    return (T) new ProxyFactory(proxyInterface, targetSource).getProxy();
  }
  // 注意:若调用此方法生成代理,就直接使用的是CGLIB的方式的
  public static Object getProxy(TargetSource targetSource) {
    if (targetSource.getTargetClass() == null) {
      throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");
    }
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTargetSource(targetSource);
    proxyFactory.setProxyTargetClass(true);
    return proxyFactory.getProxy();
  }
}
原理

由源代码可议看出,ProxyFactory的原理就是ProxyCreatorSupport#createAopProxy这个方法,其余的没啥了


相关文章
|
1天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
3天前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
27天前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
36 0
|
27天前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
35 0
|
27天前
|
Java Spring 供应链
Spring 框架事件发布与监听机制,如魔法风暴席卷软件世界,开启奇幻编程之旅!
【8月更文挑战第31天】《Spring框架中的事件发布与监听机制》介绍了Spring中如何利用事件发布与监听机制实现组件间的高效协作。这一机制像城市中的广播系统,事件发布者发送消息,监听器接收并响应。通过简单的示例代码,文章详细讲解了如何定义事件类、创建事件发布者与监听器,并确保组件间松散耦合,提升系统的可维护性和扩展性。掌握这一机制,如同拥有一把开启高效软件开发大门的钥匙。
39 0
|
1月前
|
缓存 安全 Java
Spring AOP 中两种代理类型的限制
【8月更文挑战第22天】
15 0
|
1月前
|
Java Spring
|
1月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
2月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
2月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
99 0