Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源框架,Spring ⽀持⼴泛的应⽤场景,它可以让 Java 企业级的应⽤程序开发起来更简单。
1.1.1.1 什么是IoC容器?
容器是⽤来容纳某种物品的装置。
IoC = Inversion of Control 翻译成中⽂是"控制反转"的意思,控制权发⽣的反转,不再是上级对象创建并控制下级对象了,⽽是下级对象把注⼊将当前对象中,下级的控制权不再由上级类控制了,这样即使下级类发⽣任何改变,当前类都是不受影响的,这就是 IoC 的实现思想。
Spring具备两个核心功能:将对象存⼊到容器,从容器中取出对象。对象的创建和销毁的权利都交给 Spring 来管理了,它本身⼜具备了存储对象和获取对象的能⼒。
1.1.1.2 DI 概念说明
DI 是 Dependency Injection 的缩写,翻译成中⽂是"依赖注⼊"的意思。依赖注⼊就是由 IoC 容器在运⾏期间,动态地将某种依赖关系注⼊到对象之中。所以,依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦,IoC 是"⽬标"也是⼀种思想,⽽⽬标和思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI就属于具体的实现。
1.1.1.3 IoC容器和普通程序开发的区别
将对象存储在 IoC 容器相当于将以后可能⽤的所有⼯具制作好都放到仓库中,需要的时候直接取就⾏了,⽤完再把它放回到仓库。
new 对象的⽅式相当于,每次需要工具了才现做,用完扔掉了也不会保存,下次再用的时候还得重新做。
1.2 Spring 创建和使用
1.2.1 创建 Spring 项目
使⽤ Maven ⽅式来创建⼀个 Spring 项⽬,创建 Spring 项⽬和 Servlet 类似。
创建⼀个 Maven 项⽬
然后跳转到了这个页面:
添加 Spring 框架⽀持
在项⽬的 pom.xml 中添加 Spring 框架的⽀持spring-context(spring 上下⽂)和spring-beans(管理对 象的模块),xml 配置如下:
// 1.得到 Spring 上下⽂对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-con fig.xml");
// 2.加载某个 bean
User user = (User) context.getBean("user");
Bean 的 Id 要⼀⼀对应,如下图所示:
getBean ⽅法的更多⽤法
根据类型获取 Bean:
java复制代码
UserController user = context.getBean(UserController.class);
名称 + 类型获取 Bean:
java复制代码
UserController user = context.getBean("user", UserController.class);
public class App {
public static void main(String[] args) {
// 1.得到 Spring 上下⽂对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.加载某个 bean
User user = (User) context.getBean("user");
// 3.调⽤相应的⽅法
System.out.println(user.sayHi("Java"));
}
}
1.2.4 使用注解读取和存储对象
配置扫描路径
配置⼀下存储对象的扫描包路径,只有在被配置的包下且添加了注解的类才能被正确的识别并保存到 Spring 中。
在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中
使用@Bean存储 bean
java复制代码
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
读取bean
java复制代码
public class Application {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User) context.getBean("user1");
System.out.println(user.toString());
}
}
重命名 Bean
可以通过设置 name 属性给 Bean 对象进⾏重命名。重
java复制代码
@Component
public class Users {
@Bean(name = {"u1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
命名的 name 其实是⼀个数组,⼀个 bean 可以有多个名字。
java复制代码
@Bean(name = {"u1", "us1"}) public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
name={} 可以省略
java复制代码
@Bean({"u1", "us1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
此时使⽤ u1/us1 就可以获取到 User 对象了
java复制代码
class App {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到某个 bean
User user = (User) context.getBean("u1");
// 3.调⽤ bean ⽅法
System.out.println(user);
}
}
对象装配
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。
<1> 属性注⼊
属性注⼊使⽤ @Autowired 实现。
属性注⼊的优点是简洁,使⽤⽅便;缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只 有在使⽤的时候才会出现 NPE(空指针异常)。
示例:
Service 类的实现代码如下:
java复制代码
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 根据 ID 获取⽤户数据
*
* @param id
* @return
*/
public User getUser(Integer id) {
// 伪代码,不连接数据库
User user = new User();
user.setId(id);
user.setName("Java-" + id);
return user;
}
}
Controller 类的实现代码如下:
java复制代码
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller;
@Controller
public class UserController {
// 注⼊⽅法1:属性注⼊
@Autowired
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
}
}
核⼼实现:
获取 Controller 中的 getUser ⽅法:
java复制代码
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserControllerTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean(UserController.cla ss);
System.out.println(userController.getUser(1).toString());
}
}
运行结果:
<2>构造⽅法注⼊
构造⽅法注⼊是在类的构造⽅法中实现注⼊。
构造⽅法注⼊是 Spring 推荐的注⼊⽅式,它的缺点是如果有多个注⼊会显得⽐较臃肿,但出现这 种情况应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式,优点是通⽤性,在使⽤之前⼀定能把保证注⼊的类不为空。
示例:
java复制代码
@Controller
public class UserController2 {
// 注⼊⽅法2:构造⽅法注⼊
private UserService userService; // 创建userService引用
@Autowired
public UserController2(UserService userService) { // 注入并通过构造方法让上面的引用指向注入进来的这个对象
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
}
}
@Component
public class Users {
// 第一个bean
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
// 第二个bean
@Bean
public User user2() {
User user = new User();
user.setId(2);
user.setName("MySQL");
return user;
}
}
在另⼀个类中获取 User 对象:
java复制代码
@Controller
public class UserController4 {
// 注⼊
@Resource
private User user;
public User getUser() {
return user;
}
}
执⾏结果:
解决方法:
<<1>>使⽤ @Resource(name="XXX")来指定bean
java复制代码
@Controller
class UserController4 {
// 注⼊
@Resource(name = "user1")
private User user;
public User getUser() {
return user;
}
}
<2> 使⽤ @Qualifier来指定bean
java复制代码
@Controller
public class UserController5 {
// 注⼊
@Autowired
@Qualifier(value = "user2")
private User user;
public User getUser() {
return user;
}
}
2. Bean 详解
2.1 Bean的作用域
限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。
Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式。
Bean 有 6 种作⽤域:singleton(单例作⽤域),prototype(原型作⽤域/多例作⽤域), request(请求作⽤域),session(回话作⽤域),application(全局作⽤域),websocket(HTTP WebSocket 作⽤域)。后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项⽬中只有前两种。
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java"); // 【重点:名称是 Java】
return user;
}
}
A ⽤户使⽤时,进⾏了修改操作:
java复制代码
@Controller
public class BeanScopesController {
@Autowired
private User user1;
public User getUser1() {
User user = user1;
System.out.println("Bean 原 Name:" + user.getName());
user.setName("悟空"); // 【重点:进⾏了修改操作】
return user;
}
}
B ⽤户再去使⽤公共 Bean :
java复制代码
@Controller
public class BeanScopesController2 {
@Autowired
private User user1;
public User getUser1() {
User user = user1;
return user;
}
}
查看 A ⽤户和 B ⽤户公共 Bean 的值:
java复制代码
public class BeanScopesTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanScopesController beanScopesController = context.getBean(BeanScopesController.class);
System.out.println("A 对象修改之后 Name:" + beanScopesController.getUser1().toString());
BeanScopesController2 beanScopesController2 = context.getBean(BeanScopesController2.class);
System.out.println("B 对象读取到的 Name:" + beanScopesController2.getUser1().toString());
}
}
在⼀个http servlet Context中,定义⼀个Bean实例,用于Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息。限定SpringMVC中使⽤。
application 是 Spring Web 中的作⽤域,作⽤于 Servlet 容器。singleton 是 Spring Core 的作⽤域,singleton 作⽤于 IoC 的容器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class Users {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean(name = "u1")
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java"); // 【重点:名称是 Java】
return user;
}
}
2.2 Bean的生命周期
2.2.1 Spring 执行流程
2.2.2 Bean 的执行流程
启动 Spring 容器 --> 实例化 Bean(分配内存空间,从⽆到有) --> Bean注册到Spring中(存操作) --> 将Bean装配到需要的类中(取操作)。