目录
1.Spring的存储对象------存储Bean对象
1.前置工作,配置扫描路径
2.添加注解存储Bean对象
1.@Controller(控制器存储)
2.@service(服务存储)
3.@Repository(仓库存储)
4.@Component(组件存储)
5.@Configuration(配置存储)
3.为什么这么多类注解
1.类注解之间的关系
4.方法注解@Bean
1.方法注解要搭配类注解使用
2.重命名Bean
2.Spring的读取------获取Bean对象(对象装配)
1.属性注入
2.构造方法注入
3.Setter注入
4.三种注入方法的优缺点
5.@Resource:另一种注入关键字
@Resource和@Autowired的区别
6.同一类型多个@Bean报错
1.现象
2.报错处理
使⽤ @Resource(name="xxx")
使⽤ @Qualifier
在上一篇文章中实现了基本的Spring读取和存储对象。但在操作的过程中我们发现读取和存储对象并没有想象中的那么“简单”。
所以接下来我们要讲解更加简单的操作 Bean 对象的⽅法。其核心就是,使用注解。
1.Spring的存储对象------存储Bean对象
之前我们存储 Bean 时,需要在 spring-config 中添加⼀⾏ bean 注册内容才⾏,如下图所示:
⽽现在我们只需要⼀个注解就可以替代之前要写⼀⾏配置的尴尬了。
类注解规则:1.类名前两位均为大写时,返回自身name
2.否则,首字母小写
1.前置工作,配置扫描路径
想要将对象成功的存储到 Spring 中,我们需要配置⼀下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中。
在spring-config.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:content="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"><content:component-scan base-package="org.example"></content:component-scan>
</beans>
其中标红的一行为注册扫描的包,如下图所示:
注意:即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到Spring中的。
2.添加注解存储Bean对象
想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
2. ⽅法注解:@Bean。
1.@Controller(控制器存储)
使用@Controller存储Bean的代码如下所示:
@Controller
public class UserController {public void sayHi(){System.out.println("hi,userController~");}
}
在App中读取上面的UserController对象:
public class App {public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");UserController userController=(UserController) context.getBean("userController");userController.sayHi();}
}
运行结果:
这里并没有在spring-config.xml中添加id,但是依旧能正确运行。是因为@Controller注解自动的起到了一个id的作用。作用与如下代码一致:
<bean id="userController" class="org.example.UserController"></bean>
2.@service(服务存储)
使用@service存储Bean的代码如下所示:
@Service
public class UserService {public void doService(){System.out.println("doService");}
}
在App中读取上面的UserService对象:
public class App {public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");UserService userService=(UserService) context.getBean("userService");userService.doService();}
}
3.@Repository(仓库存储)
使用@Repository存储Bean的代码如下所示:
@Repository
public class UserRepository {public void sayHi(){System.out.println("hi,repository");}
}
在App中读取上面的UserRepository对象:
public class App {public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");UserRepository userRepository=(UserRepository) context.getBean("userRepository");userRepository.sayHi();}
}
4.@Component(组件存储)
使用@Component存储Bean的代码如下所示:
@Component
public class UserComponent {public void sayHi(){System.out.println("hi,component");}
}
在App中读取上面的UserComponent对象:
public class App {public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");UserComponent userComponent=(UserComponent) context.getBean("userComponent");userComponent.sayHi();}
}
5.@Configuration(配置存储)
使用@Configuration存储Bean的代码如下所示:
@Configuration
public class UserConfiguration {public void doConfiguration(){System.out.println("hi,Configuration");}
}
在App中读取上面的UserConfiguration对象:
public class App {public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");UserConfiguration userConfiguration=(UserConfiguration) context.getBean("userConfiguration");userConfiguration.doConfiguration();}
}
3.为什么这么多类注解
由以上对每个注解的介绍,可以看出,这些注解的功能都是一样的,那么为什么需要这么多类注解呢?
其目的就是让程序员看到类注解之后,就能直接了解当前类的用途。
@Controller:表示业务逻辑层,通常是指程序的入口,比如:参数校验,参数类型转换等前置处理工作。
@Service:服务层,一般写业务代码,服务编排,比如:调用DB,调用第三方接口。
@Repository:持久层,仓库,通常指DB操作相关的代码。(通常放在DAO层下面)
@Component:其它的对象。
@Configuration:配置层。
程序的工程分层,调用流程如下:
1.类注解之间的关系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:
其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“子类”。
4.方法注解@Bean
1.方法注解要搭配类注解使用
在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示:
@Configuration
public class BeanConfig {@Beanpublic User user() {User user=new User();user.setName("温温");user.setAge(18);return user;}
}
注意:@Bean对应生产的bean名称是方法名
2.重命名Bean
可以通过设置name属性给Bean对象进行重命名操作,如下对象所示:
@Configuration
public class BeanConfig {@Bean(name={"wen","user"})public User user() {User user=new User();user.setName("温温");user.setAge(18);return user;}
}
此时使用wen和user都可以获取到User对象了:
User user=(User) context.getBean("wen");System.out.println(user.getName());
2.Spring的读取------获取Bean对象(对象装配)
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。
对象装配(对象注⼊)的实现⽅法以下 3 种:
1. 属性注⼊
2. 构造⽅法注⼊
3. Setter 注⼊
1.属性注入
属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中。
Service类的代码如下:
@Service
public class UserService {public void doService(){System.out.println("doService");}
}
Controller类的实现代码如下:
@Controller
public class UserController {@Autowiredprivate UserService userService;public void sayHi(){userService.doService();System.out.println("hi,userController~");}
}
最终结果如下:
2.构造方法注入
构造方法注入是在类的构造方法中实现注入,实现代码如下:
@Controller
public class UserController2 {private UserService us;public UserController2(UserService us) {this.us = us;}public void sayHi(){us.doService();System.out.println("hi,userController~");}
}
注意:1.如果只有一个构造方法,那么@Autowired可以省略
2.如果有多个构造方法,那么需要添加上@Autowired来明确指定到底使用哪个构造方法,否则程序会报错。
3.Setter注入
Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注
解,如下代码所示:
@Controller
public class UserController1 {private UserService us;@Autowiredpublic void setUs(UserService us) {this.us = us;}public void sayHi(){us.doService();System.out.println("hi,userController~");}
}
4.三种注入方法的优缺点
优点 | 缺点 | |
属性注入 | 简介,使用方便 | 1.是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常) 2.不能注入一个final修饰的属性 |
构造方法注入( Spring 推荐 ) | 1.可以注入final修饰的属性 2.注入的对象不会被修改 3.依赖对象在使用前一定会被完全初始化 4.通用性好 | 如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了, |
setter注入 | 方便再类实例之后,重新对该对象进行配置或注入 | 1.不能注入一个final修饰的属性 2.注入对象可能会被改变,因为setter方法可能会被多次调用,就有被修改的风险 |
补充:final修饰的属性,需要具备下列两个条件之一:
1.初始化赋值
2.构造函数赋值
5.@Resource:另一种注入关键字
在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下代码所示:
@Controller
public class UserController3 {@Resourceprivate UserService us;public void sayHi(){us.doService();System.out.println("hi,userController~");}
}
@Resource和@Autowired的区别
- 出身不同:@Autowired来自于Spring,⽽ @Resource 来⾃于 JDK 的注解;
- 使⽤时设置的参数不同:相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean。
- @Autowired 可⽤于 Setter 注⼊、构造函数注⼊和属性注⼊,⽽ @Resource 只能⽤于 Setter 注⼊和属性注⼊,不能⽤于构造函数注⼊。
补充:
@Autowired
根据类型来获取bean,如果同一个类型的bean只有一个,直接注入成功
如果同一个类型的bean有多个,以名称匹配,注入相同名称的bean
如果没有名称相同的,就报错
6.同一类型多个@Bean报错
1.现象
当出现以下多个 Bean,返回同⼀对象类型时程序会报错,如下代码所示:
@Component
public class Users {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java");return user;}@Beanpublic User user2() {User user = new User();user.setId(2);user.setName("MySQL");return user;}
}
在另一个类中获取User对象:
@Controller
public class UserController4 {// 注⼊@Resourceprivate User user;public User getUser() {return user;}
}
报错了,报错原因为:非唯一的Bean对象
2.报错处理
解决同⼀个类型,多个 bean 的解决⽅案有以下两个:
- 使⽤ @Resource(name="user1") 定义。
- 使⽤ @Qualifier 注解定义名称。