在项目的 pom.xml 中添加 Spring 支持
如何选定版本环境:打开官网,点击github图标
jdk8最后一个Spring版本是5.3.x,Spring6.0.x最低需要jdk17
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.31</version>
</dependency>
</dependencies>
版本冲突问题Maven自己处理
version : 可以选择带有 RELEASE结尾或者纯数字结尾,这样的版本更稳定
项目下创建一个main方法的启动类
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
如果获取多个 Bean 的话,重复以上2,3步骤
Spring 上下文可以使用 ApplicationContext 或者 BeanFactory获取
ApplicationContext顶层接口
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanFactory顶层接口
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
继承关系
ApplicationContext 和 BeanFactory 效果是一样的,BeanFactory 属于 ApplicationContext 子类,它们区别如下
继承角度:Spring 容器有两个顶级接口:BeanFactory 和 ApplicationContext
其中 BeanFactory 提供了基础的访问容器的能力,而 ApplicationContext 属于 BeanFactory 的子类,除了 BeanFactory 的所有功能外,它还拥有独特的特性,添加了对国际化的支持、资源访问的支持、以及事件传播等方面的支持
性能角度:ApplicationContext 是一次性加载并初始化所有的 Bean 对象「更方便」;BeanFactory 是按需加载并初始化「更轻量」
ClassPathXmlApplicationContext 属于 ApplicationContext 的子类,拥有 ApplicationContext 的所有功能是通过 xml 的配置来获取所有的 Bean 容器的
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-config.xml");
User user = (User) context.getBean("user");
user.sayHi();
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring-config.xml"));
User user1 = (User) beanFactory.getBean("user");
user1.sayHi();
// 运行结果
你好
你好
Bean 的 id 要一一对应
<beans>
<bean id="user" class="Beans.User"></bean>
</beans>
User user = (User) context.getBean("user");
User user1 = (User) beanFactory.getBean("user");
getBean() 方法很多重载,我们也可以使用其他方法来获取 Bean 对象
先看一下 getBean() 源码
getBean(String, Class): 先根据 id 匹配,再根据Class匹配
当有两个相同 id 的时候利用此方法查询会 报错Nouni
get(Class, String): 先根据Class匹配,再根据 id 匹配
当有两个的Bean,利用此方法,则会 报错Nouni
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-config.xml");
User user = context.getBean(User.class);
User user1 = context.getBean("user1", User.class);
二者的区别
当有多个重复的对象被注册到 Bean 中的时候,只能通过 id属性 来获取
<beans>
<bean id="user" class="Beans.User"></bean>
<bean id="user1" class="Beans.User"></bean>
<bean id="user2" class="Beans.User"></bean>
</beans>
如果继续使用 context.getBean(User.class)
就会报错。应该搭配 id 一起使用
在上述操作过程中,我们发现存储对象并没有想象中的那么 简单,所以就有了更简单的操作 Bean对象 的方法
Spring 更简单的存储对象和读取对象核心是使用注解
之前我们还需要在 Spring 的配置文件 spring-config.xml 中添加一行 bean
注册内容才行
因此 Spring 中为了方便注册,我们只需要配置一个存储对象的扫描包即可「目录中的所有 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: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="/Beans"></content:component-scan>
</beans>
对比之前的 Spring-config.xml 发现多了标红的两行,这两行就是注册扫描的包
也就是说,即使添加了注解,如果不是在配置的扫描报下的类对象,也是不能被存储的 Spring 中的
想把扫描包中的 Bean 添加到 Spring,由两类注解类型可以实现
我们发现它们的功能是一样的,都能达到 添加注解注册Bean 的功能,那么为何还要有这么多注解类型呢?
这就和每个省市都有自己的车牌号一样。全国的各个地区买的相同类型的车都是一样的,但是车牌号却不同:湖北的车有鄂A:武汉的车;鄂B:黄石的车;鄂C:十堰的车。。。这样做的好处就是节约了车牌号以外还可以查看车辆的归属地方便车管局管理。
那么为什么需要这么多的类注解也是一样的原因,就是让程序员看到类注解之后就能直接了解当前类的用途
程序的工程分层调用流程如下:
发现这 4个 注解里都有一个注解 @Component,说明他们本身就是属于 @Component子类
在看 @Component的源码
源码溯源就到此为止了,只需要了解它们四个实现了@Component接口即可
通过注解注册的bean名称有自己的一套默认规则:
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 如果类名长度大于1,并且前俩字母都是大写就直接返回原类名
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))) {
return name;
}
// 否则就是将首字母小写再返回
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
类注解是添加到某个类上的,而方法注解是放到某个方法上的
@Bean要搭配类注解一起使用才可以将方法存储到 Spring 中
model层
package app.model;
public class User {
private Integer id;
private String userName;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
service层
package app.service;
import app.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void doUserService() {
System.out.println("doUserService.");
}
public User getUser() {
User user = new User();
user.setId(1);
user.setUserName("张三");
user.setAge(18);
return user;
}
}
controller层
package app.controller;
import app.model.User;
import app.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
// 也可以通过 xml 中 bean 标签注册
@Controller
public class UserController {
@Autowired
private UserService userService;
public void doUserController() {
System.out.println("doUserController.");
}
@Bean
public User getUser() {
return userService.getUser();
}
}
获取@Bean对象:bean名称采取的是方法名而非类名
// 1.获取 Spring 上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
// @Bean方法注解
User user = beanFactory.getBean("getUser", User.class);
同一项目下类名一般不会出现重复但方法名有意外情况
查看 @Bean 源码会发现 name 是一个数组,因此可以填入很多bean的名称。但一般一个就够。
3种方式给Bean重命名。重命名之后就不能通过方法名获取Bean对象了
回顾之前在存储bean对象之后是如何获取的
这里就讲解一下对象注入
对象装配:获取 Bean 对象
对象注入:把对象取出来放在某个类中
对象注入有 3 种方法
@Autowired: 现根据类型查找再根据名称查找。所以bean的名称随意给都可以,这里按照标准给的是 userService
优点:写法简单
缺点:
优点:符合单一设计原则
缺点:
final不加也可以,对于专业版IDEA会提示进行final修饰
当当前类中只有一个构造方法的时候可以省略@Autowired
优点:
仅支持两种装配方式,不支持构造方法注入。用法和@Autowired一样
@Resource支持更多的参数【JDK自带】
经常用的是name参数,如果Spring中 @Bean 注解存储的Bean对象制定了名称,那么此时可以通过name属性用于指定获取对应名称的bean
同一个类型注入多个Bean对象
获取Bean对象的时候需要指定bean名称,否则会在运行时报错
此时因为是user1,@Autowired现根据type查找会发现有多个User类型的Bean对象然后再根据name查找就会获取到user1的bean对象
此时对于开发而言,user1这样的明明肯定不符合规定,因此需要一个修改一下bean的名字才行。所以引入了@Resource注解
但是@Resource注解由于不支持构造方法注入,如果仍然想用Spring的@Autowired注解使用构造方法注入就需要引入一个新注解解决名字的问题:@Qualifier
@Autowired | @Resouce | |
---|---|---|
来源 | 来自Spring | 来自 JDK |
使用时参数不同 | 搭配@Qualifier获取指定名称bean | 支持更多的参数,可以设置 name 来获取指定 Bean |
修饰对象不同 | 修饰属性,构造方法,Setter | 修饰属性,Setter |
对象注入
对象获取
运行结果
修改前: Dog{id=1, name='旺财', age=1}
修改后: Dog{id=1, name='喵喵', age=1}
后续获取: Dog{id=1, name='喵喵', age=1}
会发现由于单例模式,导致公共的Bean对象被修改之后,后续获取的Bean都会被修改
因此就引入Bean的作用域【从之前的代码区域提升到现在框架区域】
无状态:表示该Bean对象的属性状态不需要更新
设置的方式有两种
此时运行结果
修改前: Dog{id=1, name='旺财', age=1}
修改后: Dog{id=1, name='喵喵', age=1}
后续获取: Dog{id=1, name='旺财', age=1}
Bean的生命周期
会发现注解的执行顺序先于xml