Spring学习笔记
前言
1. IoC(控制反转)
IoC是面向对象编程中的一种设计原则,可以用来减少代码之间的耦合。实现了IoC思想的容器成为IoC容器。
- 在没有引入IoC容器前, 对象A依赖对象B,那么A对象在实例化或者运行到某一点时,自己必须主动创建对象B或者使用已经创建好的对象B。
- 而在引入IoC容器后,对象A和对象B之间失去了直接联系,当对象A需要使用到对象B时,IoC容器会自动创建一个对象B注入到对象A所需要的地方。
通过上面的对比,可以看出对象A获得依赖对象B的过程,由主动变成了被动,即将创建对象的操作交给了IoC容器,控制权颠倒了过来,这就叫控制反转。
2.DI(依赖注入)
DI是实现IoC的方法之一,所谓依赖注入,就是IoC容器在运行期间,动态地将某种依赖关系注入到对象当中。
3.IoC的优点
代码解耦、提高灵活性、可扩展性和可维护性
一、初始化/注册Bean
1.类注解
在类上使用注解@Controller
,@Service
,@Repository
,@Component
。需要保证该类会被Spring扫描到,这种配置方式默认会注册一个名称为类名首字母小写的Bean对象到容器中。
package org.example.test;
import org.springframework.stereotype.Repository;
@Repository
public class Login {
}
定义好Bean对象并注册到容器中后就可以获取Bean对象了,获取Bean对象有两种方式:
- 通过类型获取:这种获取方式要求该类型的Bean只有一个
- 通过名称获取:同意类型的Bean可以有多个
package org.example.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Operation {
public static void main(String[] args) {
//根据Spring配置文件路径创建容器:应用上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//获取Bean的两种方式
Login login = (Login) context.getBean("login"); //通过名称获取,注意小写
Login login2 = context.getBean(Login.class); //通过类型获取
System.out.println(login);
System.out.println(login2);
}
}
/*获取到的是同一个对象
org.example.test.Login@21a947fe
org.example.test.Login@21a947fe
*/
2.@Bean
当前类被Spring扫描到时,可以在方法上使用@Bean
注解,通过方法返回类型,也可以定义、注册Bean对象,默认使用方法名作为Bean的名称
package org.example.test;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ToString
@Getter
@Setter
public class Person {
private String name;
private int age;
}
使用注解@Bean来注册Bean对象,注意该方法的类必须被Spring扫描到
package org.example.test;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller //对该类使用类注解
public class usePerson {
@Bean //注册Bean对象
public Person person1(){
Person person = new Person();
person.setAge(23);
person.setName("张三");
return person;
}
@Bean
public Person person2() {
Person person = new Person();
person.setName("李四");
person.setAge(15);
return person;
}
}
使用Bean对象,默认使用方法的名称作为Bean的名称
Person person1 = (Person) context.getBean("person1");
Person person2 = (Person) context.getBean("person2");
System.out.println(person1);
System.out.println(person2);
/*
Person(name=张三, age=23)
Person(name=李四, age=15)
*/
3.@Configuration
当类被Spring扫描到时,使用@Configuration
注解,可以注册一个配置类到容器中。配置类一般用来自定义配置某些资源。
4.FactoryBean接口
实现了FactoryBean
接口的Bean,根据该Bean的ID从容器中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,在ID前加上&符号来获取
。
FactoryBean接口的实现方式:
public interface FactoryBean<T> {
//返回的对象实例
T getObject() throws Exception;
//Bean的类型
Class<?> getObjectType();
//是否是单例的Bean对象,不实现该接口方法默认为单例
default boolean isSingleton() {
return true;
}
}
实现FactoryBean接口,指定泛型类型为Person类,将getObject方法的返回对象注册到容器中
@Component
public class UseFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
Person p = new Person();
p.setName("老王");
p.setAge(52);
return p;
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
}
Person useFactoryBean = (Person) context.getBean("useFactoryBean"); //之前已经定义过Person对象,所以只能通过ID获取
System.out.println(useFactoryBean);
二、依赖注入
1.属性注入
当前类被Spring扫描到时,可以在属性上使用@Autowired
注解,会将容器中的Bean对象装配进来。
package org.example.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class Login {
@Autowired
private Person person;
}
在setter方法上使用@Autowired
注入
package org.example.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LoginBySetter {
private Person person;
public Person getPerson() {
return this.person;
}
@Autowired //在构造方法上加@Autowired
public void setPerson(Person person) {
System.out.println("LoginBySetter: Person = " + person);
this.person = person;
}
}
2.构造方法注入
当前类被Spring扫描到时,可以在构造方法上使用@Autowired
注解,作用和setter方法类似,会将容器中的Bean对象注入方法参数。
@Repository
public class Login {
private Person person;
@Autowired
public Login(Person person) {
this.person = person;
}
}
在使用@Bean
注解的方法上,容器中的Bean会自动注入参数
@Bean
public Person person3(Login login){
System.out.printf("person3: %s%n", login == this.login);
Person person = new Person();
person.setAge(33);
person.setName("小马");
return person;
}
3.注入指定的Bean:@Qualifier
同类型的Bean有多个时,注入该类型Bean需要指定Bean的名称:
- 属性名或方法参数名设置为Bean的名称
- 属性名或犯法参数设置
@Qualifier("名称")
注解,注解内的字符串是Bean对象的名称
三、Bean的作用域
Spring容器在初始化一个Bean的实例时,同时会指定该实例的作用域。Spring有6个作用域,最后四种基于Spring WebMVC生效:
singleton
该作用域下的Bean在IoC容器中只有一个实例:获取Bean及装配Bean都是同一个对象。通常无状态的Bean使用该作用域,无状态表示Bean对象的属性不需要更新。Spring默认选择该域。
prototype
每次对该作用域下的Bean的请求都会创建新的实例:获取Bean及装配Bean都是新的对象实例。通常有状态的Bean 使用该作用域。
request
每次http请求会创建新的Bean实例,类似于prototype。一次http的请求和响应的共享Bean。
session
在一个http session中,定义一个Bean实例。用户会话的共享Bean,如:记录一个用户的登录信息。
application
在一个http servlet Context 中,定义一个Bean实例。Web应用的上下文信息,如:记录一个应用的共享信息。
websocket
在一个http WebSocket的生命周期中,定义一个Bean 实例。WebSocket的每次会话中,保存了一个Map结构的头信息,将用来包裹客户端消息头。第一次初始化后,直到WebSocket结束都是同一个Bean。