Spring IOC容器-Bean管理——基于XML(续集)

IOC 操作 Bean 管理

1、FactoryBean

1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)

2、普通 bean:在配置文件中定义 bean 类型就是返回类型

3、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyBean implements FactoryBean<Course> {

//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
</bean>

@Test
public void test3() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Course course = context.getBean("myBean", Course.class);//返回值类型可以不是定义的bean类型!
System.out.println(course);
}

2、bean 作用域

在 Spring 里面,默认情况下,bean 是单实例对象,下面进行作用域设置:

  1. 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
  2. scope 属性值 第一个值 默认值,singleton,表示是单实例对象 第二个值 prototype,表示是多实例对象
1
2
3
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype"><!--设置为多实例-->
<property name="list" ref="bookList"></property>
</bean>
  1. singleton 和 prototype 区别
    • singleton 单实例,prototype 多实例
    • 设置 scope 值是 singleton 时候, 加载 spring 配置文件时候就会创建单实例对象
    • 设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,在调用 getBean 方法时候创建多实例对象

3、bean 生命周期

1 ) 生命周期 :

  • 从对象创建到对象销毁的过程

2 ) bean 生命周期

  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  3. 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  4. bean 可以使用了(对象获取到了)
  5. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

3 ) 演示 bean 生命周期 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Orders {
//无参数构造
public Orders() {
System.out.println("第一步 执行无参数构造创建 bean 实例");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 调用 set 方法设置属性值");
}
//创建执行的初始化的方法
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
//创建执行的销毁的方法
public void destroyMethod() {
System.out.println("第五步 执行销毁的方法");
}
}

创建类实现 BeanPostProcessor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MyBeanPost implements BeanPostProcessor {//创建后置处理器实现类
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前执行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之后执行的方法");
return bean;
}
}
<!--配置文件的bean参数配置-->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <!--配置初始化方法和销毁方法-->
<property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>

<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
@Test
public void testBean3() {
// ApplicationContext context =
// new ClassPathXmlApplicationContext("bean4.xml");
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步 获取创建 bean 实例对象");
System.out.println(orders);
//手动让 bean 实例销毁
context.close();
}

4 ) bean 的后置处理器

bean 生命周期有七步(正常生命周期为五步,而配置后置处理器后为七步)

  • 主要是第三步和第五步的区别
  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  3. 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
  4. 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  5. 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
  6. bean 可以使用了(对象获取到了)
  7. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法

4、xml自动装配Bean

自动装配的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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标签属性 autowire ,配置自动装配
autowire属性常用的两个值:
byName 根据属性名称自动注入 ,注入值bean的id值和类属性名称一样
byType:根据属性类型自动注入

-->
<bean id="emp" class="com.Keafmd.spring5.autowire.Emp" autowire="byType">
<!--手动装配-->
<!-- <property name="dept" ref="dept"></property>-->
</bean>

<bean id="dept" class="com.Keafmd.spring5.autowire.Dept"></bean>

<!--根据属性类型注入,多写这行就会报错,类型匹配到了两个bean,byName不会错-->
<!-- <bean id="dept1" class="com.Keafmd.spring5.autowire.Dept"></bean>-->


</beans>

测试代码相同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.Keafmd.spring5.autowire.Emp;
import com.Keafmd.spring5.bean.Orders;
import com.Keafmd.spring5.collectiontype.Book;
import com.Keafmd.spring5.collectiontype.Course;
import com.Keafmd.spring5.collectiontype.Stu;
import com.Keafmd.spring5.factorybean.MyBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring5demo1 {
@Test
public void test5(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
Emp emp = context.getBean("emp",Emp.class);
System.out.println(emp);

}
}

测试结果:

Emp{dept=Dept{}}

这就是自动装配,但是这种基于xml自动装配并不常用,常用的是基于注解的。
以上就是基于xml的自动装配。

5、外部属性文件

  1. 方式一:直接配置数据库信息 :(1)配置Druid(德鲁伊)连接池 (2)引入Druid(德鲁伊)连接池依赖 jar 包
1
2
3
4
5
6
7
8
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>

  1. 方式二:引入外部属性文件配置数据库连接池
    1. 创建外部属性文件,properties 格式文件,写数据库信息(jdbc.properties
1
2
3
4
prop.driverClass=com.mysql.cj.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=qwer

​ 2. 把外部 properties 属性文件引入到 spring 配置文件中 —— 引入 context 名称空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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 http://www.springframework.org/schema/context/spring-context.xsd"><!--引入context名称空间-->

<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>

</beans>

IOC 操作 Bean 管理(基于注解方式)

1、什么是注解

(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)

(2)使用注解,注解作用在类上面,方法上面,属性上面

(3)使用注解目的:简化 xml 配置

2、Spring 针对 Bean 管理中创建对象提供注解

Component 组成部分;成分;部件 [kəmˈpoʊnənt]

Repository 仓库;资源库;版本库;知识库 [rɪˈpɑːzətɔːri]

下面四个注解**==功能是一样的==**,都可以用来创建 bean 实例

(1)@Component

(2)@Service

(3)@Controller

(4)@Repository

注解 说明
@Component 使用在类上用于实例化Bean
@Controller 使用在web层类上用于实例化bean
@Service 使用在service用于实例化bean
@Repository 使用在dao层类上用于实例化bean

3、⭐基于注解方式实现对象建

第一步 引入依赖 (引入spring-aop jar包

第二步 开启组件扫描

下面的这种扫描方式会扫描这个包下的所有类

  • 如果没有这一步的话Spring就无法找到注解
1
2
3
4
5
<!--开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>

第三步 创建类,在类上面添加创建对象注解

  • value属性值可以不写

    • 默认是类的名称的首字母小写 : 例如这里 UserService — 》 userService
1
2
3
4
5
6
7
@Component(value = "userService") 
//注解等同于XML配置文件:<bean id="userService" class=".."/> class 里面是包的路径
public class UserService {
public void add() {
System.out.println("service add.......");
}
}

4、开启组件扫描细节配置

生词:

annotation: 注释, 注解 [ˌænəˈteɪʃn]

exclude : 排除 把…排斥在外;认为…不可能 [ɪkˈskluːd]

  • 配置xml使得Spring扫描部分类型的注解
  1. use-default-filters="false" 表示现在不使用默认 filter自己配置 filter
    context:include-filter 设置扫描哪些内容

    例如下面的配置文件就是使Spring只扫描使用Controller注解的类

    1
    2
    <context:component-scan base-package="com.atguigu" use-defaultfilters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  2. 表示Controller注解的类之外一切都进行扫描

1
2
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation"expression="org.springframework.stereotype.Controller"/>

5、基于注解方式实现属性注入

  • 可以结合MVC架构理解这里注解的作用

生词:

autowired :自动装配

qualifier : (击败对手可进入某竞赛的)合格者;预选赛;资格赛;外围赛;修饰词(尤指形容词或副词) [ˈkwɑːlɪfaɪər]

1)@Autowired

  • 根据属性类型进行自动装配

第一步

  • 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解

第二步

  • 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
}
}
1
2
3
4
5
6
7
@Repository(value = "userDaoImpl")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add.....");
}
}

2)@Qualifier

  • 根据名称进行注入,这个@Qualifier 注解的使用,和上面@Autowired 一起使用

    使用名称进行注入 , 目的在于避免区别同一接口下有多个实现类,根据类型就无法选择,导致出错

    原本通过xml文件赋值是需要提前写好set方法的, 这里通过@Qualifier 注解来赋值就不需要写set() 方法

              • 虽然开发时候一般都会写set() 🐔
1
2
3
4
5
6
7
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl")
private UserDao userDao;

3)@Resource

  • 可以根据类型注入,也可以根据名称注入(它属于javax包下的注解,⚠️不推荐使用!)
1
2
3
4
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl") //根据名称进行注入
private UserDao userDao;

4)@Value

  • 注入普通类型属性
1
2
@Value(value = "abc")
private String name

总体代一览

UserDao.java

1
2
3
4
5
6
package com.atguigu.spring5.dao;

public interface UserDao {
public void add();
}

UserDaoImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
package com.atguigu.spring5.dao.Impl;

import com.atguigu.spring5.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository(value = "userDaoImpl")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("DAO add.....");
}
}

UserService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.atguigu.spring5.service;

import com.atguigu.spring5.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {
@Value(value = "abc")
private String name;
//定义dao类型属性
//不需要添加set方法
//添加注入属性注解
// @Autowired //根据类型进行注入
// @Qualifier(value = "userDaoImpl1") //根据名称进行注入
// private UserDao userDao;

//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl") //根据名称进行注入
private UserDao userDao;

public void add() {
System.out.println("service add......."+name);
userDao.add();
}
}

TestSpring5Demo1.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.atguigu.spring5.testdemo;

import com.atguigu.spring5.config.SpringConfig;
import com.atguigu.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring5Demo1 {
@Test
public void testService1() {
ApplicationContext context
= new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}

@Test
public void testService2() {
//加载配置类
ApplicationContext context
= new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}

读取注解创建容器

  • 实际开发一般都使用SpringBoot 开发

不用配置文件, 全部使用注解替代

1)创建配置类,替代 xml 配置文件

  • 类名随便起名 ( ==要遵守开发规范== ) ,但是配置类一定要@Configuration 注解 ,表明这是一个配置类
1
2
3
4
5
6
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
// 这句话相当于xml <context:component-scan base-package="com.atguigu" use-defaultfilters="false">
public class SpringConfig {

}

2)编写测试类

在创建 ApplicationContext 接口实现对象 时, 调用的构造器是 AnnotationConfigApplicationContext 类的构造器

ApplicationContext :interface

常用实现类:

​ ClassPathXmlApplicationContext : 可以加载类路径下的配置文件 ,要求配置文件必须在类路径下面

​ AnnotationConfigApplicationContext :读取注解创建容器

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testService2() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

UserService userService = context.getBean("userService",UserService.class);

System.out.println(userService);

userService.add();
}