空のポストプロセッサ


紹介する



空のポストプロセッサ-BeanPostProcessor
スプリングを空のリポジトリに登録する前に操作する場合は、空のポストプロセッサを使用します.

ポストプロセスタスク
  • Nullプロセッサは、渡されたスプリングNullオブジェクトを操作したり、他のオブジェクトに置き換えたりすることができます.

  • サンプルコード1


    まず、一般的なスプリングシートの登録手順を見てみましょう.
    package hello.proxy.postprocessor;
    
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    public class BasicTest {
    
        @Test
        void basicConfig() {
            //스프링 컨테이너
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BasicConfig.class);
    
            //A는 빈으로 등록된다.
            A a = applicationContext.getBean("beanA", A.class);
            a.helloA();
    
            //B는 빈으로 등록되지 않는다.
            Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(B.class));
        }
    
        @Slf4j
        @Configuration
        static class BasicConfig {
            @Bean(name = "beanA")
            public A a() {
                return new A();
            }
        }
    
        @Slf4j
        static class A {
            public void helloA() {
                log.info("hello A");
            }
        }
    
        @Slf4j
        static class B {
            public void helloB() {
                log.info("hello B");
            }
        }
    }
    

    サンプルコード2


    空のポストプロセッサを使用して、AオブジェクトをBオブジェクトに置き換えます.

    BeanPostProcessorインタフェース-スプリングの提供
  • Nullプロセッサを使用するには、BeanPostProcessorインタフェースを実装しspring binとして登録できます.
  • postProcessBeforeInitialization:オブジェクト作成後、@PostConstruct等初期化前に呼び出されるポストプロセッサ.
  • postProcessAfterInitialization:オブジェクトの作成後に発生する@PostConstructなど初期化して呼び出されるポストプロセッサ.
  • package hello.proxy.postprocessor;
    
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    public class BeanPostProcessorTest {
    
        @Test
        void basicConfig() {
            //스프링 컨테이너
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeenPostPostProcessorConfig.class);
    
            B b = applicationContext.getBean("beanA", B.class);
            b.helloB();
            
            Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(A.class));
        }
    
        @Slf4j
        @Configuration
        static class BeenPostPostProcessorConfig {
            @Bean(name = "beanA")
            public A a() {
                return new A();
            }
    
            @Bean
            public AToBPostProcessor helloPostProcessor() {
                return new AToBPostProcessor();
            }
        }
    
        @Slf4j
        static class A {
            public void helloA() {
                log.info("hello A");
            }
        }
    
        @Slf4j
        static class B {
            public void helloB() {
                log.info("hello B");
            }
        }
    
        @Slf4j
        static class AToBPostProcessor implements BeanPostProcessor {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                log.info("beanName={} bean={}", beanName, bean);
                if (bean instanceof A) {
                    return new B();
                }
                return bean;
            }
        }
    }
    
    AToBPostProcessor
  • 空のCPU.実装コネクタBeanPostProcessor
  • このポストプロセッサは、AオブジェクトをBオブジェクトに変換します.
  • 結果
    beanName=beanA bean=hello.proxy.postprocessor.BeanPostProcessorTest$A@906d29b
    整理する
    空のポストプロセッサは、空のポストプロセッサを操作および変更できるポイントです.
    空のオブジェクトを操作したり、他のオブジェクトに置き換えたりできるほど強力です.
    ここで、アクションは、オブジェクトを呼び出す特定のメソッドを意味します.
    通常、スプリングコンテナは、特に素子スキャンオブジェクトとして空であり、中間で操作することはできませんが、空の後プロセッサは、中間操作開発者が登録したすべての空を中間操作することができます.これは、空のオブジェクトをエージェントに置き換えることも可能であることを意味します.
    リファレンス
    Springは、CommonAnnotationBeanPostProcessorという空のポストプロセッサを自動的に登録し、ここでは@PostConstruct追加の説明の方法を呼び出す.従って、スプリング自体も空のポストプロセッサを用いてスプリング内部の機能を拡張する.

    適用



    PackageLogTracePostProcessor

    package hello.proxy.config.v4_postprocessor.postprocessor;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.aop.Advisor;
    import org.springframework.aop.framework.ProxyFactory;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    @Slf4j
    public class PackageLogTracePostProcessor implements BeanPostProcessor {
    
        private final String basePackage;
        private final Advisor advisor;
    
        public PackageLogTracePostProcessor(String basePackage, Advisor advisor) {
            this.basePackage = basePackage;
            this.advisor = advisor;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            log.info("param beanName={} bean={}", beanName, bean.getClass());
    
            //프록시 적용 대상 여부 체크
            //프록시 적용 대상이 아니면 원본을 그대로 진행
            String packageName = bean.getClass().getPackageName();
            if (!packageName.startsWith(basePackage)) {
                return bean;
            }
    
            //프록시 대상이면 프록시를 만들어서 반환
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            proxyFactory.addAdvisor(advisor);
    
            Object proxy = proxyFactory.getProxy();
            log.info("create proxy: target={} proxy={}", bean.getClass(), proxy.getClass());
            return proxy;
        }
    }
    

    BeanPostProcessorConfig

    package hello.proxy.config.v4_postprocessor;
    
    import hello.proxy.config.AppV1Config;
    import hello.proxy.config.AppV2Config;
    import hello.proxy.config.v3_proxyfactory.advice.LogTraceAdvice;
    import hello.proxy.config.v4_postprocessor.postprocessor.PackageLogTracePostProcessor;
    import hello.proxy.trace.logtrace.LogTrace;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.aop.Advisor;
    import org.springframework.aop.support.DefaultPointcutAdvisor;
    import org.springframework.aop.support.NameMatchMethodPointcut;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Slf4j
    @Configuration
    @Import({AppV1Config.class, AppV2Config.class})
    public class BeanPostProcessorConfig {
    
        @Bean
        public PackageLogTracePostProcessor logTracePostProcessor(LogTrace logTrace) {
            return new PackageLogTracePostProcessor("hello.proxy.app", getAdvisor(logTrace));
        }
    
        private Advisor getAdvisor(LogTrace logTrace) {
            //pointcut
            NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
            pointcut.setMappedNames("request*", "order*", "save*");
            //advice
            LogTraceAdvice advice = new LogTraceAdvice(logTrace);
            return new DefaultPointcutAdvisor(pointcut, advice);
        }
    }
    

    V 1,V 2,V 3ともに良好に動作していることがわかる.

    こんなにたくさんのスプリングシートが登録されているので
    使用basePackage特定の小包登録を個別にフィルタリングして条件を入れた方が良い.
    プロファイルでプロキシのコードを生成する必要がなくなりました.

    このようなインタフェースがあれば,JDK動的エージェント,特定のクラスのみの場合,CGIBエージェントが適用される.
    素子スキャンにも適用
    ここで重要な点は,v 1,v 2のように手動で登録された空だけでなく,コンポーネントスキャンで登録されたv 3空にもエージェントを適用できることである.
    エージェントが適用されているかどうかを確認
  • 自分たちが登録しているスプリングシートだけでなく、基本的に登録されている空席が空きポストプロセッサに引き継がれているものも多い.したがって、エージェントとして空に設定する基準が必要です.ここでは、簡単に使用するbasePackage特定のパケットを基準として、そのパケットとそのサブパケットの空の値をエージェントとする.
    これは後で学んで、pointcutで処理することができます
  • SpringBootに内蔵されている空きの中には、プロキシオブジェクトを作成できない空きもあります.すべてのオブジェクトをプロキシにすると、エラーが発生します.
  • 整理する


    以前。


    設定しすぎ
    いちいち注ぎにくいConfig部分.仕事を繰り返すべきだ.
    空のポストプロセッサを使用すると、これらの重複作業はなくなります.
    スイープエレメント
    構成部品スキャンでは、構成部品が自動的に空に登録されます.したがって、プロキシを注入することはできませんが、ポストプロセッサで空に登録する前に、プロキシスプリングシートを元のファイルではなく元のファイルに入れることができます.
    しかし、開発者の欲望には限りがない.
    スプリングは、プロキシを作成するために空のポストプロセッサを作成し、提供しました.
    ホリモリー
    重要
    エージェントに適用されるかどうかは、ここで簡単にパッケージベースに設定します.
    しかし、飾り写真を使うともっときれいになります.
    スプリングAOPは、ポイントカットを使用して代理オブジェクトに適用されるかどうかを確認します.
    したがって、アクセントカットは、以下の2つの場所で使用することができる.
    1.エージェントが適用されるかどうかを確認し、必要な場所でのみエージェントを適用します.(空のポストプロセッサ-自動エージェント作成)
    2.エージェント呼び出しのメソッドが適用されるかどうかを判断します.(エージェント内)