文章目录
Spring 中的常用注解
1. 常用 IOC 注解的分类
首先,看一个 Spring 的配置文件:
<bean id="" class="" scope="" init-method="" destroy-method="">
<property name="" value="" (ref="")></properties>
</bean>
与上面的标签相对应,IOC 注解按照功能分类可以分为一下几类:
- 用于创建对象的
- 它们的作用就和在 xml 配置文件中编写一个 bean 标签实现的功能是一样的
- 用于注入数据的
- 它们的作用就和在 xml 配置文件中的 bean 标签中的property、constructor-arg 标签的作用是一样的
- 用于改变作用范围的
- 它们的作用就和在 bean 标签中使用 scope 属性的功能是一样
- 和声明周期相关
- 它们的作用就和在 bean 标签中使用 init-method 和 destroy-method 是一样的。
2.用于创建的 Component 注解
2.1 Componet 注解的详解
Component:用于创建对象,它可以将创建被注解的类的对象,然后再将当前创建的对象存入到 Spring 容器中。
Component 有一个属性值:value。这个属性用来定义 bean 的 id。当然,当我们不写时,它的默认值是当前的类名,且开头为小写字母。
2.2 使用实例
-
首先,需要在想要用来创建 bean 的类的类名前,加上 @Component 标签:
package com.selflearning.spring.service.impl; import com.selflearning.spring.dao.IAccountDao; import com.selflearning.spring.dao.impl.AccountDaoImpl; import com.selflearning.spring.service.IAccountService; import org.springframework.stereotype.Component; @Component(value = "accountService") public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = null; @Override public void saveAccount() { accountDao.saveAccount(); } }
-
但是这样还是不能用的,我们来看一下主函数的调用:
import com.selflearning.spring.dao.IAccountDao; import com.selflearning.spring.service.IAccountService; import com.selflearning.spring.service.impl.AccountServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 模拟一个表现层,用于调用业务层 */ public class Client { /** * 获取 spring 的 ioc 核心容器,并根据 id 获取对象 * @param args */ public static void main(String[] args) { // 1. 获取核心容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); // 2. 根据 id 获取 bean 对象 IAccountService as = (IAccountService) ac.getBean("accountService"); System.out.println(as); } }
主函数这里,由于使用 ApplicationContext 类创建的 bean,默认情况下是 singleton (单例的),所以,当读取了 bean.xml 文件时,就已经创建好了对象。读取 xml 时,我们发现,我们的 xml 文件里面是空的(因为使用注解不需要在 xml 文件配置 bean 标签):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context <!-- 告知 Spring 在创建容器时需要扫描的包,配置所需要的标签不是在beans 的约束中,而是一个名为 context 明恒空间和约束中--> </beans>
主函数:
public static void main(String[] args) { // 1. 获取核心容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); // 2. 根据 id 获取 bean 对象 IAccountService as = (IAccountService) ac.getBean("accountService"); System.out.println(as); }
运行,然后报错:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'accountService' available at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:771) at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:294) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105) at com.selflearning.spring.ui.Client.main(Client.java:23)
NoSuchBeanDefinitionException 没有当前的 bean 的详细定义。
-
这个时候,由于配置文件中,并没有配置 bean 的信息,所以,我们需要提醒 Spring 框架进入到源包中扫描注解,就需要对配置文件进行一定的修改:
<?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"> <!-- 告知 Spring 在创建容器时需要扫描的包,配置所需要的标签不是在beans 的约束中,而是一个名为 context 明恒空间和约束中--> <context:component-scan base-package="com.selflearning"></context:component-scan> </beans>
这里,导入了 context 约束(记得要导入 spring-aop 的包),然后使用 context:component 标签,将源包作为 base-package 属性的属性,以提醒 Spring 框架去扫描源包内 Java 类的注解。
再次运行:
com.selflearning.spring.service.impl.AccountServiceImpl@6eceb130
成功。
3. Controller、Service、Repository
Controller、Service、Repository 这三个注解,它们的作用和属性与 Component 是一模一样的。为什么一样呢?可能是这三个家伙都相当于继承了 Component 接口吧。
它们三个是 Spring 框架作为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
所以,在以后的 web 项目中:
- Controller:一般用于表现层
- Service:一般用于业务层
- Repository:一般用于持久层
3.1 一个小小的例子
之前模拟了一个 web 项目,现在可以拿过来用用。
-
首先,在模拟的持久层的类名上,添加注解 Repository:
package com.selflearning.spring.dao.impl; import com.selflearning.spring.dao.IAccountDao; import org.springframework.stereotype.Repository; /** * 账户的持久层实现类 */ @Repository(value="accountDao") public class AccountDaoImpl implements IAccountDao { @Override public void saveAccount() { System.out.println("保存了账户"); } }
-
然后,在模拟的业务层类上,加上注解 Service:
package com.selflearning.spring.service.impl; import com.selflearning.spring.dao.IAccountDao; import com.selflearning.spring.service.IAccountService; import org.springframework.stereotype.Service; @Service(value = "accountService") public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = null; @Override public void saveAccount() { accountDao.saveAccount(); } }
-
在现实层进行调用:
package com.selflearning.spring.ui; import com.selflearning.spring.dao.IAccountDao; import com.selflearning.spring.dao.impl.AccountDaoImpl; import com.selflearning.spring.service.IAccountService; import com.selflearning.spring.service.impl.AccountServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 模拟一个表现层,用于调用业务层 */ public class Client { /** * 获取 spring 的 ioc 核心容器,并根据 id 获取对象 * @param args */ public static void main(String[] args) { // 1. 获取核心容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); // 2. 根据 id 获取 bean 对象 IAccountService as = (IAccountService) ac.getBean("accountService"); IAccountDao ad = (IAccountDao) ac.getBean("accountDao"); System.out.println(as); System.out.println(ad); } }
-
最后是结果:
com.selflearning.spring.service.impl.AccountServiceImpl@df27fae com.selflearning.spring.dao.impl.AccountDaoImpl@24a35978
4. 自动按照类型注入
自动按照类型注入,使用的标签是 Autowired
当 ioc 容器中有唯一一个 bean 对象类型和要注入的数据类型匹配,就可以实现注入。
4.1 唯一对象类型匹配的注入
我们来看一下的例子:
这里有两个类,一个是持久层的实现类,一个是业务层的实现类:
持久层:
package com.selflearning.spring.dao.impl;
import com.selflearning.spring.dao.IAccountDao;
import org.springframework.stereotype.Repository;
/**
* 账户的持久层实现类
*/
@Repository(value="accountDao")
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了账户");
}
}
业务层:
package com.selflearning.spring.service.impl;
import com.selflearning.spring.dao.IAccountDao;
import com.selflearning.spring.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao = null;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
在业务层中,我们创建了一个 IAccountDao 类型的成员变量。而持久层的实现类也实现了这个接口,那么持久层的实现类 AccountDaoImpl 就是一个 IAccountDao 类型的对象。而且,在 ioc 容器中,也只有一个和 IAccountDao 类型相匹配的 bean 对象(AccountDaoImpl, 它被注解 Repository 注解着,会被 Spring 框架创建成为一个 bean 对象存放在 Spring 的 ioc 核心容器中)。所以,可以注入成功。
然后,创建一个表现层的类,用来进行测试:
package com.selflearning.spring.ui;
import com.selflearning.spring.dao.IAccountDao;
import com.selflearning.spring.dao.impl.AccountDaoImpl;
import com.selflearning.spring.service.IAccountService;
import com.selflearning.spring.service.impl.AccountServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 模拟一个表现层,用于调用业务层
*/
public class Client {
/**
* 获取 spring 的 ioc 核心容器,并根据 id 获取对象
* @param args
*/
public static void main(String[] args) {
// 1. 获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// 2. 根据 id 获取 bean 对象
IAccountService as = (IAccountService) ac.getBean("accountService");
System.out.println(as);
as.saveAccount();
}
}
结果:
com.selflearning.spring.service.impl.AccountServiceImpl@4ae82894
保存了账户
4.2 多个对象类型匹配的注入
但是,如果 ioc 容器中两个 IAccountDao 类型的 bean 对象呢?
我们在之前持久层的基础上,在多增加一个持久层实现类 AccountDaoImpl2, 并将之前的 AccountDaoImpl 改名为 AccountDaoImpl1:
package com.selflearning.spring.dao.impl;
import com.selflearning.spring.dao.IAccountDao;
import org.springframework.stereotype.Repository;
/**
* 账户的持久层实现类
*/
@Repository(value="accountDao1")
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了账户");
}
}
package com.selflearning.spring.dao.impl;
import com.selflearning.spring.dao.IAccountDao;
import org.springframework.stereotype.Repository;
/**
* 账户的持久层实现类
*/
@Repository(value="accountDao2")
public class AccountDaoImpl2 implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了账户");
}
}
我们保持显示层不变:
package com.selflearning.spring.ui;
import com.selflearning.spring.dao.IAccountDao;
import com.selflearning.spring.dao.impl.AccountDaoImpl;
import com.selflearning.spring.service.IAccountService;
import com.selflearning.spring.service.impl.AccountServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 模拟一个表现层,用于调用业务层
*/
public class Client {
/**
* 获取 spring 的 ioc 核心容器,并根据 id 获取对象
* @param args
*/
public static void main(String[] args) {
// 1. 获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// 2. 根据 id 获取 bean 对象
IAccountService as = (IAccountService) ac.getBean("accountService");
IAccountDao ad = (IAccountDao) ac.getBean("accountDao");
System.out.println(as);
System.out.println(ad);
as.saveAccount();
}
}
运行:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.selflearning.spring.dao.IAccountDao' available: expected single matching bean but found 2: accountDao1,accountDao2
由于 ioc 存在两个 bean 对象的类型和注入变量的类型相同,此时,Spring 就会报出 “没有指定类型对象可获得的错误,预期是单个 bean 对象匹配,可是找到两个”。
那么,此时应该怎么办呢?
由于 Spring 的 ioc 容器是以 Map 形式存放 bean 对象的:
这的 key 就是 xml 中 bean 标签的 id 属性值,或者是注解中的 value 属性的属性值。而 value 存放的就是 bean 对象。
如果这里,我们在业务层的实现类中将需要注入的变量的名称进行修改,改成 accountDao1, 此时,便能通过运行:
package com.selflearning.spring.service.impl;
import com.selflearning.spring.dao.IAccountDao;
import com.selflearning.spring.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao1 = null;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
com.selflearning.spring.service.impl.AccountServiceImpl@44a3ec6b
保存了账户
这时因为当有两个或以上的 bean 对象的数据类型和需要注入的变量的数据类型相同时,Spring 便会通过上面表中的 key 的值来寻找相匹配的 bean 对象。
5. 用于注入数据的注解:
5.1 Qualifier
由于之前的 Autowired 自动注入对象类型时,我们只能通过让唯一 bean 数据类型和注入对象的数据类型相匹配或者修改需注入变量的变量名称的方法来注入实现对象。
这个时候,Spring 框架提供了一个名为 Qualifier 的注解。
这个注解可以在按照数据类型注入的基础上,再按照名称注入。它在给类成员注入时不能单独使用,但是在给方法注入时,可以单独使用。
Qualifier 有以下的属性:
value:用于指定注入 bean 的 id。
@Service(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
@Qualifier("accountDao1")
private IAccountDao accountDao;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
5.2 Resource
作用:直接按照 bean 的 id 注入。它可以单独使用
属性:
- name:用于指定 bean 的 id
@Service(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Resource(name = "accountDao1")
private IAccountDao accountDao;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
5.3 value
由于之前的三个注解只能注入其他 bean 类型的数据,而基本数据类型和 String 类型无法使用上述注解实现。
另外,集合类型的注入,只能通过 xml 注入。
value:
- 作用:
- 用于注入基本类型数据类型和 String 类型的数据
- 属性:
- 用于指定数据的值,它可以使用 Spring 中 spEL (也就是 Spring 中的 EL 表达式)
- spEL:${表达式}
5.4 改变作用范围以及和生命周期相关的注解
5.4.1 Scope - 改变作用范围
- 作用:
- 用于指定 bean 的作用范围
- 属性
- value:指定范围的取值。常用取值:singleton、prototype
5.4.2 生命周期相关的注解
-
PreDestroy:
- 作用:用于指定销毁方法
-
PostConstruct:
- 作用:用于指定初始化方法
-
一个小的例子:
-
需要创建 bean 对象的类:
@Service(value = "accountService") @Scope("singleton") public class AccountServiceImpl implements IAccountService { // @Autowired // @Qualifier("accountDao1") @Resource(name = "accountDao1") private IAccountDao accountDao; @Override public void saveAccount() { accountDao.saveAccount(); } @PostConstruct public void init() { System.out.println("该 bean 初始化了"); } @PreDestroy() public void destroy() { System.out.println("该 bean 被销毁"); } }
-
main方法:
/** * 获取 spring 的 ioc 核心容器,并根据 id 获取对象 * @param args */ public static void main(String[] args) { // 1. 获取核心容器对象 // ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); // 2. 根据 id 获取 bean 对象 IAccountService as = (IAccountService) ac.getBean("accountService"); // IAccountDao ad = (IAccountDao) ac.getBean("accountDao"); System.out.println(as); // System.out.println(ad); as.saveAccount(); }
-
执行结果:
该 bean 初始化了 com.selflearning.spring.service.impl.AccountServiceImpl@7e9a5fbe 保存了账户 该 bean 被销毁
-