BeanPostProcessor說明及使用

BeanPostProcessor的用法

BeanPostProcessor也稱為Bean后置處理器,它是Spring中定義的接口凑保,在Spring容器的創(chuàng)建過程中(具體為Bean初始化前后)會回調(diào)BeanPostProcessor中定義的兩個方法。BeanPostProcessor的源碼如下:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;    
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

其中postProcessBeforeInitialization方法會在每一個bean對象的初始化方法調(diào)用之前回調(diào)犯助;postProcessAfterInitialization方法會在每個bean對象的初始化方法調(diào)用之后被回調(diào)。

提出的問題

在Spring開發(fā)過程中剂买,存在同一個接口有多個實現(xiàn)類的情況癌蓖,根據(jù)不同的應用場景瞎颗,通常在具體調(diào)用的地方來選擇不同的接口實現(xiàn)類疯特,雖然可以在抽象出一個工廠方法蜘腌,但是還是感覺不夠優(yōu)雅。如果通過注解的方式解決這個問題呢责循,Spring的BeanPostProcessor是個好的選擇。

具體實現(xiàn)

聲明接口
public interface HelloService{
    public void sayHello();
}

接口實現(xiàn)類1

@Service
public class HelloServiceImpl1 implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("你好我是HelloServiceImpl1");
    }
}

接口實現(xiàn)類2

@Service
public class HelloServiceImpl2 implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("你好我是HelloServiceImpl2");
    }
}

自定義注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RountingInjected {
    String value() default "helloServiceImpl1";
}

自定義的BeanPostProcessor類

@Component
public class HelloServiceInjectProcessor implements BeanPostProcessor {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetCls = bean.getClass();
        Field[] targetFld = targetCls.getDeclaredFields();
        for (Field field : targetFld) {
            //找到制定目標的注解類
            if (field.isAnnotationPresent(RountingInjected.class)) {
                if (!field.getType().isInterface()) {
                    throw new BeanCreationException("RoutingInjected field must be declared as an interface:" + field.getName()
                            + " @Class " + targetCls.getName());
                }
                try {
                    this.handleRoutingInjected(field, bean, field.getType());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    /**
     * @param field
     * @param bean
     * @param type
     * @throws IllegalAccessException
     */
    private void handleRoutingInjected(Field field, Object bean, Class type) throws IllegalAccessException {
        Map<String, Object> candidates = this.applicationContext.getBeansOfType(type);
        field.setAccessible(true);
        if (candidates.size() == 1) {
            field.set(bean, candidates.values().iterator().next());
        } else if (candidates.size() == 2) {
            String injectVal = field.getAnnotation(RountingInjected.class).value();
            Object proxy = RoutingBeanProxyFactory.createProxy(injectVal, type, candidates);
            field.set(bean, proxy);
        } else {
            throw new IllegalArgumentException("Find more than 2 beans for type: " + type);
        }
    }
}

代理實現(xiàn)類

public class RoutingBeanProxyFactory {

    private final static String DEFAULT_BEAN_NAME = "helloServiceImpl1";

    public static Object createProxy(String name, Class type, Map<String, Object> candidates) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setInterfaces(type);
        proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(name, candidates));
        return proxyFactory.getProxy();
    }

    static class VersionRoutingMethodInterceptor implements MethodInterceptor {
        private Object targetObject;

        public VersionRoutingMethodInterceptor(String name, Map<String, Object> beans) {
            this.targetObject = beans.get(name);
            if (this.targetObject == null) {
                this.targetObject = beans.get(DEFAULT_BEAN_NAME);
            }
        }

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            return invocation.getMethod().invoke(this.targetObject, invocation.getArguments());
        }
    }
}

SpringBoot入口類

@SpringBootApplication
@MapperScan("com.lx.mapper")
public class MlxcApplication {
    public static void main(final String[] args) {
        try (ConfigurableApplicationContext applicationContext = SpringApplication.run(MlxcApplication.class, args)) {
            HelloServiceTest helloService = applicationContext.getBean(HelloServiceTest.class);
            helloService.testSayHello();
        }
    }
}

總結(jié)
上述是整個解決方案的示例流程歹垫,其核心思想就是根據(jù)自定義注解攔截要注入的接口實現(xiàn)類,運用java反射和代理的知識點來進行有效的實現(xiàn)類注入吭敢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末暮芭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谴麦,更是在濱河造成了極大的恐慌,老刑警劉巖舷蟀,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異野宜,居然都是意外死亡,警方通過查閱死者的電腦和手機河胎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門虎敦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人其徙,你說我怎么就攤上這事》枚停” “怎么了闹获?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長龟虎。 經(jīng)常有香客問我,道長遣总,這世上最難降的妖魔是什么轨功? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任容达,我火速辦了婚禮,結(jié)果婚禮上花盐,老公的妹妹穿的比我還像新娘。我一直安慰自己柒昏,他們只是感情好熙揍,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般是尖。 火紅的嫁衣襯著肌膚如雪泥耀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天痰催,我揣著相機與錄音,去河邊找鬼逸吵。 笑死蜘醋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的压语。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扰才,長吁一口氣:“原來是場噩夢啊……” “哼厕怜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起粥航,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柄延,沒想到半個月后缀程,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搜吧,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡滤奈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年撩满,在試婚紗的時候發(fā)現(xiàn)自己被綠了绅你。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搞糕。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖窍仰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情针史,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布啄枕,位于F島的核電站族沃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脆淹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一漓糙、第九天 我趴在偏房一處隱蔽的房頂上張望烘嘱。 院中可真熱鬧,春花似錦蝇庭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鲫竞,卻和暖如春逼蒙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工陕截, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留批什,地道東北人农曲。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓乳规,卻偏偏與公主長得像,于是被迫代替她去往敵國和親暮的。 傳聞我的和親對象是個殘疾皇子淌实,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容