学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。 关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
一、Spring基于IOC的注解配置
注意:在基于注解的配置中,我们还要多拷贝一个 aop 的 jar 包。
- 在上一篇博客中已经介绍了导入Maven依赖包,在这里aop的包已经存在了。
- 使用@Component 注解配置管理的资源 ,新建一个Maven工程或模块,新建com.xnch.dao包,创建IAccountDao接口及其实现方法,service层及实现方法
- IAccountDao.java
/**
* 账户的持久层接口
*/
public interface IAccountDao {
/**
* 模拟保存账户
*/
void saveAccount();
}
- IAccountDaoImpl.java
/**
* 账户的持久层实现类
*/
@Component("accountDao")
public class AccountDaoImpl implements IAccountDao {
public void saveAccount(){
System.out.println("保存了账户");
}
}
- AccountServiceImpl.java
@Component(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public AccountServiceImpl(){
System.out.println("对象创建了");
}
public void saveAccount(){
accountDao.saveAccount();
}
}
- 全局配置文件beans.xml,在这里和之前的xml有所不同,因为要配置支持注解支持,所以添加了xmlns:context依赖支持。
<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
http://www.springframework.org/schema/context/spring-context.xsd">
<!--告知spring创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间和约束-->
<context:component-scan base-package="com.xnch"/>
</beans>
二、常用注解
2.1、用于创建对象的
相当于配置文件中的:
<bean id="" class="">
2.1.1、 @Component
作用:
把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名(首字母小写)。
2.1.2、@Controller @Service @Repository
他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。 他们只不过是提供了更加明确的语义化。
- @Controller:一般用于表现层的注解。
- @Service:一般用于业务层的注解。
- @Repository:一般用于持久层的注解。
以上三个注解他们的作用和属性与Component是一模一样。如果注解中有且只有一个属性要赋值时,且名称是 value,value属性在赋值时可以不写。 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。
2.2、用于注入数据的
相当于配置文件中的:
<property name="" ref="">
<property name="" value="">
2.2.1、@Autowired
作用:
自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。
出现位置:
可以是变量上,也可以是方法上
在使用注解注入时,set方法就不是必须的了。
- 演示有多个类型匹配时报错的情况
在这里主要是写了两个IAccountDao的实现类,代码如下:
/**
* 账户的持久层实现类1
*/
@Repository
public class AccountDaoImpl implements IAccountDao {
public void saveAccount(){
System.out.println("保存了账户11111111111111111111111");
}
}
/**
* 账户的持久层实现类2
*/
@Repository
public class AccountDaoImpl2 implements IAccountDao{
public void saveAccount(){
System.out.println("保存了账户222222222222222222222222");
}
}
测试类:
/**
* 测试方法
* @param args
*/
public static void main(String[] args) {
//1.获取核心容器对象
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
as.saveAccount();
}
service层的代码不变,点击运行测试类,就会出现如下错误,类型的限定bean可用,需要单个匹配bean,但找到2个bean,就报错了。
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.xnch.dao.IAccountDao’ available: expected single matching bean but found 2: accountDaoImpl,accountDaoImpl2
解决方案: 在每一个AccountDao的实现类上的注解@Repository加上属性值,比如@Repository("accountDao1")
@Repository("accountDao2")
同时还需修改service层AccountServiceImpl类的AccountDao的属性。这显然过于麻烦,下面介绍更好用的方法。
2.2.2、@Qualifier
作用:
在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和 @Autowire 一起使用,否则报空指针异常;但是给方法参数注入时,可以独立使用。
属性:
value:指定 bean 的 id。
用法:
参考上面的问题,也可将accountDao属性上面加上这两个注解,也可以解决多个类型报错的问题。
2.2.3、@Resource
作用:
直接按照bean的id注入。它可以独立使用
属性:
name:用于指定bean的id。
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
另外,集合类型的注入只能通过XML来实现。
参考上面@Autowired的问题,也可将accountDao属性上面加上@Resource 注解,也可以解决多个类型报错的问题。
2.2.4、@Value
作用:
注入基本数据类型和 String 类型数据的
属性:
value:用于指定值 它可以使用spring中SpEL(也就是spring的el表达式) SpEL的写法:${表达式}
2.3、用于改变作用范围的
相当于xml配置中的:
<bean id="" class="" scope="">
2.3.1、@Scope
作用:
指定 bean 的作用范围。
属性:
value:指定范围的值。 取值:singleton prototype request session globalsession
2.4、和生命周期相关的
相当于xml配置中的:
<bean id="" class="" init-method="" destroy-method="" />
2.4.1、@PostConstruct
作用: 用于指定初始化方法。
2.4.4、@PreDestroy
作用: 用于指定销毁方法。
三、Spring新注解
写到此处,基于注解的 IoC 配置已经完成,但是我们可能都发现了一个问题:我们依然离不开 spring 的 xml 配置文件,那么能不能不写这个 bean.xml,所有配置都用注解来实现呢? 那么Spring的新注解就能解决这个问题。
我们发现,之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置: 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中。
<context:component-scan base-package="com.xnch"></context:component-scan>
如果它要也能用注解配置,那么我们就离脱离 xml 文件就又进了一步。 看到这里,请先看另一篇博客,以便对下面介绍新注解的更容易理解。https://blog.csdn.net/weixin_43800761/article/details/103323356
3.1.1、@Configuration
作用:
用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用 AnnotationApplicationContext(有@Configuration 注解的类.class)。
属性:
value:用于指定配置类的字节码
我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢? 请看下一个注解。
3.1.2、 @ComponentScan
作用:
用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的: <context:component-scan base-package="com.xnch"/>
是一样的。
属性:
basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
在这里我们已经配置好了要扫描的包,但是数据源和 JdbcTemplate 对象如何从配置文件中移除呢? 请看下一个注解。
3.1.3、 @Bean
作用:
该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
属性:
name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。 不写时,默认值为当前方法的名称。
当使用注解配置时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,查找方式与Autowired注解的作用是一样的。
3.1.3、@PropertySource
作用:
用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到 properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
属性:
value[]:用于指定 properties 文件位置。
如果是在类路径下,需要写上 classpath:
3.1.4、@Import
作用:
用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问 题。
属性:
value[]:用于指定其他配置类的字节码。
使用Import的注解后,有Import注解的类就是父配置类,而导入的就是子配置类