1.背景
1.操作bean的意思是IOC如何创建对象(放入),如何注入属性(取出使用)。
2.操作bean有两种方式
方式一:xml的形式(用得少)
方式二:注解的形式(经常使用)
而且区别:本质上是一样的,只是写的形式不一样
理解的核心:只有先把对象放入IOC容器,才可以从容器中取出来!
2.xml操作bean
2.1.普通对象操作
1.常规使用
准备工作:创建一个订单对象
package com.ldp.model;
/**
* @Copyright (C) XXXXXXXXXXX科技股份技有限公司
* @Author: lidongping
* @Date: 2021-01-11 14:28
* @Description:
*/
public class Order {
private String orderNo;
private String orderName;
/**
* 无参数构造方法
*/
public Order() {
}
/**
* 有参数构造方法
*
* @param orderNo
* @param orderName
*/
public Order(String orderNo, String orderName) {
this.orderNo = orderNo;
this.orderName = orderName;
}
// get set 略
}
需求:使用IOC操作bean完成如下对象的创建
public class Testbean02 {
/**
* 使用传统的方式创建对象
* 那么如果采用IOC的方式如何创建呢?
*/
@Test
public void test01() {
// 1.简单的创建对象
Order order1 = new Order();
System.out.println(order1);
// 2.创建对象并给对象设定值
Order order2 = new Order();
order2.setOrderName("NO002");
order2.setOrderName("购买苹果");
System.out.println(order2);
// 3.构造器创建方法并设定值
Order order3 = new Order("NO003", "购买香蕉");
System.out.println(order3);
// 4.创建对象并设置特殊值
Order order4 = new Order();
order4.setOrderNo(null);
order4.setOrderName("购买课程<java项目面试专题>");
}
}
答案:IOC实现如上的对象创建如下
<?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:P="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
1.简单的创建对象
Order order1 = new Order();
System.out.println(order1);
-->
<bean id="order1" class="com.ldp.model.Order"></bean>
<!--
// 2.创建对象并给对象设定值
Order order2 = new Order();
order2.setOrderName("NO002");
order2.setOrderName("购买苹果");
System.out.println(order2);
注意:必须有无参数构造器
-->
<bean id="order2" class="com.ldp.model.Order">
<property name="orderNo" value="NO001"></property>
<property name="orderName" value="购买苹果"></property>
</bean>
<!--
// 3.构造器创建方法并设定值
Order order3 = new Order("NO003", "购买香蕉");
System.out.println(order3);
注意:必须有待着两个参数的构造器
-->
<bean id="order3" class="com.ldp.model.Order">
<constructor-arg name="orderNo" value="NO003"></constructor-arg>
<constructor-arg name="orderName" value="购买香蕉"></constructor-arg>
</bean>
<!--
// 4.创建对象并设置特殊值
Order order4 =new Order();
order4.setOrderNo(null);
order4.setOrderName("购买课程<java项目面试专题>");
处理特殊字符: < > 的两种方式
方式一:转义的方式 < >
方式二:<![CDATA[ 这里写值 ]]> ,例如:<![CDATA[ 购买课程<java项目面试专题> ]]>
扩展:处理特殊字符在xml中是通用的
实际案例一:我们在学习mybatis时,在xml中写sql语句经常用到 大于 小于符号 也都是这样转义的
实际案例二:在做微信支付的时候,因为微信的采用的是xml数据格式,为了代码的健壮性,我们会把所有的值都是用 <![CDATA[ ]]>,微信的官方文档也是这样提供的
-->
<bean id="order4" class="com.ldp.model.Order">
<property name="orderName">
<null/> <!--表示将orderNO设置为null-->
</property>
<property name="orderNo">
<value> <![CDATA[购买课程<java项目面试专题>]]> </value> <!--处理特殊字符: < > -->
</property>
</bean>
<!--
p 名称空间注入,相当于是简写而已
1.先在头部引入 xmlns:P="http://www.springframework.org/schema/p
2.使用: P:orderNo="N002" P:orderName="购买苹果"
-->
<bean id="order2" class="com.ldp.model.Order" P:orderNo="N002" P:orderName="购买苹果"></bean>
</beans>
2.注入外部bean,也就是在service 中注入dao的应用场景
准备工作:
1.创建一个ProductDao
public class ProductDao {
/**
* 模拟查询产品dao
*
* @return
*/
public List<Product> query() {
List<Product> list = new ArrayList<>();
return list;
}
}
2.创建一个ProductServiceImpl
public class ProductServiceImpl {
private ProductDao productDao;
public ProductDao getProductDao() {
return productDao;
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
}
IOC实现注入外部bean,使用ref=“productDao”
<!-- 配置productDao-->
<bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
<!--配置productServiceImpl-->
<bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl">
<property name="productDao" ref="productDao"></property>
</bean>
3.内部注入,其实写法差不多
<!--
内部注入
-->
<bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl">
<property name="productDao">
<bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
</property>
</bean>
2.2.集合属性操作
准备工作:创建一个User对象
package com.ldp.model;
import java.util.List;
import java.util.Map;
/**
* @Copyright (C) XXXXXXXXXXX科技股份技有限公司
* @Author: lidongping
* @Date: 2021-01-11 16:36
* @Description: <p>
* xml 注入集合属性
* 1、注入数组类型属性
* 2、注入 List 集合类型属性
* 3、注入 Map 集合类型属性
* 创建类,定义数组、list、map、set 类型属性,生成对应 get,set 方法
* </p>
*/
public class User {
/**
* 用户名
*/
private String userName;
/**
* 特长
*/
private String[] hobbyArray;
/**
* 学习科目
*/
private List<String> courseList;
/**
* 各科目得分
*/
private Map<String, String> scoreMap;
// get set 略
}
1.注入数组
2.注入List集合
3.注入Map集合
IOC注入实现
<?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">
<bean id="user" class="com.ldp.model.User">
<property name="userName" value="张无忌"></property>
<property name="hobbyArray">
<array>
<value>九阳神功</value>
<value>乾坤大挪移</value>
</array>
</property>
<property name="courseList">
<list>
<value>java</value>
<value>c#</value>
</list>
</property>
<property name="scoreMap">
<map>
<entry key="java" value="90"></entry>
<entry key="c#" value="70"></entry>
</map>
</property>
</bean>
</beans>
2.3.FactoryBean结合xml的方式操作bean
1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
2、普通 bean:在配置文件中定义 bean 类型就是返回类型
3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
步骤一: 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
package com.ldp.factoryBean;
import com.ldp.model.Product;
import org.springframework.beans.factory.FactoryBean;
/**
* @Copyright (C) XXXXXXXXXXX科技股份技有限公司
* @Author: lidongping
* @Date: 2021-01-11 17:17
* @Description:
*/
public class ProductFactoryBean implements FactoryBean<Product> {
@Override
public Product getObject() throws Exception {
return new Product();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
步骤二: IOC中装配
步骤三:测试
/**
* 普通bean与工厂bean的区别
* 1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
* 2、普通 bean:在配置文件中定义 bean 类型就是返回类型
* 3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
*/
@Test
public void test02() {
// 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
// 2.获取配置创建的对象
Product product = context.getBean("productFactoryBean", Product.class);
// 3.使用对象
System.out.println(product);
}
2.4.bean单例多例设置
1.默认的bean是单例的测试如下:
2.如何让bean为多例
/**
* 默认创建的bean是单例还是多例的?
* 答案:单例的
* <p>
* 如何为多例呢?
* 加一个:scope="prototype"
* <bean id="product2" class="com.ldp.model.Product" scope="prototype"></bean>
* <p>
* 重要说明:
* 1.设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
* 2.设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象
* 3.注意实际生产中一般使用单例
*/
@Test
public void test03() {
// 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
// 2.获取配置创建的对象
Product product1 = context.getBean("product", Product.class);
System.out.println(product1);
Product product2 = context.getBean("product", Product.class);
System.out.println(product2);
}
2.5.bean生命周期
生命周期顾名思义:就是从创建到回收的整过过程(从生—》死)
/**
* bean 生命周期
* 1)调用构造器,通过构造器创建 bean 实例
* 2)给bean设置属性值(调用set方法方法)
* 3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
* 4)使用bean....
* 5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
* xml配置如下:
* <bean id="product2" class="com.ldp.model.Product" init-method="initProduct" destroy-method="destroyProduct">
* <property name="name" value="西瓜"></property>
* </bean>
*/
@Test
public void test04() {
// 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
// 2.获取配置创建的对象
Product product = context.getBean("product2", Product.class);
System.out.println(product);
// 关闭容器
context.close();
}
BeanPostProcessor 后置处理器的理解
步骤一:添加一个后置处理器
package com.ldp.postProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @Copyright (C) XXXXXXXXXXX科技股份技有限公司
* @Author: lidongping
* @Date: 2021-01-11 18:34
* @Description: <p>
* bean的后置处理器
* </p>
*/
public class DemoPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化执行前的方法.........beanName=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化执行 后 的方法.........beanName=" + beanName);
return bean;
}
}
步骤二:xml配置IOC
步骤三:测试
/**
* bean 生命周期
* 1)调用构造器,通过构造器创建 bean 实例
* 2)给bean设置属性值(调用set方法方法)
* ------把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
* 3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
* ------把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
* 4)使用bean....
* 5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
*/
@Test
public void test04() {
// 1.加载 spring 配置文件 (这个时候 bean1.xml 中的所有对象已经创建)
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
// 2.获取配置创建的对象
Product product = context.getBean("product", Product.class);
System.out.println(product);
// 关闭容器
context.close();
}
步骤四:测试结果
调用构造方法.....
调用set方法.....
初始化执行前的方法.........beanName=product
调用初始化方法......
初始化执行 后 的方法.........beanName=product
com.ldp.model.Product@64f6106c
调用销毁化方法......
2.6.xml自动装配
概念:自动装配的意思就是,bean会根据类型或者id(名称),自动去找容器中满足条件的对象
注意:bean中id是不会重复的,但是配置的时候类可能会重复,因此
1.如果同一个类型配置了多次,那么如果根据类型装配会是什么情况呢?
报错:expected single matching bean but found 2: productDao,productDao2
2.如果没有这个类会是什么情况呢? 装配值为null,也就是不会装配,但是也不会报错
3.如果根据名称装配,没有这个名称会怎么样呢? 装配值为null,也就是不会装配,但是也不会报错
根据id名称装配
<!--自动装配演示 根据id名称装配
需求:以在ProductServiceImpl 中装配一个 ProductDao 为例讲解
-->
<bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
<!--bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 ,
《例如这里的 productDao 一定要与 bean id 值一致,因此我们经常采用规范命名 装配对象的类名首字母小写作为装配的id》
byType 根据属性类型注入-->
<bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl" autowire="byName">
<!--
自动装配不写这个dao注入,对象中也会有这个值
<property name="productDao" ref="productDao"></property>
-->
</bean>
根据类型装配
<!--自动装配演示 根据类型装配
需求:以在ProductServiceImpl 中装配一个 ProductDao 为例讲解
-->
<bean id="productDao" class="com.ldp.dao.ProductDao"></bean>
<!--bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 ,
《例如这里的 productDao 一定要与 bean id 值一致,因此我们经常采用规范命名 装配对象的类名首字母小写作为装配的id》
byType 根据属性类型注入-->
<bean id="productServiceImpl" class="com.ldp.service.ProductServiceImpl" autowire="byType">
<!--
自动装配不写这个dao注入,对象中也会有这个值
<property name="productDao" ref="productDao"></property>
-->
</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配置
<?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:context="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">
<!-- IOC 操作 Bean 管理-外部属性文件-->
<!--1.直接写参数配置连接池
缺点:参数与代码耦合在一起,不便于维护
-->
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ldp-data"></property>
<property name="username" value="root"></property>
<property name="password" value="admin"></property>
</bean>
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置连接池-->
<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${mysql.driverClassName}"></property>
<property name="url" value="${mysql.url}"></property>
<property name="username" value="${mysql.username}"></property>
<property name="password" value="${mysql.password}"></property>
</bean>
</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配置文件
<?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:context="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">
<!-- IOC 操作 Bean 管理-基于注解方式-->
<!--开启组件扫描
如果扫描多个包,多个包使用逗号隔开
-->
<context:component-scan base-package="com.ldp"></context:component-scan>
</beans>
步骤二:贴注解
// 表示使用注解将对象加入spring容器
@Service
public class TestDemoServiceImpl {
public void method() {
System.out.println("使用注解的方式加入spring容器...");
}
}
3.2.使用注解的方式将属性注入到对象中
1.简单使用
// 表示使用注解将对象加入spring容器
@Service
public class TestDemoServiceImpl {
// 使用注解将对象放入spring容器
@Autowired
private ProductDao productDao;
public void method() {
System.out.println("使用注解的方式加入spring容器...");
}
}
2.注解@Qualifier:根据名称进行注入
// 表示使用注解将对象加入spring容器
@Service
public class TestDemoServiceImpl {
// 使用注解将对象放入spring容器
@Autowired
//根据名称进行注入
@Qualifier(value = "productDao")
private ProductDao productDao;
public void method() {
System.out.println("使用注解的方式加入spring容器...");
}
}
3.注解@Resource:可以根据类型注入,可以根据名称注入
// 表示使用注解将对象加入spring容器
@Service
public class TestDemoServiceImpl {
// 使用注解将对象放入spring容器
// @Autowired
//根据名称进行注入
// @Qualifier(value = "productDao")
//根据名称进行注入(一般不使用@Resource注解)
@Resource(name = "productDao")
private ProductDao productDao;
public void method() {
System.out.println("使用注解的方式加入spring容器...");
}
}
4.注解@Value:注入普通类型属性或外部配置文件属性
// 注解注入普通属性
@Value("张三")
private String name;
// 注解注入外部配置文件普通属性
@Value("${mysql.url}")
private String url;
4.全注解开发
步骤一:配置类
//作为配置类,替代 xml 配置文件
@Configuration
@ComponentScan(basePackages = {"com.ldp"})
// 读取外部配置文件
@PropertySource("classpath:db.properties")
public class SpringConfig {
}
步骤二:测试
@Test
public void test01() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
// 获取容器对象
TestDemoServiceImpl testDemoService = context.getBean("testDemoServiceImpl", TestDemoServiceImpl.class);
// 执行方法
testDemoService.method();
}
5.总结
到此IOC容器相关的知识点已经梳理完成,如果还有不懂的可以结合视频讲解学习,该博客已录制成视频课程,也可以单独问我!