✎前言
注解配置和 xml 配置 bean 对象的特点和实现的功能都是一样的,都是降低程序间的耦合,只是配置的形式不一样。
基于注解整合时,导入约束时需要多导入一个 context 名称空间下的约束。
1 |
|
✎常用注解
✎用于创建对象的
相当于:<bean id="" class=""></bean>
-
@Component
- 作用:用于把当前类对象存入 spring 容器中
- 属性:value,用于指定 bean 的 id。当我们不写时,它的默认值是当前类名,且首字母小写。如果注解中有且只有一个属性要赋值时,且名称是 value, value 在赋值时可以不写。
-
@Controller,@Service,@Repository
这三个注解都是 @Component 的衍生注解,作用和属性均与 @Component 一模一样。它们是 spring 框架为了提供了更加明确的语义化,使三层对象更加清晰。
- @Controller:一般用在表现层的注解;
- @Service:一般用在业务层的注解;
- Repository:一般用在持久层的注解;
✎用于注入数据的
相当于:<property name="" value=""></property>
或 <property name="" ref=""></property>
-
@Autowired
-
作用:自动按照类型注入。只要 IoC 容器中有唯一的一个 bean 对象类型和要注入的变量类型匹配,就可以注入成功;如果容器中没有任何 bean 的类型和要注入的变量类型匹配,则报错;当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在容器中查找,找到了也可以注入成功,找不到就报错。
-
细节:当使用注解注入属性时, set 方法可以省略。
-
-
@Qualifier
- 作用:在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和 @Autowired 一起使用;但是给方法参数注入时,可以独立使用。
- 属性:value,用于指定 bean 的 id。
-
@Resource
- 作用:直接按照 bean的 id注入,可以独立使用。
- 属性:name,用于指定 bean 的 id。
以上三个注解都只能注入其他bean类型的数据。
- @Value
- 作用:用于注入基本数据类型和 String 类型的数据。
- 属性:value,用于指定数据的值。它可以使用 spring 中 SpEL(也就是spring的el表达式),SpEL 的写法:${表达式}。
集合类型的注入只能通过XML来实现。
✎用于改变作用范围的
相当于:bean 标签中的 scope 属性
- @Scope
- 作用:指定bean的作用范围
- 属性:value,指定范围的值。取值:singleton,prototype,request,session,globalsession
✎和生命周期相关(了解)
相当于:bean 标签中的 init-method 和destroy-methode 属性
-
@PostConstruct:用于指定初始化方法
-
@PreDestroy:用于指定销毁方法
✎注解 or XML
注解:
- 优势:配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
- 适用场景:Bean 的实现类由用户自己开发。
XML:
- 优势:修改时,不用改源码。不涉及重新编译和部署。
- 适用场景:Bean 来自第三方。
✎纯注解配置
✎待改造问题
我们发现,之所以离不开 xml 配置文件,是因为有一句很关键的配置:
1 | <!-- 告知spring框架在创建容器时要扫描的包 --> |
另外,数据源和 JdbcTemplate 的配置也需要靠注解来实现。
1 | <!--配置QueryRunner--> |
✎新注解说明
✎@Configuration
- 作用:用于指定当前类是一个 spring 配置类, 当创建容器时会从该类上加载注解。 获取容器时需要使用
AnnotationApplicationContext(有@Configuration 注解的类.class)
。 - 属性:value,用于指定配置类的字节码,通常不写.
1 |
|
我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢?
✎@ComponentScan
相当于:<context:component-scan base-package="com.ourzh"/>
- 作用:用于指定 spring 在初始化容器时要扫描的包。
- 属性:basePackages/value,用于指定要扫描的包。
1 |
|
我们已经配置好了要扫描的包,但是数据源和 JdbcTemplate 对象如何从配置文件中移除呢?
✎@Bean
- 作用:该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
- 属性:name,给当前 @Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
- 细节:当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。查找的方式和 Autowired 注解是一样的
1 | /** |
我们已经把数据源和 JdbcTemplate 的配置从配置文件中移除了,此时可以删除 bean.xml 了。但是由于没有了配置文件,创建数据源的配置又都写死在类中了。如何把它们配置出来呢?
✎@PropertySource
- 作用:用于加载
.properties
文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到.properties
配置文件中,就可以使用此注解指定.properties
配置文件的位置。 - 属性:value[],用于指定
.properties
文件位置。如果是在类路径下,需要写上classpath:
。
jdbc.properties
文件:
1 | jdbc.driver=com.mysql.jdbc.Driver |
1 | /** |
此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?
✎@Import
- 作用:用于导入其他配置类。当我们使用 Import 的注解之后,有 Import 注解的类就父配置类,而导入的都是子配置类。
- 属性:value[],用于指定其他配置类的字节码。
1 |
|
我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?
✎通过注解获取容器
1 | ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); |
✎Spring 整合 Junit
✎问题
在测试类中,每个测试方法都有以下两行代码:
1 | ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); |
这样就造成代码重复。这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常,所以又不能轻易删掉。
✎解决思路分析
- 应用程序的入口:main 方法
- junit 单元测试中,没有 main 方法也能执行
- junit集成了一个main方法
- 该方法就会判断当前测试类中哪些方法有 @Test 注解
- junit 就让有 Test 注解的方法执行
- junit 不会管我们是否采用 spring 框架
- 在执行测试方法时,junit 根本不知道我们是不是使用了 spring 框架
- 所以也就不会为我们读取配置文件/配置类创建 spring 核心容器
- 由以上三点可知,当测试方法执行时,没有 Ioc 容器,就算写了 Autowired 注解,也无法实现注入
针对上述问题,我们需要的是程序能自动帮我们创建容器。但显然,junit 自身是无法实现的,因为它无法知晓我们是否使用了 spring 框架,更不用说帮我们创建 spring 容器了。不过好在,junit 给我们暴露了一个注解,可以让我们替换掉它的运行器。这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。
✎配置步骤
-
导入 Spring 整合 Junit 的 jar(坐标)
1
2
3
4
5<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency> -
使用
@RunWith
注解替换原有运行器 -
使用
@ContextConfiguration
指定 spring 配置文件的位置@ContextConfiguration
注解属性:- locations: 用于指定 xml 配置文件的位置。如果是类路径下,需要用
classpath:
表明 - classes: 用于指定注解的类。
- locations: 用于指定 xml 配置文件的位置。如果是类路径下,需要用
-
使用
@Autowired
给测试类中的变量注入数据1
2
3
4
5
6
7
8
9
10/**
* 测试类
*/
(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(locations={"classpath:bean.xml"})
(classes=SpringConfiguration.class)
public class AccountServiceTest {
private AccountService as ;
}
为什么不把测试类配到 xml 中?
当我们在 xml 中配置了一个 bean, spring 加载配置文件创建容器时,就会创建对象。但测试类只是我们在测试功能时使用,而在项目中它并不参与程序逻辑,也不会解决需求上的问题,所以创建完了,并没有使用。那么存在容器中就会造成资源的浪费。所以,我们不应该把测试类配置到 xml 文件中。