Spring IOC的注解配置

(一)简述

上回说到,我们可以使用书写XML配置文件的方式来让spring产生我们需要的Bean对象,并放入容器中。但是,书写配置文件是一件非常繁琐的事情,而且一旦配置文件内容很多,阅读起来也是一件让人头疼的事,所以,我们急需一种部分或者完全摆脱配置文件的配置方式。

下面我们就来看看spring的注解配置方式。注解配置和用配置文件配置相比,只是配置的形式不一样,最终达到的目的是基本相同的。就好比坐飞机去北京和坐高铁去北京,虽然使用的交通工具不一样,但是都达到了去北京的目的。

(二)注解分类

用来配置的注解差不多可以这样分类:用于创建对象的、用于注入数据的、用于改变作用范围的、用于在生命周期中添加逻辑的。这些注解将取代XML文件中的一些配置内容,但是不能彻底摆脱XML文件(后面会将如何彻底摆脱XML文件)。

首先我们要在XML中引入context命名空间:

在这里插入图片描述

直接给出一个官方文档上的模板:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

</beans>

然后,我们要加上扫描器,这样spring就能找到带有用于创建对象的注解的类,然后才能将他们实例化并纳入spring IOC容器内:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
        
	<context:component-scan base-package="com.demo" />
</beans>

这里的base-package就是指所有需要加入容器的Bean所在的包。有了扫描器之后,我们就来看看如何使用这四类注解。

1. 用于创建对象

这类注解一共有四个,分别是:@Component、@Controller、@Service和@Repository,他们的作用相当于在XML配置文件中书写<bean />标签。

首先,我们来看@Component注解,他的功能就是将当前对象存入spring IOC容器中。@Component是一种通用名称,泛指任意可以通过Spring来管理的组件,@Controller, @Service, @Repository则是@Component的扩展,通常用来表示某种特定场合下的组件,并且Spring框架会根据这种应用场景做些定制,比如@Repository同时具备了自动化的异常转换。@Repository用来表示Dao层组件,@Service则用来表示Service层组件, @Controller则用来表示Controller层组件。

因此,在不考虑特殊的定制情况时,这四个注解完全一样,可以混用。但是一般我们还是根据组件的性质来选择。我们随便写一个看看:

package com.demo;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public void getUser(){
        System.out.println("getUser被调用了");
    }
}

这四个注解都有一个参数,用来指定Bean对象的名称,如果不写(我这里就没写),默认为类名且首字母改小写。这个注解相当于我们在XML中配置了:

<bean id="userDao" class="com.demo.UserDao" />

2. 用于注入数据

有了对象我们可能还要注入数据,这类注解常用的有:@Value、@Autowired、@Resource,这三个注解的功能相当于<bean />标签中的<property />标签,@Value负责注入基本类型,而另外两个负责注入对象类型(依赖注入)。

@Value没啥好讲的,直接在注解的属性里面写上要注入的值就行,另外两个可能会有点复杂。

@Autowired默认按照类型匹配的方式进行注入 ,可以用于成员变量、setter方法、构造器函数等,使用@Autowired注解须有且仅有一个与之类型匹配的Bean对象,若存在多个匹配的Bean时,会按照名称匹配,若始终找不到匹配的对象Spring容器将抛出异常(若不想抛出异常可给注解添加属性:required=false)。我们还可以通过@Qualifier注解指定注入Bean对象的名称,@Autowired和@Qualifier结合使用时,自动注入的策略就从按类型匹配转变成按名称匹配了。

在这里插入图片描述

在这里插入图片描述
使用@Resource注解以后,spring判断该注解name属性是否为空,如果为空,则会让当前属性名和spring IOC容器中的Bean对象的名称做匹配,如果匹配成功则赋值,如果匹配不成功,则会按照spring配置文件class类型进行匹配,如果还匹配不成功,则报错。如果有name属性,则会按照name属性的值和spring IOC容器中的Bean对象的名称进行匹配,匹配成功,则赋值,不成功则报错。

在这里插入图片描述

3. 用于改变作用范围

这一类注解只有一个,就是@Scope,相当于<bean />标签中的scope属性。

@Scope用来改变Bean对象的作用范围,属性可以填以下几个值,与XML配置中的一样:

  1. singleton :单例模式
  2. prototype :多例模式
  3. request :request域,需要在web环境
  4. session :session域,需要在web环境
  5. application: context域,需要在web环境
  6. globalsession 集群环境的session域,需要在web环境

4. 用于在生命周期中添加逻辑

这一类注解主要有两个,@PostConstruct和@PreDestroy,用在方法上,相当于<bean />标签中的init-method属性和destroy-method属性。

    @PostConstruct
    public void init() {
    	System.out.println("初始化方法......");
    }
    
    @PreDestroy
    public void destroy() {
    	System.out.println("销毁方法......");
    }

看了这么多注解,我们来总结一下,其实这里的每一个注解都对应了以前XML中的一种配置功能,所以只要对应着记忆还是很容易的。

类型注解对应XML元素
用于创建对象@Component、@Controller、@Service、@Repository<bean />标签
用于注入数据@Value、@Autowired、@Resource<bean />标签中的<property />标签
用于改变作用范围@Scope<bean />标签中的scope属性
用于在生命周期中添加逻辑@PostConstruct、@PreDestroy<bean />标签中的init-method属性和destroy-method属性

(三)纯注解配置

纯注解配置使得我们终于可以抛弃XML文件了,不过,我们需要书写一个配置类来代替他的功能。

我们来想想这个配置类中可能有哪些东西,首先,spring如何知道他是一个配置类?我们得给他添加一个配置类专用的注解,这个注解就是@Configuration,有了他之后这个类就会被当做一个配置类。

然后,我们还讲过我们以前在XML中配置过一个扫描器,这个在配置类中也变成了一个注解,他就是@ComponentScan,我们只要在里面填入我们需要spring扫描的包即可。注意,这边如果要填入多个包,我们需要写成数组形式。我们来看一个例子:

package com.demo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.demo")
public class Config {

}
package com.demo;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    public void getUser(){
        System.out.println("getUser被调用了");
    }
}

package com.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public void get(){
        userDao.getUser();
}

这个例子非常简洁,主要目的就是展示一下纯注解的配置模式。很显然,我们以前在XML中配置的东西一个都没有少。这也是印证了我之前说的一句话:XML配置和注解配置,只是形式不一样,最后达成的目的是基本一致的。什么?你问能不能把配置类也省了?居然有比我还懒的人?当然,是可以的,不过需要出门右拐去看看springboot,领略一下约定大于配置,体会一下一行代码都不写就能跑起来一个应用程序的潇洒。

不过,十分不推荐初学者直接去看springboot,原因很简单,如果不懂spring的原理,调试的时候你会怀疑人生的。我们学spring,除了学习框架怎么使用,更多的是学习一下什么叫Java顶级项目、什么称得上规范、什么是设计模式。只有能领略到这些,才算是真的把spring学到家了,到那时候自己手写一个spring框架也不是不可能了。

2020年5月13日

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐