Spring
Spring 是什么?
-
一个包含了众多工具方法的 IOC容器
-
IOC
- 控制反转
- 控制权的反转
- 控制对象的生命周期
- 控制反转
-
DI 是 Dependency Injection 的缩写,翻译成中⽂是“依赖注⼊”的意思
- 所谓依赖注⼊,就是由 IoC 容器在运⾏期间,动态地将某种依赖关系注⼊到对象之中
从Spring中获取Bean
xml获取
-
spring-config.xml配置
-
建立 beanid 与 类名的 映射
-
<?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="com.demo"></content:component-scan>--> <!-- <bean id="myComponent" class="com.demo.component.BeanLifeComponent"--> <!-- init-method="init"></bean>--> <bean id="user" class="org.example.User"/> </beans>
-
-
先获取Spring上下文
-
从spring中取出Bean对象
-
User user = (User) context.getBean("user"); //通过 id User user1 = context.getBean(User.class); // 通过类名.class User user2 = context.getBean("user", User.class); // 不需要强转了 System.out.println(user1.Hi());
-
- 操作流程如下图所示:
注解存储
-
jdk1.5之后才有注解
-
扫描指定包下的类注册到 spring中
-
<content:component-scan base-package="org.example"></content:component-scan>
-
类注解
-
@Controller
- 控制器
- 业务逻辑层
- 控制器
-
@Service
- 服务层
- 调用持久化实现相应的功能
- 服务层
-
@Repository
- 持久层
- 直接与数据库做交互
- 通常每一个表都会对应一个 @Repository
- 持久层
-
@Component
- 组件
- 归属于公共工具类,提供一些公共方法
- 组件
-
@Configuration
- 配置层
- 用来配置当前项目的一些信息
- 配置层
⽅法注解
-
@Bean
- 将当前修饰方法的返回类 存储到 spring中
-
使用:
-
⽅法注解 @Bean 一定要配合类注解才能将对象正常的存储到 Spring 容器中
-
@Component public class StudentBeans { @Bean public Student student(){ Student student = new Student(); student.setId(1); return student; } }
-
-
默认情况下,Bean name 等于 方法名
-
可以重命名 Bean
-
@Component public class StudentBeans { @Bean(name = {"s1","s2"}) public Student student(){ Student student = new Student(); student.setId(1); return student; } }
-
-
-
-
BeanFactory不能支持@Bean
bean命名规则
- spring通过调用jdk的工具方法类还生成注解的名字的
- 判断第一和第二个 都是大写
- 则直接返回name
- 假如第二个不是
- 则将首字母小写,返回
- 判断第一和第二个 都是大写
类注解之间的关系
- 查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现
- 其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”
注解获取bean
属性注⼊
-
@Data public class Student { private int id; @Autowired private User user; }
缺点
- 功能性问题:
- 不能注入 不可变/final对象
- 因为 jdk 规定 了 final 必须要在 初始化 和 构造函数中 赋值
- 不能注入 不可变/final对象
- 通用性问题:
- 只适用于 IOC容器
- 设计原则问题:
- 更加容易违背单一设计原则
构造⽅法注⼊
-
@Component public class Student { private int id; private User user; @Autowired public Student(User user){ this.user = user; } }
-
注意事项
-
如果只有⼀个构造⽅法,那么 @Autowired 注解可以省略,如下图所示:
-
@Component public class Student { private int id; private User user; public Student(User user){ this.user = user; } }
-
但是如果类中有多个构造⽅法,那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法
-
-
-
优点:
- 可以注入不可变对象
- 注入对象不会被修改
- 可以加final
- 构造函数只会在类加载的时候执行一次
Setter 注⼊
-
Setter 注⼊和属性的 Setter ⽅法实现类似
-
只不过在设置 set ⽅法的时候需要加上 @Autowired 注解,如下代码所示:
-
@Component public class Student { private int id; private User user; @Autowired public void setUser(User user) { this.user = user; } }
-
-
-
缺点:
-
不可注入不可变对象
-
注入对象可被修改
-
@Autowired public void setUser(User user) { this.user = user; this.user = null; }
-
-
三种注⼊优缺点分析
- 属性注⼊的优点是简洁,使⽤⽅便;
- 缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)
- 构造⽅法注⼊是 Spring 推荐的注⼊⽅式
- 它的缺点是如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了
- 它的优点是通⽤性,在使⽤之前⼀定能把保证注⼊的类不为空
- Setter ⽅式是 Spring 前期版本推荐的注⼊⽅式,但通⽤性不如构造⽅法
- 所有 Spring 现版本已经推荐使⽤构造⽅法注⼊的⽅式来进⾏类注⼊了
@Resource:另⼀种注⼊关键字
-
可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下代码所示
-
@Component public class Student { private int id; @Resource private User user; }
-
-
@Autowired 和 @Resource 的区别
- 相同点:
- 都是用来实现依赖注入的注释
- 不同:
- 出身不同:
- @Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解;
- 使⽤时设置的参数不同:
- 相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如name 设置,根据名称获取 Bean。
- @Autowired 可⽤于 Setter 注⼊、构造函数注⼊和属性注⼊
- ⽽ @Resource 只能⽤于 Setter 注⼊和属性注⼊,不能⽤于构造函数注⼊。
- 出身不同:
- 相同点:
同⼀类型多个 Bean 报错处理
-
解决同⼀个类型,多个 bean 的解决⽅案有以下两个:
-
使⽤ @Resource(name=“user1”) 定义。
-
@Component public class Student { private int id; @Resource(name = "user1") private User user; }
-
-
使⽤ @Qualifier 注解定义名称。
-
@Component public class Student { private int id; @Autowired @Qualifier(value = "user1") private User user; }
-
-
获取bean对象的几种方式
-
根据名称获取Bean
-
根据Bean类型来获取Bean
-
根据Bean名称 + Bean类型来获取Bean
-
User user = (User) context.getBean("user"); //通过 id User user1 = context.getBean(User.class); // 通过类名.class User user2 = context.getBean("user", User.class); // 不需要强转了
-
ApplicationContext VS BeanFactory
- 继承关系和功能⽅⾯来说:Spring容器有两个顶级的接⼝:
- BeanFactory和ApplicationContext。其中BeanFactory提供了基础的访问容器的能⼒
- ⽽ApplicationContext属于BeanFactory的⼦类,它除了继承了BeanFactory的所有功能之外
- 它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持。
- 从性能⽅⾯来说:
- ApplicationContext是⼀次性加载并初始化所有的Bean对象,⽽BeanFactory是需要那个才去加载那个,因此更加轻量。
Bean 作⽤域
- Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。
- Spring有 6 种作⽤域,最后四种是基于 Spring MVC ⽣效的:
- singleton:单例作⽤域
- prototype:原型作⽤域(多例作⽤域)
- request:请求作⽤域
- session:回话作⽤域
- application:全局作⽤域
- websocket:
- HTTP WebSocket 作⽤
注意后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项⽬中只有前两种
singleton
-
描述:
- 该作⽤域下的Bean在IoC容器中只存在⼀个实例:
- 获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。
- 该作⽤域下的Bean在IoC容器中只存在⼀个实例:
-
场景:
- 通常⽆状态的Bean使⽤该作⽤域。
- ⽆状态表示Bean对象的属性状态不需要更新
-
备注:
- Spring默认选择该作⽤域
prototype
-
描述:
- 每次对该作⽤域下的Bean的请求都会创建新的实例:
- 获取Bean(即通过applicationContext.getBean等⽅法获取)
- 及装配Bean(即通过@Autowired注⼊)都是新的对象实例
- 每次对该作⽤域下的Bean的请求都会创建新的实例:
-
场景:通常有状态的Bean使⽤该作⽤域
request
-
描述:
- 每次http请求会创建新的Bean实例,类似于prototype
-
场景:
- ⼀次http的请求和响应的共享Bean
-
备注:限定SpringMVC中使⽤
session
- 描述:在⼀个http session中,定义⼀个Bean实例
- 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
- 备注:限定SpringMVC中使⽤
application(了解)
- 描述:在⼀个http servlet Context中,定义⼀个Bean实例
- 一个上下文对象共享
- 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
- 备注:限定SpringMVC中使⽤
websocket(了解)
- 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
- 场景:
- WebSocket的每次会话中
- 保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。
- 第⼀次初始化后,直到WebSocket结束都是同⼀个Bean
- WebSocket的每次会话中
单例作⽤域(singleton) VS 全局作⽤域(application)
- singleton 是 Spring Core 的作⽤域;
- application 是 Spring Web 中的作⽤域;
- singleton 作⽤于 IoC 的容器
- ⽽ application 作⽤于 Servlet 容器
设置作⽤域
-
使⽤ @Scope 标签就可以⽤来声明 Bean 的作⽤域
-
⽐如设置 Bean 的作⽤域,如下代码所示:
-
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class User { public String Hi(){ return "哈你妈个头"; } }
-
-
-
@Scope 标签既可以修饰⽅法也可以修饰类,@Scope 有两种设置⽅式:
- 直接设置值:@Scope(“prototype”)
- 使⽤枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Spring 执⾏流程
-
Bean 执⾏流程(Spring 执⾏流程):
- 启动容器(项目)
- 读取配置文件
- 使用xml直接注册bean
- 配置bean根(扫描)路径
- 将bean存储到 spring中
- 通过类注解进行扫描和装配
- 将bean从spring中读出,配置相应的类
Bean的生命周期
-
所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期。
-
Bean 的⽣命周期分为以下 5 ⼤部分:
-
实例化 Bean(为 Bean 分配内存空间)
- 将字节码转换为内存中的对象
- 调用构造函数
-
设置属性(Bean 注⼊和装配)
- 设置类的属性
-
Bean 初始化
-
实现了各种 Aware 通知的⽅法
- 如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接⼝⽅法;
-
执⾏ BeanPostProcessor 初始化前置⽅法;
-
执⾏ @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏;
-
@PostConstruct public void init() { // Initialization logic System.out.println("Bean is being initialized..."); }
-
-
执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);
-
<bean id="myBean" class="com.example.MyBean" init-method="init"> <!-- Other bean configurations --> </bean>
-
-
执⾏ BeanPostProcessor 初始化后置⽅法
-
-
使⽤ Bean
-
销毁 Bean
- 销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method
-
-
执⾏流程如下图所示