1、获取bean实例的三种方式

1.1 id 属性

1.1.1 jar

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.18.RELEASE</spring.version>
<lombok.version>1.16.18</lombok.version>
<junit.version>4.11</junit.version> </properties> <dependencies>
<!-- spring-beans begin -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring-beans end --> <!-- spring-core begin -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring-core end --> <!-- spring-context begin -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring-context end --> <!-- spring-expression begin -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring-expression end --> </dependencies>

1.1.2 application.xml

<?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"> <!-- 基于spring的核心配置文件方式,手动配置一个bean,放到spring的容器中 -->
<bean id="userOne" class="com.kgc.spring.bean.User">
<property name="nickName" value="kh96_spring_one"></property>
</bean>
</beans>

<bean>标签:定义一个实例对象,会自动被创建 并 交给spring容器进行管理:

  • id 属性:被spring容器进行管理的实例的 唯一标识,整个容器中,只能是唯一的(不可重复);

  • class属性:指定创建当前实例对象的类型(全类名),spring底层是使用的 反射机制 ,根据指定的目标类型,创建目标实例(必须有 空参构造)

  • <property>子标签:给当前创建对象的属性值

    • name:指定对象的属性名
    • value: 给属性赋值

1.1.3 测试

@Test
public void testSpringPrimer(){
//1.创建spring的核心容器对象(引用上下文对象),解析spring的核心配置文件,将文件中的bean标签进行实例化(创建对象并赋值)
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //2. 写法一: 从容器中获取实例对象,根据核心配置文件中,配置的bean标签的id属性值
// 不足:强制转化,容易出现转换错误
User userOne = (User) context.getBean("userOne"); userOne.sayHello(); //对比以前创建对象的写法(对象必须手动new创建,并手动赋值)
User userOld = new User();
userOld.setNickName("kh96_old");
userOld.sayHello(); }

1.1.4 总结

通过id属性,获取实例对象后需要强制转换,容易出现强制转化错误

1.2 class 属性

1.2.1 测试

//写法二:从容器中获取实例对象,根据核心配置文件中,配置的bean标签的class属性指定的目标类型
User userTwo = context.getBean(User.class); userTwo.sayHello();

1.2.2 总结

不足:如果存在同类型的多个实例,会报异常

1.2.2.1 application.xml

当用多个同类型的实例:

...
<bean id="userOne" class="com.kgc.spring.bean.User">
<property name="nickName" value="kh96_spring_one"></property>
</bean> <!-- Configuration problem: Bean name 'userOne' is already used in this <beans> element -->
<bean id="userTwo" class="com.kgc.spring.bean.User">
<property name="nickName" value="kh96_spring_two"></property>
</bean>
...
1.2.2.2 测试
User userTwo = context.getBean(User.class);

会发生异常:

NoUniqueBeanDefinitionException: No qualifying bean of type 'com.kgc.spring.bean.User' available: expected single matching bean but found 2: userOne,userTwo

主要原因是由于只用class属性去获取实例,但是有多个相同类型的实例,所以无法确定你要获取的是哪一个

1.3 id 属性 和 class 属性 (推荐)

1.3.1 测试

//写法三:从容器中获取实例对象,根据配置文件中,配置的bean标签的id属性值和class属性指定的目标类型
User userTwoThree = context.getBean("userTwo", User.class);

1.3.2 总结

能够确定唯一实例,推荐使用;

1.4 id 不能重复注意点

配置文件中的id必须是唯一的;

如果id不唯一:两个id一样的实例

...
<bean id="userOne" class="com.kgc.spring.bean.User">
<property name="nickName" value="kh96_spring_one"></property>
</bean>
<bean id="userOne" class="com.kgc.spring.bean.User">
<property name="nickName" value="kh96_spring_one"></property>
</bean>
...

报错:

BeanDefinitionParsingException: Configuration problem: Bean name 'userOne' is already used in this <beans> element

提示 id 为 userOne 的实例已经存在;

1.5 实例化时机

初始化spring的容器对象时,会将核心配置文件中所有的bean实例化,不是使用哪个,创建哪个;

2、DI

IOC(控制反转是一种思想),DI是IOC的一种实现方式;

2.1 set 方式注入

2.1.1 实体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User { //用户昵称
//普通参数
private String nickName; //私家车
//实体参数
private Car car; //喜欢的书
//数组参数
private String[] books; //爱好
//list集合参数
private List<String> hobbies; //喜欢的游戏
//set 集合
private Set<String> games; //卡包
//map参数
private Map<String,String> cards; //在校信息
//properties参数
private Properties info; //是否有女朋友 (主要是演示赋值为null,其他类型都可以)
//空参数注入
private Boolean wife; }

2.1.2 参数 注入

...
<!-- 不同类型参数的set注入 -->
<bean id="user01" class="com.kgc.spring.bean.User">
<!-- 1.普通参数 注入 -->
<property name="nickName" value="小气鬼"></property>
<!-- 2.实体参数 注入 -->
<!-- 2.1 实体参数 注入方式一 外部引入 ref -->
<!-- <property name="car" ref="carOne"></property>-->
<property name="car">
<!-- 2.2 实体参数注入方式二 内部注入 <bean> -->
<bean class="com.kgc.spring.bean.Car">
<property name="brand" value="Bmw325"></property>
<property name="factory" value="华晨"></property>
<property name="price" value="300000"></property>
</bean>
</property>
<!-- 3.数组参数 注入 -->
<property name="books">
<array>
<value>红楼梦</value>
<value>水浒传</value>
<value>三国演义</value>
<value>西游记</value>
</array>
</property>
<!-- 4.list集合参数 -->
<property name="hobbies" >
<list>
<value>跑步</value>
<value>尤克里里</value>
<value>敲代码</value>
</list>
</property>
<!-- 5.set 集合参数 注入 -->
<property name="games">
<set>
<value>唱歌</value>
<value>跳舞</value>
<value>喝酒</value>
</set>
</property>
<!-- 6.map参数 -->
<property name="cards">
<map>
<entry key="身份证" value="1212121212121212"></entry>
<entry key="银行卡" value="1111111111111111"></entry>
</map>
</property>
<!-- 7.properties参数 -->
<property name="info">
<props>
<prop key="学号">123456</prop>
<prop key="性别">男</prop>
<prop key="姓名">化羽</prop>
</props>
</property>
<!-- 8.空参数注入 -->
<property name="wife">
<null></null>
</property>
</bean> <bean id="carOne" class="com.kgc.spring.bean.Car">
<property name="brand" value="Bmw325"></property>
<property name="factory" value="华晨"></property>
<property name="price" value="300000"></property>
</bean>
...

2.1.3 测试

@Test
public void testPramsDI(){
User user01 = context.getBean("user01", User.class);
//输入对象详情
System.out.println(user01);
}

输出结果:

User(
nickName=小气鬼,
car=Car(brand=Bmw325, factory=华晨, price=300000.0),
books=[红楼梦, 水浒传, 三国演义, 西游记],
hobbies=[跑步, 尤克里里, 敲代码],
games=[唱歌, 跳舞, 喝酒],
cards={身份证=1212121212121212, 银行卡=1111111111111111},
info={学号=1913001072, 性别=男, 姓名=化羽},
wife=null
)

2.2 Constructor 构造器注入

2.2.1 按照默认顺序 注入

2.2.1.1 参数注入
<!--
实体构造方法:Car(String brand, String factory, Double price)
-->
<!-- 构造器注入:通过构造方法,默认是按照构造方法的参数定义顺序赋值 -->
<bean id="carTwo" class="com.kgc.spring.bean.Car">
<constructor-arg value="AudiA4"></constructor-arg>
<constructor-arg value="一汽"></constructor-arg>
<constructor-arg value="320000"></constructor-arg>
</bean>
2.2.1.2 测试
@Test
public void testSpringDIConstructor(){
Car carTwo = context.getBean("carTwo", Car.class); //输出对象详情
System.out.println(carTwo);
}

2.2.2 根据 参数的下标 和 类型 注入

2.2.2.1 参数注入
<!-- 根据构造器中参数的下标 和 类型 赋值 -->
<bean id="carThree" class="com.kgc.spring.bean.Car">
<constructor-arg index="0" value="BenzC200"></constructor-arg>
<constructor-arg index="1" value="北京"></constructor-arg>
<constructor-arg index="2" type="java.lang.Double" value="350000"></constructor-arg>
<!--
如果参数列表中,该类型的参数只用一个,也可以只指定参数类型
<constructor-arg type="java.lang.Double" value="350000"></constructor-arg>
-->
</bean>
2.2.2.2 测试
@Test
public void testSpringDIConstructor2(){
Car carTwo = context.getBean("carThree", Car.class); //输入对象详情
System.out.println(carTwo);
}

2.3 自定义 实体工厂bean

自定义实体工厂bean ,必须实现FactoryBean接口

普通bean 与 工厂bean 的区别:

  • 普通 bean:在配置文件中定义 bean 类型 就是 返回类型
  • 工厂 bean:在配置文件定义 bean 类型返回类型 不一样

2.3.1 返回bean

@Data
@ToString
public class Car {
/*
品牌
*/
private String brand; /*
厂商
*/
private String factory; /*
价格
*/
private Double price;
}

2.3.2 实体工厂 bean

  • spring容器初始化时,创建当前工厂bean的实例对象,但是真实返回不是当前类的实例对象而是当前类的实例对象返回的目标实例对象(自定义);
  • 作用:可以在程序中,实现自定义创建实例(还可以增加业务逻辑处理),放入容器;
  • 存在的原因:spring框架对外开放了一个入口,可以让其他的开发人员参与到spring底层创建bean的实例过程中去给整合其他框架使用的,比如mybatis;
public class CarFactoryBean implements FactoryBean<Car> {
@Override
public Car getObject() throws Exception {
System.out.println("通过CarFactoryBean的实例对象的getObject方法:返回一个自定义的car的实例");
return new Car("Byd唐","南京",2500000.0);
} @Override
public Class<?> getObjectType() {
//指定给Object方法返回的目标类型
return Class.class;
} @Override
public boolean isSingleton() {
//是否单例
return false;
}
}

2.3.3 工厂bean容器中添加自定义实例对象

...
<!-- 使用工厂bean容器,添加自定义实例对象 -->
<bean id="carFactoryBean" class="com.kgc.spring.bean.CarFactoryBean"></bean>
...

2.3.4 测试

@Test
public void testSpringFactoryBean(){
//从容器中获取工厂bean的实例对象
Car car1 = context.getBean("carFactoryBean", Car.class);
Car car2 = context.getBean("carFactoryBean", Car.class); //输出对象详情
System.out.println(car 1);
//在容器中添加的实例是 CarFactoryBean 类型,返回的是
//Car(brand=Byd唐, factory=南京, price=2500000.0)
System.out.println("car1 == car2 : " + ( car1 == car2));
//false 说明工厂bean是多例的 }

3、scope作用域

  • singleton 单例 (默认) 容器初始化之前创建;
  • prototype 多例 (手动设置) 使用到的时候才创建;

3.1 singleton 单例

这里区别于整个程序运行期间有且只有唯一的实例(单例模式-懒汉和饿汉);

容器中bean的单例不是指当前类的实例在容器中,只有唯一的实例,而是当创建bean的实例时,此实例是单例(容器内唯一),但是同一个类的实例,容器中可以创建多个每个都是单例的

3.1.1 配置bean

<!-- scope="singleton" 不写默认也是单例的  -->
<bean id="busTwo" class="com.kgc.spring.bean.Bus" scope="singleton">
<property name="brand" value="金龙2"></property>
<property name="factory" value="厦门2"></property>
<property name="price" value="1200000"></property>
</bean>

3.1.2 测试

@Test
public void testScopeSingleton(){ //从容器中,获取Bus的实例对象
Bus busThree = context.getBean("busTwo", Bus.class);
Bus busFour = context.getBean("busTwo", Bus.class); System.out.println(busThree);
System.out.println(busFour); System.out.println("singleton busThree == busFour:"+(busThree == busFour));
//true
}

3.2 prototype 多例

多例:prototype,不是spring容器中的默认作用,需要单独指定

spring容器创建时,不会自动创建指定作用域为多例的bean的实例,而是每次通过getBean方法获取bean的实例,才会创建bean的实例

3.2.1 配置bean

<!--  scope="prototype" 多例需要手动设置 -->
<bean id="busThree" class="com.kgc.spring.bean.Bus" scope="prototype">
<property name="brand" value="中通3"></property>
<property name="factory" value="山东3"></property>
<property name="price" value="1200000"></property>
</bean>

3.2.2 测试

@Test
public void testScopePrototype(){
Bus busOne = context.getBean("busThree", Bus.class);
Bus busTwo = context.getBean("busThree", Bus.class); //输入详情
System.out.println(busOne);
System.out.println(busTwo); System.out.println("prototype busOne == busTwo:"+(busOne == busTwo));
//false }

Spring(一)- 初始 + DI+scope的更多相关文章

  1. Spring中bean的scope详解

    如何使用spring的作用域: <bean id="role" class="spring.chapter2.maryGame.Role" scope=& ...

  2. Spring系列之DI的原理及手动实现

    目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 前言 在上一章中,我们介绍和简单实现了容器的部分功能,但是这里还留下了很多的问题.比如我们在构造bean实例的时 ...

  3. Spring IoC 和 DI 简介(二)

    Spring IoC 和 DI 简介 IoC:Inverse of Control(控制反转) 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由 ...

  4. java:Spring框架1(基本配置,简单基础代码模拟实现,spring注入(DI))

    1.基本配置: 步骤一:新建项目并添加spring依赖的jar文件和commons-logging.xx.jar: 步骤二:编写实体类,DAO及其实现类,Service及其实现类; 步骤三:在src下 ...

  5. 如何快速理解Spring中的DI和AOP

    前言 Spring框架通过POJO最小侵入性编程.DI.AOP.模板代码手段来简化了Java 开发,简化了企业应用的开发.POJO和模板代码相对来说好理解,本篇重点解读下DI和AOP. 一 DI DI ...

  6. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  7. Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的?

    Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的? 在asp.net core中的DI生命周期有一个Scoped是根据请求走的,也就是说在处理一次请求时, ...

  8. Spring框架-IOC/DI详细学习

    一.IOC/DI概念 参考博客:https://www.cnblogs.com/xdp-gacl/p/4249939.html IOC(inversion of control, 控制反转)是一种设计 ...

  9. spring的IOC/DI功能实践

    一.写在前面: 做这个Demo主要是为了能够更好的理解Spring的原理,看再多的文章,听再多的讲解最终都最好自己去实现一遍,可以将Spring的功能分块实现,最终自然比较容易将各个功能组合起来. 这 ...

随机推荐

  1. Linux Cgroup v1(中文翻译)(2):CPUSETS

    英文原文:https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cpusets.html Copyright (C) 2004 BU ...

  2. mybatis-plus对空字段 时间进行填充

    package com.tanhua.sso.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; imp ...

  3. Java 多线程共享模型之管程(下)

    共享模型之管程 wait.notify wait.notify 原理 Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态 BLOCKED 和 W ...

  4. Apache Poi 操作word,替换字符保留样式问题,runs段落混乱问题。

    关于这个问题也是刚好遇到,一通搜索也没有找到类似的或者是有效的方法.下面介绍一下. 首先apache poi的引入 <dependency> <groupId>org.apac ...

  5. redisson之分布式锁实现原理(三)

    官网:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 一.什么是分布式锁 1.1.什么是分布式锁 分布式锁,即分布式系统中的锁 ...

  6. SAP APO-供应链监控

    供应链监控(SCM)是一种有效的工具,可以为供应商和购买者做出明智的决策. 供应链流程中的关键人物基于供应链监控做出决策. 供应链包括两个工具- 库存需求清单MD04 物流信息系统(LIS) 事务MD ...

  7. go-zero微服务实战系列(七、请求量这么高该如何优化)

    前两篇文章我们介绍了缓存使用的各种最佳实践,首先介绍了缓存使用的基本姿势,分别是如何利用go-zero自动生成的缓存和逻辑代码中缓存代码如何写,接着讲解了在面对缓存的穿透.击穿.雪崩等常见问题时的解决 ...

  8. VisionPro · C# · 图像显示十字光标

    程序通过 CogRecordDisplay 显示控件显示视觉运行结果图像,当我们对调试时,可能需要用到图像中心十字对位光标. 本文通过VisionPro两个拟合线工具,一个拟合圆工具在图像中画出光标, ...

  9. CF487E Tourists 题解

    题目链接 思路分析 看到这道题首先想到的此题的树上版本.(不就是树链剖分的板子题么?) 但是此题是图上的两点间的走法,自然要想到是圆方树. 我们先无脑构建出圆方树. 我们先猜测:设后加入的节点权值为 ...

  10. JAVA中计算两个日期时间的差值竟然也有这么多门道

    上半年春招的时候,作为面试官,对于面试表现的不错的同学会要求其写一小段代码看看.题目很简单: 给定一个日期,然后计算下距离今天相差的天数. 本以为这么个问题就是用来活跃面试氛围的,但是结果却让人大跌眼 ...