SpringBoot之自定義注解(基于BeanPostProcessor接口實現)

步驟
使用@interface 自定義注解
編寫注解處理類,實現BeanPostProcessor接口
原理
實現BeanPostProcessor接口的類即為Bean后置處理器,Spring加載機制會在所有Bean初始化的時候遍歷調用每個Bean后置處理器。
其順序為:Bean實例化-》依賴注入-》Bean后置處理器-》@PostConstruct

缺陷
只有在會實例化成Bean的類中使用,注解才會生效。(因為是使用了Bean后置處理器實現的嘛)

代碼示例
完整參考代碼github

自定義注解接口

import org.springframework.stereotype.Component;
import java.lang.annotation.*;

@Target({ElementType.FIELD}) //聲明應用在屬性上
@Retention(RetentionPolicy.RUNTIME) //運行期生效
@Documented //Java Doc
@Component
public @interface Boy {
    String value() default "";
}

注解處理類

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

@Component //注意:Bean后置處理器本身也是一個Bean
public class BoyAnnotationBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        /**
         * 利用Java反射機制注入屬性
         */
        Field[] declaredFields = bean.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Boy annotation = declaredField.getAnnotation(Boy.class);
            if (null == annotation) {
                continue;
            }
            declaredField.setAccessible(true);
            try {
                declaredField.set(bean, annotation.value());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        return o; //這里要返回o,不然啟動時會報錯
    }
}

注解使用類

import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;

@Service
public class HelloBoy {

    @Boy("小明")
    String name = "world";

    public void sayHello() {
        System.out.println("hello, " + name);
    }
}

測試類

import com.markey.annotations.Boy.HelloBoy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloBoyTest {

    @Autowired
    HelloBoy helloBoy;

    @Test
    public void HelloBoyTest() {
        helloBoy.sayHello();
    }

}

注解無效示例
測試類

import com.markey.annotations.Boy.HelloBoy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloBoyTest {

    HelloBoy helloBoy = new HelloBoy(); //新建的對象,不是容器中的Bean
    @Test
    public void HelloBoyTest() {
        helloBoy.sayHello();
    }
}

Tips
其實Spring很多原生注解(例如@Autowired,其處理類是AutowiredAnnotationBeanPostProcessor),也是通過實現BeanPostProcessor接口這種方式來實現的。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本來是準備看一看Spring源碼的。然后在知乎上看到來一個帖子,說有一群**自己連Spring官方文檔都沒有完全讀...
    此魚不得水閱讀 6,952評論 4 21
  • 參考W3C Spring教程 Spring致力于J2EE應用的各種解決方案,而不僅僅專注于某一層解決方案。可以說S...
    王偵閱讀 1,186評論 0 6
  • 源碼: # 前言 前兩篇文章我們分析了AOP的原理,知道了AOP的核心就是代理,通知器,切點。我們也知道了XML配...
    莫那一魯道閱讀 2,673評論 0 6
  • 注意LifecycleProcessor接口繼承了Lifcycle接口。同時,增加了2個方法,用于處理容器的ref...
    google666s閱讀 1,123評論 0 51
  • 柳條輕搖 我坐在十里岸邊 望著橋上路人 贈折柳送別離 我沒有折柳 也沒有你 驕陽似火 我行至山中小溪 看見溪邊兩人...
    爾之語閱讀 231評論 2 1