这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
前言
作为Java工程师,每天都在跟依赖注入打交道,Controller层调用Service层,如果不用@Autowired和@Resource,大部分人估计都不知道怎么写了,哈哈。
错误的方法
经常会有很多类依赖于一个或多个底层资源(underlying resources)。比如,一个拼写检查器依赖一本词典。有的人会把它实现成静态类或者单例,这都是错误的,因为它们假定只有一个词典会被使用。
正确的方法
行为被底层资源参数化的类不适合作为静态工具类或单例。
我们需要的是类具有支持多个实例的能力(在我们的例子中是 SpellChcker),并且在每个实例中使用客户端所需的资源(在我们例子中是 dictionary)。满足这一需求的简单方法是在创建新的实例时,将资源传递到构造器中。这是依赖注入(dependency injection)的一种形式:拼写检查器所依赖的字典在 SpellChecker 对象被创建时注入到其构造方法中。
// 依赖注入提供了灵活性和可测性
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}
实际工作中
实际工作中我们不可能在构造器中添加这么多的资源,比如前端一个下单的Controller可能需要订单模块,购物车模块,商品信息模块,商品库存模块,Redis模块,这么多的模块如果都添加到Controller的构造器里,这构造器都没法看了,Controller还不是最惨的,最惨的是Service,每次实例化都得写非常多的mapper到自己的构造器里,不敢想不敢想。。。。
所以我们需要Spring的出场,在它的帮助下我们可以很方便的通过@autowired和@Resource来将我们的模块自动引入到我们需要的地方。
Autowired
@Autowired是Spring的注解,位于org.springframework.beans.factory.annotation包中,具体的代码如下:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
注意它的Target,我们平常只用它去自动引入变量,实际上它还可以放到构造器,方法,参数和注解上!!!
Autowired只根据type进行注入,只要你对应的类被@Service,@Component、@Repository这些模块注解修饰着,并且你的 @ComponentScan扫描路径也配置正确的话一般都不会有问题,如果启动报错或者长时间无法初始化的时候就得配置log为debug级别,看看是不是引用的mapper中有问题导致初始化失败一直报错一直重试(这种会在debug级别,我们一般都是用info级别,出问题的时候控制台并不会打印出报错日志给我们)。不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰。
Resource
@Resource是Java自己的注解,是在javax.annotation这个包中。
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
@Resource有两个属性是比较重要的,分是name和type;Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。