12 Spring IoC注解式开发
12.1 回顾注解¶
注解的存在主要是为了简化 XML 的配置。Spring 6 倡导全注解开发。
我们来回顾一下:
- 第一:注解怎么定义,注解中的属性怎么定义?
- 第二:注解怎么使用?
- 第三:通过反射机制怎么读取注解?
注解怎么定义,注解中的属性怎么定义?
```java title:Component.java package com.powernode.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Component { String value(); }
以上是自定义了一个注解:Component
**该注解上面修饰的注解包括:Target 注解和 Retention 注解,这两个注解被称为元注解。**
- Target 注解用来设置 Component 注解可以出现的位置,以上代表表示 Component 注解只能用在类和接口上。
- Retention 注解用来设置 Component 注解的保持性策略,以上代表 Component 注解可以被反射机制读取。
`String value()`; 是 Component 注解中的一个属性。该属性类型 String,属性名是 value。
注解怎么使用?
```java title:User.java
package com.powernode.bean;
import com.powernode.annotation.Component;
@Component(value = "userBean")
public class User {
}
用法简单,语法格式:@注解类型名(属性名=属性值, 属性名=属性值, 属性名=属性值......)
userBean 为什么使用双引号括起来,因为 value 属性是 String 类型,字符串。
另外如果属性名是 value,则在使用的时候可以省略属性名,例如: ```java title:User.java package com.powernode.bean;
import com.powernode.annotation.Component;
//@Component(value = "userBean") @Component("userBean") public class User { }
通过反射机制怎么读取注解?
接下来,我们来写一段程序,当 Bean 类上有 Component 注解时,则实例化 Bean 对象,如果没有,则不实例化对象。
我们准备两个 Bean,一个上面有注解,一个上面没有注解。
```java title:User.java
package com.powernode.bean;
import com.powernode.annotation.Component;
@Component("userBean")
public class User {
}
```java title:Vip.java package com.powernode.bean;
public class Vip { }
假设我们现在只知道包名:com. powernode. bean。至于这个包下有多少个 Bean 我们不知道。哪些 Bean 上有注解,哪些 Bean 上没有注解,这些我们都不知道,如何通过程序全自动化判断。
```java title:Test.java
package com.powernode.test;
import com.powernode.annotation.Component;
import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* @author 动力节点
* @version 1.0
* @className Test
* @since 1.0
*/
public class Test {
public static void main(String[] args) throws Exception {
// 存放Bean的Map。key存储beanId。value存储Bean。
Map beanMap = new HashMap<>();
String packageName = "com.powernode.bean";
String path = packageName.replaceAll("\\.", "/");
URL url = ClassLoader.getSystemClassLoader().getResource(path);
File file = new File(url.getPath());
File[] files = file.listFiles();
Arrays.stream(files).forEach(f -> {
String className = packageName + "." + f.getName().split("\\.")[0];
try {
Class clazz = Class.forName(className);
if (clazz.isAnnotationPresent(Component.class)) {
Component component = (Component) clazz.getAnnotation(Component.class);
String beanId = component.value();
Object bean = clazz.newInstance();
beanMap.put(beanId, bean);
}
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println(beanMap);
}
}
执行结果: ![[12.01-1.png]]
12.2 声明 Bean 的注解¶
负责声明 Bean 的注解,常见的包括四个:
- @Component
- @Controller
- @Service
- @Repository
源码如下: ```java title:Component.java package com.powernode.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Component { String value(); }
```java title:Controller.java
package org.springframework.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
```java title:Service.java package org.springframework.stereotype;
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { @AliasFor( annotation = Component.class ) String value() default ""; }
```java title:Repository.java
package org.springframework.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
通过源码可以看到,@Controller、@Service、@Repository 这三个注解都是 @Component 注解的别名。
也就是说:这四个注解的功能都一样。用哪个都可以。
只是为了增强程序的可读性,建议:
- 控制器类上使用:Controller
- service 类上使用:Service
- dao 类上使用:Repository
他们都是只有一个 value 属性。value 属性用来指定 bean 的 id,也就是 bean 的名字。 ![[12.02-1.png]]
12.3 Spring 注解的使用¶
如何使用以上的注解呢?
- 第一步:加入 aop 的依赖
- 第二步:在配置文件中添加 context 命名空间
- 第三步:在配置文件中指定扫描的包
- 第四步:在 Bean 类上使用注解
第一步:加入 aop 的依赖
我们可以看到当加入 spring-context 依赖之后,会关联加入 aop 的依赖。所以这一步不用做。
![[12.03-1.png]]
第二步:在配置文件中添加 context 命名空间
```xml title:spring.xml
第三步:在配置文件中指定要扫描的包
```xml title:spring.xml
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.powernode.spring6.bean"/>
</beans>
第四步:在 Bean 类上使用注解
```java title:User.java
package com.powernode.spring6.bean;
import org.springframework.stereotype.Component;
@Component(value = "userBean") public class User { }
编写测试程序:
```java title:AnnotationTest.java
package com.powernode.spring6.test;
import com.powernode.spring6.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationTest {
@Test
public void testBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println(userBean);
}
}
执行结果: ![[12.03-2.png]]
如果注解的属性名是 value,那么 value 是可以省略的。 ```java title:Vip.java package com.powernode.spring6.bean;
import org.springframework.stereotype.Component;
@Component("vipBean") public class Vip { }
```java title:AnnotationTest.java
package com.powernode.spring6.test;
import com.powernode.spring6.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationTest {
@Test
public void testBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
}
}
执行结果: ![[12.03-3.png]]
如果把 value 属性彻底去掉,spring 会被 Bean 自动取名吗?会的。 并且默认名字的规律是:Bean 类名首字母小写即可。 ```java title:BankDao.java package com.powernode.spring6.bean;
import org.springframework.stereotype.Component;
@Component public class BankDao { }
也就是说,这个 BankDao 的 bean 的名字为:bankDao
测试一下:
```java title:AnnotationTest.java
package com.powernode.spring6.test;
import com.powernode.spring6.bean.BankDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationTest {
@Test
public void testBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
BankDao bankDao = applicationContext.getBean("bankDao", BankDao.class);
System.out.println(bankDao);
}
}
执行结果: ![[12.03-4.png]]
我们将 Component 注解换成其它三个注解,看看是否可以用: ```java title:BankDao.java package com.powernode.spring6.bean;
import org.springframework.stereotype.Controller;
@Controller public class BankDao { }
执行结果:
![[12.03-5.png]]
剩下的两个注解大家可以测试一下。
如果是多个包怎么办?有两种解决方案:
- 第一种:**在配置文件中指定多个包,用逗号隔开。**
- 第二种:**指定多个包的共同父包。**
先来测试一下逗号(英文)的方式:
创建一个新的包:bean 2,定义一个 Bean 类。
```java title:Order.java
package com.powernode.spring6.bean2;
import org.springframework.stereotype.Service;
@Service
public class Order {
}
配置文件修改: ```xml title:spring.xml
测试程序:
```java title:AnnotationTest.java
package com.powernode.spring6.test;
import com.powernode.spring6.bean.BankDao;
import com.powernode.spring6.bean2.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationTest {
@Test
public void testBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
BankDao bankDao = applicationContext.getBean("bankDao", BankDao.class);
System.out.println(bankDao);
Order order = applicationContext.getBean("order", Order.class);
System.out.println(order);
}
}
执行结果: ![[12.03-6.png]]
我们再来看看,指定共同的父包行不行: ```xml title:spring.xml
执行测试程序:
![[12.03-7.png]]
---
## 12.4 选择性实例化 Bean
假设在某个包下有很多 Bean,有的 Bean 上标注了 Component,有的标注了 Controller,有的标注了 Service,有的标注了 Repository,现在由于某种特殊业务的需要,只允许其中所有的 Controller 参与 Bean 管理,其他的都不实例化。这应该怎么办呢?
```java title:com.powernode.spring6.bean3
package com.powernode.spring6.bean3;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@Component
public class A {
public A() {
System.out.println("A的无参数构造方法执行");
}
}
@Controller
class B {
public B() {
System.out.println("B的无参数构造方法执行");
}
}
@Service
class C {
public C() {
System.out.println("C的无参数构造方法执行");
}
}
@Repository
class D {
public D() {
System.out.println("D的无参数构造方法执行");
}
}
@Controller
class E {
public E() {
System.out.println("E的无参数构造方法执行");
}
}
@Controller
class F {
public F() {
System.out.println("F的无参数构造方法执行");
}
}
我只想实例化 bean 3 包下的 Controller。配置文件这样写:
```xml title:spring-choose.xml
<context:component-scan base-package="com.powernode.spring6.bean3" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
- `use-default-filters="true"` 表示:使用 spring 默认的规则,只要有 Component、Controller、Service、Repository 中的任意一个注解标注,则进行实例化。
- `use-default-filters="false"` 表示:不再 spring 默认实例化规则,即使有 Component、Controller、Service、Repository 这些注解标注,也不再实例化。
- `<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>` 表示只有 Controller 进行实例化。java title:ChooseTest
@Test
public void testChoose(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-choose.xml");
}
```
执行结果: ![[12.04-1.png]]
也可以将 use-default-filters 设置为 true(不写就是 true),并且采用 exclude-filter 方式排出哪些注解标注的 Bean 不参与实例化:
```xml title:spring-choose.xml
执行测试程序:
![[12.04-2.png]]
---
## 12.5 负责注入的注解
`@Component @Controller @Service @Repository` 这四个注解是用来声明 Bean 的,声明后这些 Bean 将被实例化。接下来我们看一下,如何给 Bean 的属性赋值。给 Bean 属性赋值需要用到这些注解:
- `@Value`
- `@Autowired`
- `@Qualifier`
- `@Resource`
### 12.5.1 @Value
当属性的类型是简单类型时,可以使用 `@Value` 注解进行注入。
```java title:com.powernode.spring6.bean4.User
package com.powernode.spring6.bean4;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
@Value(value = "zhangsan")
private String name;
@Value("20")
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
开启包扫描: ```xml title:spring-injection.xml
```java title:ValueTest
@Test
public void testValue(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-injection.xml");
Object user = applicationContext.getBean("user");
System.out.println(user);
}
执行结果: ![[12.05-1.png]]
通过以上代码可以发现,我们并没有给属性提供 setter 方法,但仍然可以完成属性赋值。
如果提供 setter 方法,并且在 setter 方法上添加@Value 注解,可以完成注入吗?尝试一下: ```Java title:User.java
package com.powernode.spring6.bean4;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
private String name;
private int age;
@Value("李四")
public void setName(String name) {
this.name = name;
}
@Value("30")
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
执行结果:
![[12.05-2.png]] ![[12.05-3.png]]
### 12.5.2 @Autowired 与 @Qualifier
`@Autowired` 注解可以用来注入非简单类型。被翻译为:自动连线的,或者自动装配。
单独使用 `@Autowired` 注解,默认根据类型装配【**默认是 byType**】
看一下它的源码:
```java title: @Autowired 注解源码
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
源码中有两处需要注意:
-
第一处:该注解可以标注在哪里?
- 构造方法上
- 方法上
- 形参上
- 属性上
- 注解上
-
第二处:该注解有一个
required属性,- 默认值是
true,表示在注入的时候要求被注入的 Bean 必须是存在的,如果不存在则报错。 - 如果
required属性设置为false,表示注入的 Bean 存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。
- 默认值是
我们先在属性上使用 @Autowired 注解:
```java title: UserDao 接口
package com.powernode.spring6.dao;
public interface UserDao { void insert(); }
```java title: UserDaoForMySQL 实现类
package com.powernode.spring6.dao;
import org.springframework.stereotype.Repository;
@Repository // 纳入bean管理
public class UserDaoForMySQL implements UserDao {
@Override
public void insert() {
System.out.println("正在向mysql数据库插入User数据");
}
}
```java title: UserService 服务类 package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
@Service // 纳入bean管理 public class UserService {
@Autowired // 在属性上注入
private UserDao userDao;
// 没有提供构造方法和setter方法。
public void save() {
userDao.insert();
}
}
xml title: spring-injection.xml 配置文件
```java title: 测试方法 @Test public void testAutowired() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-injection.xml"); UserService userService = applicationContext.getBean("userService", UserService.class); userService.save(); }
执行结果:
![[12.05-4.png]]
以上构造方法和 setter 方法都没有提供,经过测试,仍然可以注入成功。
接下来,再来测试一下 `@Autowired` 注解出现在 setter 方法上:
```java title: UserService 设置器方法
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.insert();
}
}
执行结果: ![[12.05-5.png]]
我们再来看看能不能出现在构造方法上: ```java title: UserService 构造方法 package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
@Service public class UserService {
private UserDao userDao;
@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.insert();
}
}
执行结果:
![[12.05-6.png]]
再来看看,这个注解能不能只标注在构造方法的形参上:java title: UserService 构造方法参数
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserDao userDao;
public UserService(@Autowired UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.insert();
}
}
```
执行结果: ![[12.05-7.png]]
还有更劲爆的,当有参数的构造方法只有一个时,@Autowired 注解可以省略。
```java title: UserService 构造方法省略
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao; import org.springframework.stereotype.Service;
@Service public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.insert();
}
}
执行结果:
![[12.05-8.png]]
当然,如果有多个构造方法,`@Autowired` 肯定是不能省略的。java title: UserService 多构造方法
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public UserService() {
}
public void save() {
userDao.insert();
}
}
```
执行结果: ![[12.05-9.png]]
到此为止,我们已经清楚 @Autowired 注解可以出现在哪些位置了。
@Autowired 注解默认是 byType 进行注入的,也就是说根据类型注入的,如果以上程序中,UserDao 接口还有另外一个实现类,会出现问题吗?
```java title: UserDaoForOracle 实现类
package com.powernode.spring6.dao;
import org.springframework.stereotype.Repository;
@Repository // 纳入bean管理 public class UserDaoForOracle implements UserDao { @Override public void insert() { System.out.println("正在向Oracle数据库插入User数据"); } }
当你写完这个新的实现类之后,此时 IDEA 工具已经提示错误信息了:
![[12.05-10.png]]
错误信息中说:不能装配,`UserDao` 这个 Bean 的数量大于 1。
怎么解决这个问题呢?当然要 byName,根据名称进行装配了。
`@Autowired` 注解和 `@Qualifier` 注解联合起来才可以根据名称进行装配,在 `@Qualifier` 注解中指定 Bean 名称。
```java title: UserDaoForOracle 实现类命名
package com.powernode.spring6.dao;
import org.springframework.stereotype.Repository;
@Repository // 这里没有给bean起名,默认名字是:userDaoForOracle
public class UserDaoForOracle implements UserDao {
@Override
public void insert() {
System.out.println("正在向Oracle数据库插入User数据");
}
}
```java title: UserService 使用 @Qualifier package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service;
@Service public class UserService {
private UserDao userDao;
@Autowired
@Qualifier("userDaoForOracle") // 这个是bean的名字。
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.insert();
}
}
``
执行结果:
![[12.05-11.png]]
总结:
-@Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter 方法上。
- 当带参数的构造方法只有一个,@Autowired注解可以省略。
-@Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier` 注解一起使用。
12.5.3 @Resource¶
@Resource 注解也可以完成非简单类型注入。那它和@Autowired 注解有什么区别?
- @Resource 注解是 JDK 扩展包中的,也就是说属于 JDK 的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250 标准中制定的注解类型。JSR 是 Java 规范提案。)
- @Autowired 注解是 Spring 框架自己的。
- @Resource 注解默认根据名称装配 byName,未指定 name 时,使用属性名作为 name。通过 name 找不到的话会自动启动通过类型 byType 装配。
- @Autowired 注解默认根据类型装配 byType,如果想根据名称装配,需要配合@Qualifier 注解一起用。
- @Resource 注解用在属性上、setter 方法上。
- @Autowired 注解用在属性上、setter 方法上、构造方法上、构造方法参数上。
@Resource 注解属于 JDK 扩展包,所以不在 JDK 当中,需要额外引入以下依赖:【如果是 JDK 8 的话不需要额外引入依赖。高于 JDK 11 或低于 JDK 8 需要引入以下依赖。】
XML title:如果你是Spring6+版本请使用这个依赖.xml
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
一定要注意:如果你用 Spring 6,要知道 Spring 6 不再支持 JavaEE,它支持的是 JakartaEE 9。(Oracle 把 JavaEE 贡献给 Apache 了,Apache 把 JavaEE 的名字改成 JakartaEE 了,大家之前所接触的所有的 javax.* 包名统一修改为 jakarta.* 包名了。)
```XML title:如果你是spring5-版本请使用这个依赖.xml
@Resource 注解的源码如下:
![[12.05-12.png]]
试一下:
```Java title:给这个UserDaoForOracle起名xyz.java
package com.powernode.spring6.dao;
import org.springframework.stereotype.Repository;
@Repository("xyz")
public class UserDaoForOracle implements UserDao {
@Override
public void insert() {
System.out.println("正在向Oracle数据库插入User数据");
}
}
```Java title:在UserService中使用Resource注解根据name注入.java package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service;
@Service public class UserService {
@Resource(name = "xyz")
private UserDao userDao;
public void save() {
userDao.insert();
}
}
执行测试程序:
![[12.05-13.png]]
我们把 UserDaoForOracle 的名字 xyz 修改为 userDao,让这个 Bean 的名字和 UserService 类中的 UserDao 属性名一致:Java title:UserDaoForOracle.java
package com.powernode.spring6.dao;
import org.springframework.stereotype.Repository;
@Repository("userDao")
public class UserDaoForOracle implements UserDao {
@Override
public void insert() {
System.out.println("正在向Oracle数据库插入User数据");
}
}
```
```Java title:UserService类中Resource注解并没有指定name.java package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service;
@Service public class UserService {
@Resource
private UserDao userDao;
public void save() {
userDao.insert();
}
}
执行测试程序:
![[12.05-14.png]]
通过测试得知,当@Resource 注解使用时没有指定 name 的时候,还是根据 name 进行查找,这个 name 是属性名。
接下来把 UserService 类中的属性名修改一下:Java title:UserService的属性名修改为userDao2.java
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Resource
private UserDao userDao2;
public void save() {
userDao2.insert();
}
}
```
执行结果: ![[12.05-15.png]]
根据异常信息得知:显然当通过 name 找不到的时候,自然会启动 byType 进行注入。 以上的错误是因为 UserDao 接口下有两个实现类导致的。所以根据类型注入就会报错。
我们再来看@Resource 注解使用在 setter 方法上可以吗? ```Java title:UserService添加setter方法并使用注解标注.java package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service;
@Service public class UserService {
private UserDao userDao;
@Resource
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.insert();
}
}
注意这个 setter 方法的方法名,setUserDao 去掉 set 之后,将首字母变小写 userDao,userDao 就是 name
执行结果:
![[12.05-16.png]]
当然,也可以指定 name:Java title:UserService.java
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserDao userDao;
@Resource(name = "userDaoForMySQL")
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.insert();
}
}
```
执行结果: ![[12.05-17.png]]
一句话总结@Resource 注解:默认 byName 注入,没有指定 name 时把属性名当做 name,根据 name 找不到时,才会 byType 注入。 byType 注入时,某种类型的 Bean 只能有一个。
12.6 全注解式开发¶
所谓的全注解开发就是不再使用 spring 配置文件了。写一个配置类来代替配置文件。
```java title:Spring6Configuration.java package com.powernode.spring6.config;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScans; import org.springframework.context.annotation.Configuration;
@Configuration @ComponentScan({"com.powernode.spring6.dao", "com.powernode.spring6.service"}) public class Spring6Configuration { }
编写测试程序:不再 `new ClassPathXmlApplicationContext ()` 对象了。
```java title:TestNoXml.java
@Test
public void testNoXml(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.save();
}
执行结果: ![[12.06-1.png]]