什么是IOC与DI

IOC(inversion of control) 它描述的其实是一种面向对象编程中的设计原则,用来降低代码之间的耦合度, 而DI(dependency Injection)依赖注入是spring对IOC编程思想的实现

spring的编程风格

  • 基于xml配置文件维护bean之间的关系
  • 基于注解维护bean之间的关系
  • 基于javaconfig维护bean之间的关系

IOC中注入Bean的两种方式

方式1: 配置文件+setter方法

配置文件

  1. <bean id="dao" class="com.changwu.tryspring.FirstDaoImpl"></bean>
  2. <bean id="service" class="com.changwu.tryspring.service">
  3. <property name="dao" ref="dao"></property>
  4. </bean>

java类中提供构造方法

  1. public class service {
  2. FirstDaoImpl dao;
  3. public void say(){
  4. dao.say();
  5. }
  6. // 注意方式1: 提供setter
  7. public void setDao(FirstDaoImpl dao) {
  8. this.dao = dao;
  9. }
  10. }

方式2: 配置文件+构造方法注入

配置文件

  1. <bean id="dao" class="com.changwu.tryspring.FirstDaoImpl">
  2. </bean>
  3. <bean id="service" class="com.changwu.tryspring.service">
  4. <constructor-arg ref="dao"></constructor-arg>
  5. </bean>

java类,提供构造方法

  1. public class service {
  2. FirstDaoImpl dao;
  3. // 方式2 构造方法
  4. public service(FirstDaoImpl dao) {
  5. this.dao = dao;
  6. }
  7. public void say(){
  8. dao.say();
  9. }
  10. }

方式3: 基于注解注入Bean

在配置文件中配置开启注解模式

在spring4中,仅仅保存下面的一行配置信息,(它涵盖了开启注解和包扫描两层语义)

  1. <!-- 配置包扫描的路径 -->
  2. <context:component-scan base-package="com"></context:component-scan>

虽然现在可以开启注解了,但是xml文件还在,下面演示使用java-basic方式完成注入bean,完全抛弃xml

通过这个配置类完全舍弃xml

  1. @Configuration
  2. @ComponentScan("com.changwu.tryspring")
  3. public class SpringConfiguration {
  4. }

启动类

  1. public class MainTest {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
  4. service bean = applicationContext.getBean(service.class);
  5. bean.say();
  6. }
  7. }

annotaion + xml + java-basic 混合使用的实现方式:

在javaConfiguration类上使用@ImportResources("classpath:spring.xml")

  1. @Configuration
  2. @ComponentScan("com.changwu.tryspring")
  3. @ImportResource("classpath:spring.xml")
  4. public class SpringConfiguration {
  5. }

自动装配的优势参考

就像上面那样, 本来我们的java代码中就已经存在了类之间的关系,但是还得通过xml文件的方式再重复描述一遍类之间的关系,这本来就是很鸡肋,

自动装配模式就帮我们省去了在xml中描述类之间的关系这一步

有了自动装配,当对象中添加或者减少某些属性时,不用更改 xml文件中 标签下的 等等...

基于xml版的自动装配的五种模式

no & default

不使用自动装配,需要我们手动装配,否则报错

byType

常用的根据类型的自动注入

我的java类

然后这样写配置文件

程序一开始运行他就去读取配置文件,根据配置文件创建出相应的bean的代理对象放到IOC中但是,虽然我没有明确的指出service依赖于FirstDaoImpl,程序会正常运行因为我添加了default-autowired=byType,他会替我将FirstDaoImpl装配进service

这是根据类型的自动装配,如果是我们像下面的配置一样,添加多个FirstDaoImpl,他就会报错: 说同一个类型的对象找到了两个,不知道使用哪个

byName

还是上面的场景,使用byName的自动装配,当程序发现service依赖FirstDaoImpl时,会根据名字在IOC中查询出指定的bean,完成依赖注入,

那使用的什么名字呢? 就是我们在service类中提供的setter方法去掉开头的set然后首字母小写, 默认情况下,name和id相同

constructor

补充

byName,ByType等这些自动装配的配置还可以单独的配置在某一个<bean>上,如下

  1. <bean id="service" class="com.changwu.tryspring.service" autowire="byType">
  2. <!--<constructor-arg ref="dao"></constructor-arg>-->
  3. </bean>

注解版本的自动装配

  • @Autowired 默认使用的自动装配类型为 Autowired_No

    • 但是还是会通过byType查找, 如果没有找到,就使用byName , 什么name呢? 就是属性名
  • @Resource 默认采用的自动装配类型为byName,和xml版本不同的是,它是根据属性名完成的注入,而xml版本的name为setter方法去掉set后首字母小写的英文单词
    • @Resource(name = "XXX") 明确指定按照名字注入, XXX是什么呢? 其实是类名首字母小写
    • @Resource(type=A.class) 就是明确指定注入A.class

使用注解的方式完成依赖注入

举个例子, 通过@Bean 往IOC中注入对象时,如果这个对象还依赖其他的对象,可以使用下面的构造函数的方式注入进去

  1. public class AppConfig {
  2. @Bean
  3. public TransferService transferService(AccountRepository accountRepository) {
  4. return new TransferServiceImpl(accountRepository);
  5. }
  6. @Bean
  7. public AccountRepository accountRepository(AccountRepository accountRepository) {
  8. return new AccountRepository();
  9. }
  10. }

Spring中的循环依赖

如果Spring中的Bean的单例的,可以存在循环引用,因为在扫描包时,会把扫描的到的bean实例化到一个缓冲区,然后他们就可以相互引用了,但是

如果是prototype多例的bean,容器创建之初,不进行初始化,因此循环引用失败

Spring的懒加载

默认情况下,spring上下文中的bean都是单例的,并且spring在加载上下文时,就会初始化所有的单实例bean.因为这样做可以立即发现环境中的

错误,我们也可以配置懒加载,也就是说在第一次使用bean时对bean进行初始化的工作

在xml中可以这样配置

  1. <bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
  2. <bean name="not.lazy" class="com.something.AnotherBean"/>

或者

  1. <beans default-lazy-init="true">
  2. <!-- no beans will be pre-instantiated... -->
  3. </beans>

或者使用注解 @Lazy

SpringBean的作用域

默认是singleton

基于xml配置文件配置单例

  1. <bean id="accountService" class="com.something.DefaultAccountService"/>
  2. <!-- the following is equivalent, though redundant (singleton scope is the default) -->
  3. <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

基于注解:@Scope 默认单例

  • 多例prototype

bean被配置成多例,每次从IOC中获取都将获取一个新的对象,并且容器不负责该对象的销毁

此外还有四种

  • request
  • session
  • application
  • websocket

拓展-多例失效问题

当单例bean中引用了多例bean时,默认情况下多实例bean的多实例会失效

例:多例bean代码如下

  1. @Component
  2. @Scope("prototype")
  3. public class FirstDaoImpl implements FirstDao {
  4. @Override
  5. public void say() {
  6. System.out.println("hello");
  7. }
  8. }

单例bean如下

  1. @Component
  2. public class service {
  3. @Autowired
  4. FirstDaoImpl dao;
  5. public void say(){
  6. System.out.println("this.hashcode = "+this.hashCode());
  7. System.out.println("dao.hashcode = "+dao.hashCode());
  8. // dao.say();
  9. }

主类:

  1. public class MainTest {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
  4. service bean = applicationContext.getBean(service.class);
  5. bean.say();
  6. service bean1 = applicationContext.getBean(service.class);
  7. bean1.say();
  8. service bean2 = applicationContext.getBean(service.class);
  9. bean2.say();
  10. }

打印的结果中可以看到,单例的service和多例的dao中的一个实例进行了唯一的绑定,换句话说是多例失效,如果项目真的需要dao是

多例的话,就会产生并发问题

  1. this.hashcode = 1032000752
  2. dao.hashcode = 770911223
  3. this.hashcode = 1032000752
  4. dao.hashcode = 770911223
  5. this.hashcode = 1032000752
  6. dao.hashcode = 770911223
  • 解决方法1

实现ApplicationContextAware接口,通过这个接口可以获取到Spring的应用上下文,进而通过getBean()随意获取IOC的bean

  1. @Component
  2. public class service implements ApplicationContextAware {
  3. private ApplicationContext applicationContext;
  4. public void say(){
  5. System.out.println("this.hashcode = "+this.hashCode());
  6. System.out.println("dao.hashcode = "+applicationContext.getBean("firstDaoImpl").hashCode());
  7. }
  8. @Override
  9. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  10. this.applicationContext = applicationContext;
  11. }
  12. }
  • 解决方法2

使用@lookup注解

  1. @Component
  2. public class service {
  3. /* @Autowired
  4. FirstDaoImpl dao;*/
  5. @Lookup
  6. public FirstDaoImpl getFirstDaoImpl(){
  7. return null;
  8. }
  9. public void say(){
  10. System.out.println("this.hashcode = "+this.hashCode());
  11. System.out.println("dao.hashcode = "+getFirstDaoImpl().hashCode());
  12. }

或者

  1. @Component
  2. public abstract class service {
  3. @Lookup
  4. public abstract FirstDaoImpl getFirstDaoImpl();
  5. public void say(){
  6. System.out.println("this.hashcode = "+this.hashCode());
  7. System.out.println("dao.hashcode = "+getFirstDaoImpl().hashCode());
  8. }

方法名字随便取,返回值没差就行

springBean的生命周期和回调

  • 实现Spring提供的InitializingBean接口
  • 通过xml配置
  • 通过注解完成

在构造方法完成后的回调钩子

方式1: 实现InitializingBean接口

  1. public class AnotherExampleBean implements InitializingBean {
  2. @Override
  3. public void afterPropertiesSet() {
  4. // do some initialization work
  5. }
  6. }

方式2: 配置xml

  1. public class DefaultBlogService implements BlogService {
  2. private BlogDao blogDao;
  3. public void setBlogDao(BlogDao blogDao) {
  4. this.blogDao = blogDao;
  5. }
  6. // this is (unsurprisingly) the initialization callback method
  7. public void init() {
  8. if (this.blogDao == null) {
  9. throw new IllegalStateException("The [blogDao] property must be set.");
  10. }
  11. }
  12. }

配置default-init-method如下

  1. <beans default-init-method="init">
  2. <bean id="blogService" class="com.something.DefaultBlogService">
  3. <property name="blogDao" ref="blogDao" />
  4. </bean>
  5. </beans>

或者配置init-method

  1. <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

方式3: 添加注解

@PostConstruct

容器销毁前的回调

方式1: 实现接口

  1. 实现这个接口
  2. org.springframework.beans.factory.DisposableBean
  3. 重写它的 void destroy() throws Exception;

方式2: 通过xml配置

  1. <bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

方式3: 添加注解

@PreDestroy

如果为一个对象的用不同的方式添加了不同声明周期回调钩子,并且回调的方法名还不一样**,那么这些会钩子按顺序依次回调,

方法名一样的话,回调一次, 回调的顺序如下

initialization methods are called as follows

  1. Methods annotated with @PostConstruct

  2. afterPropertiesSet() as defined by the InitializingBean callback interface

  3. A custom configured init() method

Destroy methods are called in the same order:

  1. Methods annotated with @PreDestroy

  2. destroy() as defined by the DisposableBean callback interface

  3. A custom configured destroy() method


Spring IOC 笔记的更多相关文章

  1. Spring 学习笔记 IoC 基础

    Spring IoC Ioc 是什么 IoC -- Inversion of Control(控制反转)什么是控制?什么是反转? 控制反转了什么? 在很早之前写项目不用 Spring 的时候,都是在 ...

  2. Spring IOC(控制反转)思想笔记

    Spring IOC(控制反转)思想笔记 IOC控制反转基本理念就是将程序控制权从程序员手中交给用户自定义,从而避免了因为用户一个小需求的变化使得程序员需要改动大量代码. 案例 如果按照之前javaw ...

  3. Spring Framework 学习笔记——核心技术之Spring IOC

    Spring Framework 官网文档学习笔记--核心技术之Spring IOC 官方文档 spring-framework-5.3.9 1. Spring Framework 核心技术 1.1 ...

  4. spring笔记6 spring IOC的中级知识

    1,spring ioc的整体流程,xml配置 spring ioc初始化的流程结合上图 步骤编号 完成的工作 1 spring容器读取配置文件,解析称注册表 2 根据注册表,找到相应的bean实现类 ...

  5. Spring学习笔记之三----基于Annotation的Spring IOC配置

    使用Annotation 来创建Bean有两种方式 在配置类中创建bean(配置类是指标注为@Configuration的类),在配置类中每一个创建bean的方法都应该标注为@Bean,可以在@Bea ...

  6. Spring Framework 笔记(一):IoC

    一:Spring中重要的概念 1. 容器( container ) : spring容器( ApplicationContext )的工作原则是创建容器中的组件( instance ),处理组件之间的 ...

  7. Spring学习笔记(二)Spring基础AOP、IOC

    Spring AOP 1. 代理模式 1.1. 静态代理 程序中经常需要为某些动作或事件作下记录,以便在事后检测或作为排错的依据,先看一个简单的例子: import java.util.logging ...

  8. [跟我学spring学习笔记][IoC]

    IoC基础 什么是IoC Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想. ioc做什么 IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找 ...

  9. Spring Boot笔记十:IOC控制反转

    目录 IOC控制反转和DI依赖注入 IOC实现Hello World Spring IOC容器怎么知道哪些是管理的对象? IOC容器getBean方法的三种签名 xml配置文件的import导入 @A ...

随机推荐

  1. Django-View中绕过RSCF验证

    在Django中对于基于函数的视图我们可以 @csrf_exempt 注解来标识一个视图可以被跨域访问.那么对于基于类的视图,我们应该怎么办呢? 简单来说可以有两种访问来解决 方法一:在类的 disp ...

  2. moviepy音视频剪辑:使用concatenate_videoclips和clips_array将多个视频合成一个顺序播放或同屏播放的视频

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一.视频合成概述 视频合成,也称为非线性编辑,实际 ...

  3. 老猿学5G随笔:5G核心网中与用户数据相关的NF功能体UDM、AUSF、PCF、UDR

    在业务支撑工作中,与核心网主要的交互包括用户数据管理(含签约关系.策略数据),5G核心网中与用户数据相关的NF功能体包括UDM.AUSF和PCR以及UDR,在此只简单介绍这些NF的功能: UDM:统一 ...

  4. moviepy应用pyinstaller打包后执行报错AttributeError: module audio/video.fx.all has no attribute fadein、crop

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 在开发moviepy的Python程序使用pyinstaller打包后 ...

  5. PyQt(Python+Qt)学习随笔:QMainWindow的tabifyDockWidget方法将QDockWidget两个停靠窗选项卡式排列

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 主窗口的tabifyDockWidget方法用于将主窗口的两个停靠窗口 ...

  6. Python的富比较方法__eq__和__ne__之间的关联关系分析

    Python的富比较方法包括__lt__.__gt__.__le__.__ge__.__eq__和__ne__六个方法,分别表示:小于.大于.小于等于.大于等于.等于和不等于,对应的操作运算符为:&l ...

  7. 第8.34节 《Python类中常用的特殊变量和方法》总结

    本章介绍了Python类中常用的特殊变量和方法,这些特殊变量和方法都有特殊的用途,是Python强大功能的基石之一,许多功能非常有Python特色.由于Python中一切皆对象,理解这些特殊变量和方法 ...

  8. Asp.NetCore之AutoMapper进阶篇

    应用场景 在上一篇文章--Asp.NetCore之AutoMapper基础篇中我们简单介绍了一些AutoMapper的基础用法以及如何在.NetCore中实现快速开发.我相信用过AutoMapper实 ...

  9. 蒲公英 · JELLY技术周刊 Vol.33: 前端基础课堂开课啦~

    蒲公英 · JELLY技术周刊 Vol.33 页面文件太大?图片过大了吧:页面加载白屏?很有可能是字体文件还没加载完:页面加载时间过长?多半是主进程被阻塞--该怎么办呢?快来小葵,咳咳,「蒲公英」前端 ...

  10. luogu P6835 概率DP 期望

    luogu P6835 概率DP 期望 洛谷 P6835 原题链接 题意 n + 1个节点,第i个节点都有指向i + 1的一条单向路,现在给他们添加m条边,每条边都从一个节点指向小于等于自己的一个节点 ...