1.背景

1.操作bean的意思是IOC如何创建对象(放入),如何注入属性(取出使用)。
2.操作bean有两种方式
方式一:xml的形式(用得少)
方式二:注解的形式(经常使用)
而且区别:本质上是一样的,只是写的形式不一样
理解的核心:只有先把对象放入IOC容器,才可以从容器中取出来!

2.xml操作bean

2.1.普通对象操作

1.常规使用
准备工作:创建一个订单对象

  1. package com.ldp.model;
  2. /**
  3. * @Copyright (C) XXXXXXXXXXX科技股份技有限公司
  4. * @Author: lidongping
  5. * @Date: 2021-01-11 14:28
  6. * @Description:
  7. */
  8. public class Order {
  9. private String orderNo;
  10. private String orderName;
  11. /**
  12. * 无参数构造方法
  13. */
  14. public Order() {
  15. }
  16. /**
  17. * 有参数构造方法
  18. *
  19. * @param orderNo
  20. * @param orderName
  21. */
  22. public Order(String orderNo, String orderName) {
  23. this.orderNo = orderNo;
  24. this.orderName = orderName;
  25. }
  26. // get set 略
  27. }

需求:使用IOC操作bean完成如下对象的创建

  1. public class Testbean02 {
  2. /**
  3. * 使用传统的方式创建对象
  4. * 那么如果采用IOC的方式如何创建呢?
  5. */
  6. @Test
  7. public void test01() {
  8. // 1.简单的创建对象
  9. Order order1 = new Order();
  10. System.out.println(order1);
  11. // 2.创建对象并给对象设定值
  12. Order order2 = new Order();
  13. order2.setOrderName("NO002");
  14. order2.setOrderName("购买苹果");
  15. System.out.println(order2);
  16. // 3.构造器创建方法并设定值
  17. Order order3 = new Order("NO003", "购买香蕉");
  18. System.out.println(order3);
  19. // 4.创建对象并设置特殊值
  20. Order order4 = new Order();
  21. order4.setOrderNo(null);
  22. order4.setOrderName("购买课程<java项目面试专题>");
  23. }
  24. }

答案:IOC实现如上的对象创建如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:P="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!--
  7. 1.简单的创建对象
  8. Order order1 = new Order();
  9. System.out.println(order1);
  10. -->
  11. <bean id="order1" class="com.ldp.model.Order"></bean>
  12. <!--
  13. // 2.创建对象并给对象设定值
  14. Order order2 = new Order();
  15. order2.setOrderName("NO002");
  16. order2.setOrderName("购买苹果");
  17. System.out.println(order2);
  18. 注意:必须有无参数构造器
  19. -->
  20. <bean id="order2" class="com.ldp.model.Order">
  21. <property name="orderNo" value="NO001"></property>
  22. <property name="orderName" value="购买苹果"></property>
  23. </bean>
  24. <!--
  25. // 3.构造器创建方法并设定值
  26. Order order3 = new Order("NO003", "购买香蕉");
  27. System.out.println(order3);
  28. 注意:必须有待着两个参数的构造器
  29. -->
  30. <bean id="order3" class="com.ldp.model.Order">
  31. <constructor-arg name="orderNo" value="NO003"></constructor-arg>
  32. <constructor-arg name="orderName" value="购买香蕉"></constructor-arg>
  33. </bean>
  34. <!--
  35. // 4.创建对象并设置特殊值
  36. Order order4 =new Order();
  37. order4.setOrderNo(null);
  38. order4.setOrderName("购买课程<java项目面试专题>");
  39. 处理特殊字符: < > 的两种方式
  40. 方式一:转义的方式 < >
  41. 方式二:<![CDATA[ 这里写值 ]]> ,例如:<![CDATA[ 购买课程<java项目面试专题> ]]>
  42. 扩展:处理特殊字符在xml中是通用的
  43. 实际案例一:我们在学习mybatis时,在xml中写sql语句经常用到 大于 小于符号 也都是这样转义的
  44. 实际案例二:在做微信支付的时候,因为微信的采用的是xml数据格式,为了代码的健壮性,我们会把所有的值都是用 <![CDATA[ ]]>,微信的官方文档也是这样提供的
  45. -->
  46. <bean id="order4" class="com.ldp.model.Order">
  47. <property name="orderName">
  48. <null/> <!--表示将orderNO设置为null-->
  49. </property>
  50. <property name="orderNo">
  51. <value> <![CDATA[购买课程<java项目面试专题>]]> </value> <!--处理特殊字符: < > -->
  52. </property>
  53. </bean>
  54. <!--
  55. p 名称空间注入,相当于是简写而已
  56. 1.先在头部引入 xmlns:P="http://www.springframework.org/schema/p
  57. 2.使用: P:orderNo="N002" P:orderName="购买苹果"
  58. -->
  59. <bean id="order2" class="com.ldp.model.Order" P:orderNo="N002" P:orderName="购买苹果"></bean>
  60. </beans>

2.注入外部bean,也就是在service 中注入dao的应用场景
准备工作:
1.创建一个ProductDao

  1. public class ProductDao {
  2. /**
  3. * 模拟查询产品dao
  4. *
  5. * @return
  6. */
  7. public List<Product> query() {
  8. List<Product> list = new ArrayList<>();
  9. return list;
  10. }
  11. }

2.创建一个ProductServiceImpl

  1. public class ProductServiceImpl {
  2. private ProductDao productDao;
  3. public ProductDao getProductDao() {
  4. return productDao;
  5. }
  6. public void setProductDao(ProductDao productDao) {
  7. this.productDao = productDao;
  8. }
  9. }

IOC实现注入外部bean,使用ref=“productDao”

  1. <!-- 配置productDao-->
  2. <bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
  3. <!--配置productServiceImpl-->
  4. <bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl">
  5. <property name="productDao" ref="productDao"></property>
  6. </bean>

3.内部注入,其实写法差不多

  1. <!--
  2. 内部注入
  3. -->
  4. <bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl">
  5. <property name="productDao">
  6. <bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
  7. </property>
  8. </bean>

2.2.集合属性操作

准备工作:创建一个User对象

  1. package com.ldp.model;
  2. import java.util.List;
  3. import java.util.Map;
  4. /**
  5. * @Copyright (C) XXXXXXXXXXX科技股份技有限公司
  6. * @Author: lidongping
  7. * @Date: 2021-01-11 16:36
  8. * @Description: <p>
  9. * xml 注入集合属性
  10. * 1、注入数组类型属性
  11. * 2、注入 List 集合类型属性
  12. * 3、注入 Map 集合类型属性
  13. * 创建类,定义数组、list、map、set 类型属性,生成对应 get,set 方法
  14. * </p>
  15. */
  16. public class User {
  17. /**
  18. * 用户名
  19. */
  20. private String userName;
  21. /**
  22. * 特长
  23. */
  24. private String[] hobbyArray;
  25. /**
  26. * 学习科目
  27. */
  28. private List<String> courseList;
  29. /**
  30. * 各科目得分
  31. */
  32. private Map<String, String> scoreMap;
  33. // get set 略
  34. }

1.注入数组
2.注入List集合
3.注入Map集合
IOC注入实现

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean id="user" class="com.ldp.model.User">
  6. <property name="userName" value="张无忌"></property>
  7. <property name="hobbyArray">
  8. <array>
  9. <value>九阳神功</value>
  10. <value>乾坤大挪移</value>
  11. </array>
  12. </property>
  13. <property name="courseList">
  14. <list>
  15. <value>java</value>
  16. <value>c#</value>
  17. </list>
  18. </property>
  19. <property name="scoreMap">
  20. <map>
  21. <entry key="java" value="90"></entry>
  22. <entry key="c#" value="70"></entry>
  23. </map>
  24. </property>
  25. </bean>
  26. </beans>

2.3.FactoryBean结合xml的方式操作bean

1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
2、普通 bean:在配置文件中定义 bean 类型就是返回类型
3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
步骤一: 创建类,让这个类作为工厂 bean,实现接口 FactoryBean

  1. package com.ldp.factoryBean;
  2. import com.ldp.model.Product;
  3. import org.springframework.beans.factory.FactoryBean;
  4. /**
  5. * @Copyright (C) XXXXXXXXXXX科技股份技有限公司
  6. * @Author: lidongping
  7. * @Date: 2021-01-11 17:17
  8. * @Description:
  9. */
  10. public class ProductFactoryBean implements FactoryBean<Product> {
  11. @Override
  12. public Product getObject() throws Exception {
  13. return new Product();
  14. }
  15. @Override
  16. public Class<?> getObjectType() {
  17. return null;
  18. }
  19. }

步骤二: IOC中装配

步骤三:测试

  1. /**
  2. * 普通bean与工厂bean的区别
  3. * 1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
  4. * 2、普通 bean:在配置文件中定义 bean 类型就是返回类型
  5. * 3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
  6. */
  7. @Test
  8. public void test02() {
  9. // 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
  10. ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
  11. // 2.获取配置创建的对象
  12. Product product = context.getBean("productFactoryBean", Product.class);
  13. // 3.使用对象
  14. System.out.println(product);
  15. }

2.4.bean单例多例设置

1.默认的bean是单例的测试如下:
IOC操作Bean(创建、注入) - 图1
2.如何让bean为多例

  1. /**
  2. * 默认创建的bean是单例还是多例的?
  3. * 答案:单例的
  4. * <p>
  5. * 如何为多例呢?
  6. * 加一个:scope="prototype"
  7. * <bean id="product2" class="com.ldp.model.Product" scope="prototype"></bean>
  8. * <p>
  9. * 重要说明:
  10. * 1.设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
  11. * 2.设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象
  12. * 3.注意实际生产中一般使用单例
  13. */
  14. @Test
  15. public void test03() {
  16. // 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
  17. ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
  18. // 2.获取配置创建的对象
  19. Product product1 = context.getBean("product", Product.class);
  20. System.out.println(product1);
  21. Product product2 = context.getBean("product", Product.class);
  22. System.out.println(product2);
  23. }

2.5.bean生命周期

生命周期顾名思义:就是从创建到回收的整过过程(从生—》死)

  1. /**
  2. * bean 生命周期
  3. * 1)调用构造器,通过构造器创建 bean 实例
  4. * 2)给bean设置属性值(调用set方法方法)
  5. * 3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
  6. * 4)使用bean....
  7. * 5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
  8. * xml配置如下:
  9. * <bean id="product2" class="com.ldp.model.Product" init-method="initProduct" destroy-method="destroyProduct">
  10. * <property name="name" value="西瓜"></property>
  11. * </bean>
  12. */
  13. @Test
  14. public void test04() {
  15. // 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
  16. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
  17. // 2.获取配置创建的对象
  18. Product product = context.getBean("product2", Product.class);
  19. System.out.println(product);
  20. // 关闭容器
  21. context.close();
  22. }

BeanPostProcessor 后置处理器的理解
步骤一:添加一个后置处理器

  1. package com.ldp.postProcessor;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.factory.config.BeanPostProcessor;
  4. /**
  5. * @Copyright (C) XXXXXXXXXXX科技股份技有限公司
  6. * @Author: lidongping
  7. * @Date: 2021-01-11 18:34
  8. * @Description: <p>
  9. * bean的后置处理器
  10. * </p>
  11. */
  12. public class DemoPostProcessor implements BeanPostProcessor {
  13. @Override
  14. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  15. System.out.println("初始化执行前的方法.........beanName=" + beanName);
  16. return bean;
  17. }
  18. @Override
  19. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  20. System.out.println("初始化执行 后 的方法.........beanName=" + beanName);
  21. return bean;
  22. }
  23. }

步骤二:xml配置IOC





步骤三:测试

  1. /**
  2. * bean 生命周期
  3. * 1)调用构造器,通过构造器创建 bean 实例
  4. * 2)给bean设置属性值(调用set方法方法)
  5. * ------把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
  6. * 3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
  7. * ------把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
  8. * 4)使用bean....
  9. * 5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
  10. */
  11. @Test
  12. public void test04() {
  13. // 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
  14. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
  15. // 2.获取配置创建的对象
  16. Product product = context.getBean("product", Product.class);
  17. System.out.println(product);
  18. // 关闭容器
  19. context.close();
  20. }

步骤四:测试结果

  1. 调用构造方法.....
  2. 调用set方法.....
  3. 初始化执行前的方法.........beanName=product
  4. 调用初始化方法......
  5. 初始化执行 的方法.........beanName=product
  6. com.ldp.model.Product@64f6106c
  7. 调用销毁化方法......

2.6.xml自动装配

概念:自动装配的意思就是,bean会根据类型或者id(名称),自动去找容器中满足条件的对象
注意:bean中id是不会重复的,但是配置的时候类可能会重复,因此
1.如果同一个类型配置了多次,那么如果根据类型装配会是什么情况呢?
报错:expected single matching bean but found 2: productDao,productDao2
2.如果没有这个类会是什么情况呢? 装配值为null,也就是不会装配,但是也不会报错
3.如果根据名称装配,没有这个名称会怎么样呢? 装配值为null,也就是不会装配,但是也不会报错
根据id名称装配

  1. <!--自动装配演示 根据id名称装配
  2. 需求:以在ProductServiceImpl 中装配一个 ProductDao 为例讲解
  3. -->
  4. <bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
  5. <!--bean 标签属性 autowire,配置自动装配
  6. autowire 属性常用两个值:
  7. byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 ,
  8. 《例如这里的 productDao 一定要与 bean id 值一致,因此我们经常采用规范命名 装配对象的类名首字母小写作为装配的id》
  9. byType 根据属性类型注入-->
  10. <bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl" autowire="byName">
  11. <!--
  12. 自动装配不写这个dao注入,对象中也会有这个值
  13. <property name="productDao" ref="productDao"></property>
  14. -->
  15. </bean>

根据类型装配

  1. <!--自动装配演示 根据类型装配
  2. 需求:以在ProductServiceImpl 中装配一个 ProductDao 为例讲解
  3. -->
  4. <bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
  5. <!--bean 标签属性 autowire,配置自动装配
  6. autowire 属性常用两个值:
  7. byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 ,
  8. 《例如这里的 productDao 一定要与 bean id 值一致,因此我们经常采用规范命名 装配对象的类名首字母小写作为装配的id》
  9. byType 根据属性类型注入-->
  10. <bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl" autowire="byType">
  11. <!--
  12. 自动装配不写这个dao注入,对象中也会有这个值
  13. <property name="productDao" ref="productDao"></property>
  14. -->
  15. </bean>

2.7.装配值来自外部配置文件

这里以我们常用的数据库配置为例进行讲解
外部配置文件:db.properties
mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/ldp-data
mysql.username=root
mysql.password=admin
spring的xml配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. 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">
  6. <!-- IOC 操作 Bean 管理-外部属性文件-->
  7. <!--1.直接写参数配置连接池
  8. 缺点:参数与代码耦合在一起,不便于维护
  9. -->
  10. <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource">
  11. <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  12. <property name="url" value="jdbc:mysql://localhost:3306/ldp-data"></property>
  13. <property name="username" value="root"></property>
  14. <property name="password" value="admin"></property>
  15. </bean>
  16. <!--引入外部属性文件-->
  17. <context:property-placeholder location="classpath:db.properties"/>
  18. <!--配置连接池-->
  19. <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
  20. <property name="driverClassName" value="${mysql.driverClassName}"></property>
  21. <property name="url" value="${mysql.url}"></property>
  22. <property name="username" value="${mysql.username}"></property>
  23. <property name="password" value="${mysql.password}"></property>
  24. </bean>
  25. </beans>

3.注解的方式操作bean

这个用法用得很多,也很简单相信大家都已经很熟练了!
1、什么是注解
(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化 xml 配置
2、Spring 针对 Bean 管理中创建对象提供注解
(1)@Repository:一般贴在dao上
(2)@Service :一般贴在业务类上
(3)@Controller :控制层类上
(4)@Component:一般贴在非dao、service、controller的类上
上面四个注解功能是一样的,都可以用来创建 bean 实例

3.1.使用注解将对象放入spring容器

具体用法:
步骤一:编写spring配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. 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">
  6. <!-- IOC 操作 Bean 管理-基于注解方式-->
  7. <!--开启组件扫描
  8. 如果扫描多个包,多个包使用逗号隔开
  9. -->
  10. <context:component-scan base-package="com.ldp"></context:component-scan>
  11. </beans>

步骤二:贴注解

  1. // 表示使用注解将对象加入spring容器
  2. @Service
  3. public class TestDemoServiceImpl {
  4. public void method() {
  5. System.out.println("使用注解的方式加入spring容器...");
  6. }
  7. }

3.2.使用注解的方式将属性注入到对象中

1.简单使用

  1. // 表示使用注解将对象加入spring容器
  2. @Service
  3. public class TestDemoServiceImpl {
  4. // 使用注解将对象放入spring容器
  5. @Autowired
  6. private ProductDao productDao;
  7. public void method() {
  8. System.out.println("使用注解的方式加入spring容器...");
  9. }
  10. }

2.注解@Qualifier:根据名称进行注入

  1. // 表示使用注解将对象加入spring容器
  2. @Service
  3. public class TestDemoServiceImpl {
  4. // 使用注解将对象放入spring容器
  5. @Autowired
  6. //根据名称进行注入
  7. @Qualifier(value = "productDao")
  8. private ProductDao productDao;
  9. public void method() {
  10. System.out.println("使用注解的方式加入spring容器...");
  11. }
  12. }

3.注解@Resource:可以根据类型注入,可以根据名称注入

  1. // 表示使用注解将对象加入spring容器
  2. @Service
  3. public class TestDemoServiceImpl {
  4. // 使用注解将对象放入spring容器
  5. // @Autowired
  6. //根据名称进行注入
  7. // @Qualifier(value = "productDao")
  8. //根据名称进行注入(一般不使用@Resource注解)
  9. @Resource(name = "productDao")
  10. private ProductDao productDao;
  11. public void method() {
  12. System.out.println("使用注解的方式加入spring容器...");
  13. }
  14. }

4.注解@Value:注入普通类型属性或外部配置文件属性

  1. // 注解注入普通属性
  2. @Value("张三")
  3. private String name;
  4. // 注解注入外部配置文件普通属性
  5. @Value("${mysql.url}")
  6. private String url;

4.全注解开发

步骤一:配置类

  1. //作为配置类,替代 xml 配置文件
  2. @Configuration
  3. @ComponentScan(basePackages = {"com.ldp"})
  4. // 读取外部配置文件
  5. @PropertySource("classpath:db.properties")
  6. public class SpringConfig {
  7. }

步骤二:测试

  1. @Test
  2. public void test01() {
  3. //加载配置类
  4. ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
  5. // 获取容器对象
  6. TestDemoServiceImpl testDemoService = context.getBean("testDemoServiceImpl", TestDemoServiceImpl.class);
  7. // 执行方法
  8. testDemoService.method();
  9. }

5.总结

到此IOC容器相关的知识点已经梳理完成,如果还有不懂的可以结合视频讲解学习,该博客已录制成视频课程,也可以单独问我!

完美!