文章目录
- 1. 对Spring的理解
- 2. Spring IoC
- 3. DI
- 4. 如何创建一个Spring项目
- 4.1 创建一个Maven项目
- 4.2 添加Spring框架支持
- 4.3 添加启动类
- 5. 存储Bean对象
- 5.1 添加配置文件
- 5.2 创建Bean对象
- 5.3 注册Bean
- 6. 获取并使用Bean对象
- 7. 更简单存储Bean对象的方式
- 7.1 前置工作
- 7.2 添加存储对象的注解
- 7.2.1 类注解
- 7.2.2 方法注解
- 8. 对象装配/对象注入
- 8.1 属性注入
- 8.2 构造方法注入
- 8.3 Setter注入
- 8.4 @Resource注解
- 8.5 @Bean将一个对象多次注入问题
1. 对Spring的理解
Spring是一个框架,包含了众多工具方法的IoC
容器
什么是容器?
容器就是容纳物品的装置,比如:
- List/Map,用来储存数据,说明它是一个数据存储容器
- Tomcat,webapps目录下保存多个web应用程序,说明它是一个web容器
什么是IoC?
IoC的意思就是控制反转,是一种思想,主要通过IoC容器实现控制权发生反转,如对象的初始化及依赖关系(对象引用另一个对象),由程序自己控制这部分逻辑转变为由容器统一控制
在传统的开发中,如果A对象依赖B对象,那在A对象中就要new B对象,此时B对象的控制权就在A中,但是如果B对象一旦有所改变,如构造方法改变,那在A中也就要发生改变
控制反转呢就是控制权发生反转,B对象的控制权交给容器统一管理,此时B对象改变的话,A对象不用做任何处理,容器自动的将改动后的B对象交给A,此时A不必关注B的实现的方式,只管用就行
IoC的优点: 实现代码的解耦,将对象的生命周期交给IoC容器来管理,程序员无需管理
2. Spring IoC
前面说了Spring是一个包含众多工具方法的IoC容器,所以关键还是在容器上,所谓容器,就得具有容器的基本功能,取东西,存东西,对应这里的Spring容器,它具有最基本的功能为:
- 将对象存储到容器
- 从容器中将对象取出
Spring IoC就是一个实现了IoC思想的框架,具有存储Bean和取Bean的功能
将容器中存储的对象称为Bean对象(Java中的普通对象),Spring是一个IoC容器也就是将对象的创建和销毁的权利都交给Spring来管理,它本身具备了存储对象和获取对象的能力
3. DI
DI的意思为依赖注入,就是由IoC容器在运行期间,动态的将某种依赖关系注入到当前类中,就是将依赖的某个对象拿过来给当前类使用
DI与IoC的区别?
IoC是一种思想,DI为具体实现,它俩是从不同角度描述同一件事情,就是利用IoC容器,利用依赖关系注入的方式实现对象之间的解耦,例如:吃饭(IoC),吃米饭(DI)
4. 如何创建一个Spring项目
4.1 创建一个Maven项目
设置好创建项目的路径
4.2 添加Spring框架支持
在pom.xml中添加Spring的框架支持,xml配置如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.23</version>
</dependency>
</dependencies>
4.3 添加启动类
在java文件夹下,创建一个启动类,包含main方法
5. 存储Bean对象
5.1 添加配置文件
第一次添加需要在项目中添加Spring配置文件(只需第一次添加
)
在resources
目录下创建一个spring-config.xml(此文件名可以为别的,建议为spring-config)
<?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>
5.2 创建Bean对象
Bean对象就是Java中普通的对象,这里我们创建一个Dog类
public class Dog {
public void say(String name){
System.out.println(name+":旺旺~");
}
}
5.3 注册Bean
将创建好的Bean对象通过配置文件注册到Spring中
<bean id="dog" class="com.beans.Dog"></bean>
id为bean的名称,相当于给bean对象起一个名字,class为bean的路径(包名+类名)
6. 获取并使用Bean对象
使用Spring上下文的方式获取Bean对象
- 先获取Spring上下文
- 再通过上下文对象提供的方法获取指定的Bean对象
public class App {
public static void main(String[] args) {
//ApplicationContext是一个接口,new其实现类,构造方法的参数为刚才创建的配置文件的名称
//得到spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//使用spring容器获取bean对象,这里使用bean的名称获取,就是配置文件中bean的id
//getBean的返回类型为Object,所以强转一下
Dog dog = (Dog)context.getBean("dog"); //该句代码就是上述说的DI
dog.say("旺财");
}
}
getBean的更多用法:
- 使用bean id获取bean(需强转,返回类型为Object,
id必须唯一
)
Dog dog = (Dog)context.getBean("dog");
- 使用bean class获取bean(该类必须有唯一的bean,也就是多个该类型被注入到Spring中会报错)
Dog dog = context.getBean(Dog.class);
使用此种方式不用强转,但是该类必须唯一
- 使用bean id和class获取bean
Dog dog = context.getBean("dog",Dog.class);
此种方式是为了解决使用class获取bean时存在多个相同类型的bean对象,此时加上id(因为id的唯一性)就可以获取唯一的bean对象,当相同的类多次注入到spring容器中时,此时多个相同类型的bean对象各不相同(每个bean都有其唯一的地址)
不管使用哪种方式获取,都必须保证能获取唯一的bean对象,否则会报错
推荐使用最后一种id和class
获取bean对象
使用Bean工厂的方式获取Bean对象
- 先获取bean工厂对象
- 再使用该对象的getBean方法获取Bean对象
public class App {
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
Dog dog = factory.getBean("dog",Dog.class);
dog.say("旺财");
}
}
ApplicationContext与BeanFactory有什么区别?
- ApplicationContext属于BeanFactory的子类
- BeanFactory只提供了基础访问Bean的方法,而ApplicationContext除了拥有BeanFactory的所有功能外,还提供了更多的方法实现,如对国际化的支持,资源访问的支持以及事件和传播等方面的支持
- 性能两者不同,BeanFactory是按需加载Bean类似懒加载,而ApplicationContext在创建时会将所有Bean加载起来,属于饿汉式
使用ApplicationContext的方式,当代码执行完上述步骤的时候,所有的bean对象都会被初始化好加载到spring容器中
使用BeanFactory的方式,只有真正调用getBean的时候才会初始化对应的对象然后加载到spring容器中,类似懒加载的方式
7. 更简单存储Bean对象的方式
7.1 前置工作
更简单存储Bean的方式就是使用包扫描+注解
的方式,此种方法可以和上述添加bean标签的方式共存
在Spring配置文件中设置bean扫描路径,将前面配置的xml文件换成下面的,但是注意下面存放bean对象的包名要和自己的创建的bean对象的包名要对应
<?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">
<!-- com.beans 是所有要存放bean对象的根路径-->
<content:component-scan base-package="com.beans"></content:component-scan>
</beans>
7.2 添加存储对象的注解
7.2.1 类注解
- @Controller,控制层(前端参数校验)
- @Service,服务层(数据组装和接口调用)
- @Repository,数据持久层(负责和数据库进行交互)
- @Component,组件(非其他四类)
- @Configuration,配置层(系统运行前,提前加载一些资源)
@Controller
public class UserController {
public void sayHello(){
System.out.println("hello Controller");
}
}
使用注解注入Bean对象时,bean的名称也就是bean
id为类名小驼峰
的表示
如果原类名的第一个字母和第二个字母都是大写,那么此时的beanid为原类名
不变
public class Application1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//bean id为类名改写为小驼峰方式
UserController userController = context.getBean("userController",UserController.class);
userController.sayHello();
}
}
其他四个注解与此用法相同
为什么需要五大类注解
提高代码的可读性,让程序员能够直观的判断当前类的用途
类注解的关系
@Controller,@Service,@Repository,@Configuration都是基于@Component实现的,所以可以认为@Component是它们的父类
7.2.2 方法注解
@Bean注解加到方法上,将方法的返回值添加到容器中
注意: @Bean注解必须结合五大类注解来使用,并且方法必须有返回值
@Component
public class Students {
@Bean
public Student s1(){
Student student = new Student();
student.setName("张三");
student.setAge(23);
return student;
}
}
public class App1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = context.getBean("s1",Student.class);
System.out.println(student);
}
}
@Bean重命名
@Bean命名规则:
- 当没有设置name属性时,方法名为bean名称
- 当设置了name属性,只能通过name属性对应的值来获取,使用方法名就获取不到
@Component
public class Students {
@Bean(name = "student1")
public Student s1(){
Student student = new Student();
student.setName("张三");
student.setAge(23);
return student;
}
}
public class App1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = context.getBean("student1",Student.class);
System.out.println(student);
}
}
起名字的时候,可以起多个名字:
@Bean(name = {"user1","user2","user3"})
public User getUser(){
User user = new User();
user.setUsername("张三");
user.setAge(10);
return user;
}
8. 对象装配/对象注入
8.1 属性注入
使用@Autowired
注解获取到Spring中的Student对象,将其注入到UserController1的属性中
@Controller
public class UserController1 {
@Autowired
private Student student;
public void sayHello(){
System.out.println(student);
}
}
public class App2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController1 userController1 = context.getBean(UserController1.class);
userController1.sayHello();
}
}
8.2 构造方法注入
构造方法注入是在构造方法上加@Autowired
,实现注入
@Service
public class UserService {
private Student student;
@Autowired
public UserService(Student student1){
this.student = student1;
}
public void say(){
System.out.println(student);
}
}
public class App2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = context.getBean(UserService.class);
userService.say();
}
}
如果当前类中只存在一个构造方法,那么@Autowired可以省略
,多个构造方法时则不可以省略
8.3 Setter注入
Setter注入是在设置的set方法上加@Autowired
注解
@Controller
public class UserController2 {
private Student student;
@Autowired
public void setStudent(Student student) {
this.student = student;
}
public void say(){
System.out.println(student);
}
}
public class App1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController2 userController2 = context.getBean(UserController2.class);
userController2.say();
}
}
注意: 属性注入与Setter注入不可往final修饰的变量中注入,但是构造方法注入可以往final修饰的变量中注入
原因:final修饰的变量必须直接赋值,或者使用构造方法赋值,构造方法只会执行一次
8.4 @Resource注解
在进行对象注入的时候,除了可以使用 @Autowired 关键字之外,我们还可以使用 @Resource
进行注入
public class UserService2 {
@Resource(name = "student1")
private Student student;
public void say(){
System.out.println(student);
}
}
@Autowired与@Resource区别
- 出身不同:@Resource来自于jdk,@Autowired时Spring框架提供的
- 用法不同:@Autowried可以用于属性注入,构造方法注入,Setter注入,@Resource不支持构造方法注入
- 参数不同:@Resource支持更多的参数设置,比如name,type,而@Autowried只支持required参数
8.5 @Bean将一个对象多次注入问题
在spring容器中找bean有两种方式:
- 根据bean的名称也就是bean id
- 根据bean的类型也就是bean class
先创建一个Cat类,属性有name和color,并提供get,set,toString方法
往Spring中注入多个Cat对象
@Controller
public class Cats {
@Bean
public Cat cat1(){
Cat cat = new Cat();
cat.setName("糯米");
cat.setColor("白色");
return cat;
}
@Bean
public Cat cat2(){
Cat cat = new Cat();
cat.setName("汤圆");
cat.setColor("黑色");
return cat;
}
}
在另一个类中获取Cat对象
@Controller
public class CatController {
@Autowired
private Cat cat;
public void getCat(){
System.out.println(cat);
}
}
创建启动类
public class App3 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
CatController catController = context.getBean(CatController.class);
catController.getCat();
}
}
发现报错了,因为Spring中已经注入了两个Cat对象,所以此时不知道获取哪个对象
三种解决方案
- 精确的描述bean的名称(将注入的名称写对)
- 使用
@Rsource设置name
方式来重命名注入对象 - 使用
@Autowired+@Qualifier
来筛选bean对象
方案一:精确的描述bean的名称(将注入的名称写对)
@Controller
public class CatController {
@Autowired
private Cat cat1; //精确描述bean名称
public void getCat(){
System.out.println(cat1);
}
}
方案二: 使用@Rsource设置name方式来重命名注入对象
@Controller
public class CatController {
@Resource(name = "cat2")
private Cat cat;
public void getCat(){
System.out.println(cat);
}
}
方案三:使用@Autowired+@Qualifier来筛选bean对象
@Controller
public class CatController {
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
public void getCat(){
System.out.println(cat);
}
}