目录
1.存储Bean对象
1.1.前置工作:在配置文件中设置bean扫描的根路径(重要)
1.2.添加注解存储Bean对象到Spring中
1.2.1.类注解(添加到某个类上,将当前的类存储到Spring中):@Controller,@Service,@Repository,@Component,@Configuration
关于类注解的bean的命名规则:
PS:为什么要这么多类注解?
1.2.2.方法注解(添加到某个方法上,将当前方法返回的对象存储到Spring中):@Bean
2.获取Bean对象(对象装配/对象注入)
2.1.属性注入:是使用@Autowired(自动写入)注解实现的
2.2.构造方法注入:是在类的构造方法中实现注入
2.3.Setter注入:和属性的Setter方法实现类似,只不过在设置set方法时需要加上@Autowired注解
PS:(经典面试题)属性注入,构造方法注入和Setter注入的异同分析?
2.4.另一种注入关键字:@Resource
2.4.1.属性注入:
2.4.2.Setter注入:
PS:(经典面试题)@Autowired和@Resource的区别?
2.5.同一类型多个@Bean报错
处理方法:
2.5.1.使用正确的bean name来获取(有限制,不用于大规模使用)
2.5.2.使用@Resource注解设置name属性(最简单)(@Autowired只有一个参数,没有name属性)
2.5.3.使用@Autowired + @Qualifier
在Spring中想要更简单地读取和存储对象的核心是使用注解。(Spring,SpringBoot,SpringMVC中的注解是通用的)
1.存储Bean对象
之前需要在spring-config.xml文件中添加bean标签注册内容,而xml的问题在于出错的概率和排错的难度都高:
- 无法像代码一样调试,只能人工去看;
- 有错后IDEA不会提示/即使有提示,程序也能正常运行;
- 写法不方便。
而现在只需要一个注解就ok~
1.1.前置工作:在配置文件中设置bean扫描的根路径(重要)
要想将对象存储到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">
<!--设置需要存储到spring中的bean根目录-->
<content:component-scan base-package="com.beans"></content:component-scan>
</beans>
其中:
<content:component-scan base-package="com.beans"></content:component-scan>
content:正文;
component-scan:扫描组件;
base-package:根目录(写自己的)。
规定了需要存储到Spring中的类的根目录的扫描路径,否则要扫描当前项目下的所有的类(要加到Spring中的和不要加到Spring中的类),这样提高了效率。
base-package是当前路径及当前路径下的所有子路径下的所有的bean。
1.2.添加注解存储Bean对象到Spring中
1.2.1.类注解(添加到某个类上,将当前的类存储到Spring中):@Controller,@Service,@Repository,@Component,@Configuration
使用这5大类注解中的任意一种都可以将bean存储到Spring中。
注:
- 即使在Spring配置文件中配置了bean的扫描路径,5大类注解依旧不能省略。
- 即使加了5大类注解,但类没有放在Spring配置的bean路径下,那么也是不能将类注入到Spring中的。
①@Controller(控制器存储)
@Controller //将当前对象存储到Spring中
public class UserController {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
用之前读取对象的方式来读取:
public class App {
public static void main(String[] args) {
//1.获取Spring上下文
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean
UserController userController = (UserController) context.getBean("userController");//通常情况下是小驼峰(将加注解的类的类名首字母小写)就可以获取到bean对象
//3.使用bean
userController.sayHi("张三");
}
}
当使用id获取带注解的bean时:
关于类注解的bean的命名规则:
通常bean使用的都是标准的大驼峰命名:
若bean的命名为第一个字母大写,第二个字母小写,那么读取时将bean的名称首字母小写;
若bean的命名为第一个字母和第二个字母都大写,那么读取时为原bean名;
若bean的命名为第一个字母和第二个字母都小写,那么读取时为原bean名(一般不存在)。
查看源码:
②@Service(服务存储)
@Service //将当前对象存储到Spring中
public class UserService {
public void sayHi(String name){
System.out.println("Hi," + name);
}
}
③@Repository(仓库存储)
@Repository //将当前对象存储到Spring中
public class UserRepository {
public void sayHi(String name){
System.out.println("Hi," + name);
}
}
④@Component(组件存储)
@Component //将当前对象存储到Spring中
public class UserComponent {
public void sayHi(String name){
System.out.println("Hi," + name);
}
}
⑤@Configuration(配置存储)
@Configuration //将当前对象存储到Spring中
public class UserConfiguration {
public void sayHi(String name){
System.out.println("Hi," + name);
}
}
PS:为什么要这么多类注解?
①5大类注解之间的关系:
查看源码:
结论:@Controller,@Service,@Repository,@Configuration这4个注解都是依靠@Component注解来实现的,因此@Component注解是其他4个注解的父类。那为啥还要其他4个呢?
②程序的工程分层-调用流程:
一个项目中后端接口最少有这4个目录,方便大型项目中类的分类,查找,维护:让程序员看到类注解后,能直接了解当前类的用途,让逻辑条理更加清晰;也方便项目的人员分工,每人只针对一部分即可。而若都是@Component注解,则不会直观地了解到类的用途。
so~类注解由1个变5个,使得整个代码的语义更加直观!
- @Controller:业务逻辑层/控制器层,和前端请求打交道,验证前端的有效性;
- @Service:服务层,组织数据调用相应的接口,不做具体的业务;
- @Repository:数据持久层/仓库(不同的叫法:dao/repository),和数据打交道,执行具体的CRUD操作;
- @Configuration:配置层,存放所有的配置信息。
还可能会有更多分层,如有实体层,工具层等。
1.2.2.方法注解(添加到某个方法上,将当前方法返回的对象存储到Spring中):@Bean
①方法注解要配合类注解一起使用,才能将对象正常存储到Spring中。(主要是为了考虑性能的问题) 默认读取bean的名称为方法名。
@Component
public class UserBeans {
@Bean
public User getUser() {
User user = new User();
user.setId(1);
user.setName("张三");
user.setPassword("123");
return user;
}
}
②重命名Bean
方法名主要体现业务性,多为动词;读取bean名称多为名词。若默认二者相等会比较奇怪。
那么可以通过在@Bean注解中设置name属性给Bean对象进行重命名操作:
@Component
public class UserBeans {
@Bean(name = {"user1"})
public User getUser() {
User user = new User();
user.setId(1);
user.setName("张三");
user.setPassword("123");
return user;
}
}
注:
1).这个重命名的name其实是一个数组,一个bean可以有多个名字。
含有"{ }"的写法表示给当前对象起一个或多个别名。
@Bean(name = {"user1"})
@Bean(name = {"user1", "user2"})
并且name = { } 可以省略。
@Bean("user1", "user2")
不含有"{ }"的写法表示给当前对象起一个别名。
@Bean(name = "user1")
2).当@Bean注解重命名之后就不能使用方法名来读取bean了。
2.获取Bean对象(对象装配/对象注入)
获取bean对象是把对象取出来放到某个类中。实现方法有3种。5大类注解不能省略(存了才能取)。
2.1.属性注入:是使用@Autowired(自动写入)注解实现的
例:将Service类注入到Controller类中:
Service类的实现代码如下:
import com.model.User;
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 根据id获取用户数据
* @param id
* @return
*/
public User getUserById(Integer id) {
//伪代码,不连接数据库
User user = new User();
user.setId(id);
user.setName("唐僧");
user.setPassword("123456");
return user;
}
}
Controller类的实现代码如下:
import com.beans.service.UserService;
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
//1.属性注入:先定义一个属性,再把对象注入给属性
@Autowired
private UserService userService;
public User getUserById(Integer id) {
return userService.getUserById(id);
}
}
获取Controller中的getUserById方法:
import com.beans.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
//1.获取Spring上下文
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean
UserController userController = context.getBean(UserController.class);
//3.使用bean
System.out.println(userController.getUserById(10));
}
}
2.2.构造方法注入:是在类的构造方法中实现注入
如果当前类只有一个构造方法,则@Autowired注解可以省略;如果当前类有多个构造方法,则@Autowired注解不可以省略。
import com.beans.service.UserService;
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController2 {
private UserService userService;
//2.构造方法注入
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public UserController2(UserService userService, Integer id) {
this.userService = userService;
}
public User getUserById(Integer id) {
return userService.getUserById(id);
}
}
2.3.Setter注入:和属性的Setter方法实现类似,只不过在设置set方法时需要加上@Autowired注解
import com.beans.service.UserService;
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController3 {
private UserService userService;
//3.Setter注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUserById(Integer id) {
return userService.getUserById(id);
}
}
PS:(经典面试题)属性注入,构造方法注入和Setter注入的异同分析?
同:
都可以将一个对象注入到当前的类当中。
异:
属性注入(差):
优点:简洁,使用方便。官方实际还是用属性注入多些。
缺点:不通用,只能用于IoC容器,非IoC容器不可用(使用的时候会出现NPE(空指针异常))。
构造方法注入(好):
优点:通用性强;且在使用之前一定能保证注入的类不为空(能保证在调用对象之前,此对象一定是存在的)是Spring后期推荐的注入方式。
缺点:可能存在传递多个参数来实现构造方法的初始化,如果有多个注入会显得代码比较臃肿,但出现这种情况是程序员自身的问题,他需要反思当前类是否符合程序的单一职责的设计模式。
Setter注入(中):
优点:是Spring早期版本推荐的注入方式,比属性注入适用性好,无论是IoC容器还是非IoC容器都可以使用。
缺点:通用性不如构造方法注入,所有Spring版本已经推荐使用构造方法注入的方式来进行类注入了。
2.4.另一种注入关键字:@Resource
在进行对象注入时,除了可以使用@Autowired关键字之外,还可以使用@Resource进行注入。
2.4.1.属性注入:
import com.beans.service.UserService;
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController4 {
@Resource //引入一个资源到当前类
private UserService userService;
public User getUserById(Integer id){
return userService.getUserById(id);
}
}
2.4.2.Setter注入:
import com.beans.service.UserService;
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController5 {
private UserService userService;
@Resource
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUserById(Integer id){
return userService.getUserById(id);
}
}
PS:(经典面试题)@Autowired和@Resource的区别?
①出身不同:
@Autowired是Spring框架提供的实现;
而@Resource是JDK提供的实现。
②支持的参数设置不同:
@Autowired只支持required设置;
@Autowired注解源码实现
而@Resource支持更多的参数设置,如name设置,根据名称获取bean。
@Resource注解源码实现
③支持的注入类型不同:
@Autowired支持属性注入,构造方法注入和Setter注入;
而@Resource只支持属性注入和Setter注入。
④获取注入一个对象时的机制不同:
@Autowired默认先按Type进行匹配,如果找到多个bean,则又会按照组件id方式进行匹配(需要@Qualifier("name")配合);
而@Resource默认按照组件id自动注入,如果按照默认组件id找不到bean时,再按照类型去匹配。
2.5.同一类型多个@Bean报错
import com.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class UserBeans {
@Bean(name = {"user1"})
public User getUser1() {
User user = new User();
user.setId(1);
user.setName("张三");
user.setPassword("123");
return user;
}
@Bean(name = "user2")
public User getUser2() {
User user = new User();
user.setId(2);
user.setName("李四");
user.setPassword("456");
return user;
}
}
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController6 {
@Resource
private User user;
public void sayHi(){
System.out.println(user);
}
}
import com.beans.controller.UserController6;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController6 controller6 = context.getBean(UserController6.class);
controller6.sayHi();
}
}
一个类型被注册到Spring多次时,程序运行会出现异常报错:非唯一的Bean对象。
处理方法:
2.5.1.使用正确的bean name来获取(有限制,不用于大规模使用)
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController6 {
@Resource
private User user2;
public void sayHi(){
System.out.println(user2);
}
}
2.5.2.使用@Resource注解设置name属性(最简单)(@Autowired只有一个参数,没有name属性)
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController6 {
@Resource(name = "user1")
private User user;
public void sayHi(){
System.out.println(user);
}
}
2.5.3.使用@Autowired + @Qualifier
@Qualifier只有一个属性,做筛选功能。
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
@Controller
public class UserController6 {
@Autowired
@Qualifier(value = "user2")
private User user;
public void sayHi(){
System.out.println(user);
}
}
其中:
@Qualifier(value = "user2")
也可以省略"value =",写为:
@Qualifier("user2")