@Component、@Service、@Repository、@Controller的源碼有區(qū)別嗎
所以從源碼上來講,@Component、@Service、@Repository、@Controller沒有任何區(qū)別對嗎?
非常接近了!你說的基本對,但還有一點點關鍵的差異,主要體現在:
從源碼實現上看,它們本質上都是@Component的"派生注解",所以它們最終都會被Spring掃描成 Bean,沒有區(qū)別。
但——在一些具體功能實現上,Spring對這些注解確實會做一些"額外處理",這就引出了它們的區(qū)別。
? 首先看它們的源碼結構(全都繼承自@Component)
來看每個注解的源碼:
@Component
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Component { String value() default ""; }
@Service
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { String value() default ""; }
@Repository
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Repository { String value() default ""; }
@Controller
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { String value() default ""; }
? 所以從注解繼承角度,它們確實完全等價,都被認為是Spring的組件,都會被@ComponentScan掃描注冊為Bean。
? 但它們的"行為"真的完全一樣嗎?并不完全一樣!
下面列舉Spring對不同注解做的"特別處理":
?? 1. @Repository有異常翻譯機制
當你用@Repository標注DAO類時,Spring會做一件額外的事:
捕捉原始數據庫異常,并將其轉為Spring自己的異常類型(比如 DataAccessException)。
這叫做:
Repository exception translation(數據訪問異常翻譯)
源碼位置:
org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
這在使用MyBatis/JPA時特別有用,不然很多數據庫異常你得自己try-catch。
?? 2. @Controller會被Spring MVC專門處理
- 標識這個類是Web請求處理器。
- @RequestMapping、@GetMapping等注解的識別,是通過識別@Controller或@RestController的類來實現的。
它和普通 Bean不一樣,它還被交給了 Spring MVC DispatcherServlet 專門處理請求分發(fā)。
?? 3. @Service沒有特別的行為,但它是AOP建議的目標
雖然@Service本身沒有額外功能,但它通常被用于AOP的增強目標(比如事務處理、日志、監(jiān)控等)。
所以如果你用@Service標注類,寫個切面@Around("execution(* com.xxx.service..*(..))")更好匹配。
? 結論總結一下:
注解 | 是Bean嗎? | 有額外行為? | 使用目的 |
@Component | ? | ? | 通用組件注冊 |
@Service | ? | ??(無額外行為) | 語義上表示"業(yè)務服務層" |
@Repository | ? | ? 異常轉換機制 | DAO層組件、數據庫操作類 |
@Controller | ? | ? Spring MVC 控制器 | 控制請求、返回視圖或JSON響應 |
所以你說得沒錯:如果只是從Bean注冊的角度看,它們沒有本質區(qū)別。
但在Spring的運行時邏輯里,確實有一層"語義驅動的特殊處理",而這就是Spring精妙的地方:不僅統(tǒng)一處理Bean,還按職責分工做細節(jié)增強。