目录
Spring IoC(Inversion of Control,控制反转)容器通过注解提供了一种简洁且强大的方式来进行依赖注入和配置管理。注解开发使得 Spring 应用程序的配置更为简洁和直观,减少了 XML 配置的冗余和复杂性。注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
1、如何使用注解标记和扫描
通过注解实现IOC 主要分为两步
- 在类上配置IOC注解
- 告诉spring ioc容器我们在哪个类上面添加了ioc注解
Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。
@Component | 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。 |
@Repository | 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Service | 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Controller | 该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。对于Spring使用IOC容器管理这些组件(组件就是bean)来说没有区别,也就是语法层面没有区别。
接下来我们举个简单的例子学习下注解如何使用
首先准备一个普通的组件(包括xml配置文件、common普通组件、controller组件、service组件、dao组件)
xml配置文件
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.11.RELEASE</version>
</dependency>
<!-- junit5测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
</dependencies>
common组件
public class CommonComponent {
}
controller组件
public class XxxController {
}
service组件
public class XxxService {
}
dao组件
public class XxxDao {
}
我们如果想要上面的组件通过注解方式被ioc管理,只需要2步
第一步:在上面的组件类上加注解
common组件
@Component //<bean id="commonComponet" class="Commonponet.class">
public class CommonComponent {
}
controller组件
@Controller
public class XxxController {
}
service组件
@Service
public class XxxService {
}
dao组件
@Repository
public class XxxDao{
}
第二步:我们要在配置文件告诉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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置自动扫描的包,base-package=指定ioc容器去哪些包下找注解类 -->
<!-- 1.包要精准,提高性能!
2.会扫描指定的包和子包内容
3.多个包可以使用,分割!-->
<context:component-scan base-package="springxml01"/>
</beans>
第二种:指定排除组件扫描
<context:component-scan base-package="springxml01">
<!-- context:exclude-filter标签:指定排除规则 -->
<!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 -->
<!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
第三种:指定扫描组件
<!-- 仅扫描 = 关闭默认规则 + 追加规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<context:component-scan base-package="springxml01" use-default-filters="false">
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
最后我可以写个测试类去验证一下
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springxml01.XxxDao;
import springxml01.XxxService;
public class Springioctest1 {
@Test
public void test01(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("springxml1.xml");
XxxDao xxxDao = applicationContext.getBean(XxxDao.class);
System.out.println("xxxdao的bean为"+xxxDao);
XxxService xxxService = applicationContext.getBean(XxxService.class);
System.out.println("xxxService的bean为"+xxxService);
}
}
在我们使用 XML 方式管理 bean 的时候,每个 bean 都有一个唯一标识——id 属性的值,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。默认情况下类名首字母小写就是 bean 的 id。例如:SoldierController 类对应的 bean 的 id 就是 soldierController。我们也可以使用value属性指定
@Controller(value = "aaaa")
public class SoldierController {
}
2、如何使用注解配置作用域和周期方法
我们可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用!这两个方法我们称为生命周期方法!类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。
public class BeanOne {
//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
@PostConstruct //注解制指定初始化方法
public void init() {
// 初始化逻辑
}
}
public class BeanTwo {
@PreDestroy //注解指定销毁方法
public void cleanup() {
// 释放资源逻辑
}
}
bean的作用域:<bean 标签声明Bean,只是将Bean的信息配置给SpringIoC容器!在IoC容器中,这些<bean标签对应的信息转成Spring内部 BeanDefinition 对象,BeanDefinition 对象内,包含定义的信息(id,class,属性等等)这意味着,BeanDefinition与类概念一样,SpringIoC容器可以可以根据BeanDefinition对象反射创建多个Bean对象实例。具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定。
singleton:在 IOC 容器中,这个 bean 的对象始终为单实例
prototype:这个 bean 在 IOC 容器中有多个实例
配置作用域
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) //单例,默认值
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) //多例 二选一
public class BeanOne {
//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
@PostConstruct //注解制指定初始化方法
public void init() {
// 初始化逻辑
}
}
3、如何使用注解进行引用类型自动装配
在 Spring IoC 容器中,自动装配(Autowired)是指 Spring 自动解析和注入对象依赖的一种机制。通过注解,Spring 可以自动处理依赖注入,简化配置和编码工作。
举个例子
AController 需要 AService
AService 需要 ADao
同时在各个组件中声明要调用的方法
AController中声明方法
import org.springframework.stereotype.Controller;
@Controller(value = "aaa")
public class AController {
private AService aService;
public void getMessage() {
aService.getMessage();
}
}
AService中声明方法
@Service("bbb")
public class AService {
private ADao aDao;
public void getMessage() {
aDao.getMessage();
}
}
ADao中声明方法
@Repository
public class ADao {
public void getMessage() {
System.out.print("I am ADao");
}
}
以上参与自动装配的组件(需要装配、被装配)全部都必须在IoC容器中
要实现自动装配,在成员变量上直接标记@Autowired注解即可,不需要提供setXxx()方法。
给Controller装配Service
import org.springframework.stereotype.Controller;
@Controller(value = "aaa")
public class AController {
@Autowired
private AService aService;
public void getMessage() {
aService.getMessage();
}
}
给Service装配Dao
@Service("bbb")
public class AService {
@Autowired
private ADao aDao;
public void getMessage() {
aDao.getMessage();
}
}
@Autowired可以在三种位置实现自动装配
第一种:成员变量
@Service("bbb")
public class AService {
@Autowired
private ADao aDao;
public void getMessage() {
aDao.getMessage();
}
}
第二种:构造器
@Controller(value = "aaa")
public class AController {
private AService aService;
@Autowired
public Aontroller(AService aService) {
this.aService = aService;
}
……
第三种:setXxx()方法
@Controller(value = "aaa")
public class AController {
private AService aService;
@Autowired
public void setAService(AService aService) {
this.aService = aService;
}
……
@Autowired首先根据所需要的组件类型到 IOC 容器中查找
能够找到唯一的 bean:直接执行装配
如果完全找不到匹配这个类型的 bean:装配失败
如果所需类型匹配的 bean 不止一个,我们就可以用@Qualifier 注解,使用 @Qualifier 注解,根据 @Qualifier 注解中指定的名称作为 bean 的id进行匹配
@Controller(value = "aaa")
public class AController {
@Autowired
@Qualifier(value = "xxxxx")
// 根据面向接口编程思想,使用接口类型引入Service组件
private IAService aService;
佛系装配
给 @Autowired 注解设置 required = false 属性表示:能装就装,装不上就不装。但是实际开发时,基本上所有需要装配组件的地方都是必须装配的,用不上这个属性
@Resource注解
@Resource注解默认根据Bean名称装配,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型装配。
@Autowired注解默认根据类型装配,如果想根据名称装配,需要配合@Qualifier注解一起用
@Resource注解用在属性上、setter方法上
@Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上
@Controller
public class XxxController {
/**
* 1. 如果没有指定name,先根据属性名查找IoC中组件xxxService
* 2. 如果没有指定name,并且属性名没有对应的组件,会根据属性类型查找
* 3. 可以指定name名称查找! @Resource(name='test') == @Autowired + @Qualifier(value='test')
*/
@Resource
private XxxService xxxService;
//@Resource(name = "指定beanName")
//private XxxService xxxService;
public void show(){
System.out.println("XxxController.show");
xxxService.show();
}
}
4、如何使用注解对基本类型属性赋值
在 Spring 中,可以使用 @Value 注解来对基本类型属性进行赋值。@Value 注解支持从多种来源注入值,包括硬编码的字符串、Spring 配置文件(如 application.properties )中的值、系统环境变量等。
举个例子,我要从外部配置文件注入一个值
第一步:声明外部配置
application.properties
catalog.name=MovieCatalog
第二步:xml引入外部配置
<!-- 引入外部配置文件-->
<context:property-placeholder location="application.properties" />
第三步:value注解读取外部配置
package com.atguigu.components;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* projectName: com.atguigu.components
*
* description: 普通的组件
*/
@Component
public class CommonComponent {
/**
* 情况1: ${key} 取外部配置key对应的值!
* 情况2: ${key:defaultValue} 没有key,可以给与默认值
*/
@Value("${catalog:hahaha}")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}