@Autowired 引發的一系列思考

關於Java註解

註解定義

  1. 標記註解 - 沒有元素
@interface Marker {
}
  1. 單元素註解 - 只有一個元素
@interface Single {
    String value() default "name";
}
  1. 普通註解 - 除了上面兩種
@interface Normal {
    String id();
    String name();
}

@interface NormalWithValue {
    String id() default "id";
    String value();
}

註解使用

1. 標記註解 由 @Marker() 可以簡化為 @Marker
2. 按照約定,單元素註解的名稱定義為 value ,這樣 Single(value="123") 可以簡化為 @Single("123")
3. 對於普通註解,必須給出註解中名稱以及對應的值,當然有默認值的除外,如果普通註解含有 value 名稱,並且其他名稱都有默認值,也可以簡化為 @Normal("123")

實例如下:

@Marker
@Single("123")
@Normal(id = "123", name = "123")
@NormalWithValue("123")
class AnnotationMain {
}

Annotation Types
Annotations

Spring 中的註解

  1. 元註解(Meta-Annotations)
在另一個註解上聲明的註解,所以說任何一個註解都可以成為元註解

在下面這個例子中,@Target、@Retention、@Documented、@Indexed都是元註解
如果@Component又跑到別的註解頭上了,那它也是元註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
}
  1. 角色註解(Stereotype Annotations)
代表著一種固有的形象
比如@Component代表組件,@Repository代表DAO,@service代表服務等等
  1. 組合註解(Composed Annotations)
將一個或多個註解註解到一個註解上
在Spring中,註解上的任何一個註解都是可以被感知到的,就代表這個註解擁有了上述幾個註解的所有功能

在下面這個例子中,@EnableAutoConfiguration組合了@AutoConfigurationPackage和@Import
使用@EnableAutoConfiguration的類會被Spring認為@EnableAutoConfiguration和@AutoConfigurationPackage也是存在的
因此@EnableAutoConfiguration就具備了@AutoConfigurationPackage的功能以及@Import的功能
這個實現好像是通過快取實現的,具體也不太了解,有大佬知道的可以在評論里告知一下~

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

Spring-Annotation-Programming-Model
MergedAnnotation-API-internals

Spring中的 @Autowired,一個自動注入的註解

  1. 可以在構造函數中注入
@Service
@Getter
public class MyService1 {
    private MyDao1 myDao1;

    @Autowired
    public MyService1(MyDao1 myDao1) {
        this.myDao1 = myDao1;
    }
}
  1. 可以在方法中注入
Spring 會自動自動執行方法,並把myDao注入住到方法參數裡面

@Getter
public class MyService2 {
    private MyDao1 myDao1;
    private MyDao2 myDao2;

    @Autowired
    public void setMyDao1(MyDao1 myDao1) {
        this.myDao1 = myDao1;
    }

    @Autowired
    public void fun(MyDao2 myDao2) {
        this.myDao2 = myDao2;
    }
}
  1. 也可以在屬性上注入
@Getter
public class MyService3 {
    @Autowired
    private MyDao1 myDao1;
}
  1. 也可以混合注入
@Getter
public class MyService4 {
    @Autowired
    private MyDao1 myDao1;
    private MyDao2 myDao2;
    private MyDao3 myDao3;

    @Autowired
    public void fun(MyDao2 myDao2) {
        this.myDao2 = myDao2;
    }

    @Autowired
    public MyService4(MyDao3 myDao3) {
        this.myDao3 = myDao3;
    }
}
  1. 其他注意的地方
  • 使用@Autowired時,容器里必須要存在這個類型的實例的,如果沒有就會報錯,如果不是必須要注入此類,可以將required設置為false,默認為true
@Getter
public class MyService5 {
    private MyDao1 myDao1;

    @Autowired(required = false)
    public MyService5(MyDao1 myDao1) {
        this.myDao1 = myDao1;
    }
}
  • 如果在多個構造函數上使用@Autowired,則所有的@Autowired必須將required設置為false,將會選一個參數較多的構造函數進行注入
@Getter
public class MyService6 {
    private MyDao1 myDao1;
    private MyDao2 myDao2;

    @Autowired(required = false)
    public MyService6() {
    }

    @Autowired(required = false)
    public MyService6(MyDao1 myDao1) {
        this.myDao1 = myDao1;
    }

    @Autowired(required = false)
    public MyService6(MyDao2 myDao2) {
        this.myDao2 = myDao2;
    }
}
  • 如果只有一個構造函數,不用加@Autowired也會自動注入,下面的例子中的所有屬性將會注入
@Getter
public class MyService6 {
    private MyDao1 myDao1;
    private MyDao2 myDao2;

    public MyService6(MyDao1 myDao1, MyDao2 myDao2) {
        this.myDao1 = myDao1;
        this.myDao2 = myDao2;
    }
}

beans-autowired-annotation

使用Java編程獲取Spring bean

  • @Bean 用於標記一個bean 定義
  • @Configuration 裡面有很多 @Bean 標記的方法
public class MyDao {
}

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class MyService {
    private MyDao myDao;
}

@Configuration
public class AppConfig {
    
    @Bean
    public MyService myService(MyDao myDao) {
        return new MyService(myDao);
    }

    @Bean
    public MyDao myDao() {
        return new MyDao();
    }
}

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService myService = applicationContext.getBean(MyService.class);
        MyDao myDao = applicationContext.getBean(MyDao.class);
        System.out.println(myService.getMyDao() == myDao);
    }
}
輸出:
true
說明 myService不為null,myDao不為null,myDao已經被注入到myService中