@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中