3. IOC操作Bean管理

  1. 什么是 Bean 管理
  • Bean 管理指的是两个操作
  • Spring 创建对象
  • Spirng 注入属性
  1. Bean 管理操作有两种方式
  • 基于 xml 配置文件方式实现
  • 基于注解方式实现
3.1 基于XML配置文件方式
1.基于xml方式创建对象

(1)在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建 (2)在 bean 标签有很多属性,介绍常用的属性 id 属性:唯一标识 class 属性:类全路径(包类路径) (3)创建对象时候,默认是执行无参数构造方法完成对象创建

如下图:

java spring boot 注解XML 转实体类 spring xml注入bean_spring

2.基于xml方式注入属性

DI:依赖注入,就是注入属性(要在创建好对象后才能进行)

①第一种注入方式:使用set方法进行注入

(1)创建类,定义属性和对应的 set 方法

public class Book {
    //创建属性
    private String bname;
    private String bauthor;
    //创建属性对应的 set 方法
    public void setBname(String bname) {
        this.bname = bname;
    }
    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }
    //重写了toString方法
}

(2)在 spring 配置文件配置对象创建,配置属性注入

<!--2 set 方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
    <!--使用 property完成属性注入       name:类里面属性名称      value:向属性注入的值-->
    <property name="bname" value="易筋经"></property>
    <property name="bauthor" value="达摩老祖"></property>
</bean>

编写测试代码

@Test
public void testBook(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println(book); //Book{bname='易筋经', bauthor='达摩'}
}
//从输入结果可以看出在配置文件成功创建对象并给属性赋值
②第二种注入方式:使用有参构造函数进行注入

(1)创建类,定义属性和对应有参构造函数

public class Book {
    private String bname;
    private String bauthor;

    public Book(String bname, String bauthor) {
        this.bname = bname;
        this.bauthor = bauthor;
    }
    //重写了toString方法

(2)在 spring 配置文件配置利用有参构造函数创建对象同时给属性赋值

<bean id="book" class="Bean.Book">
    <!--使用constructor-arg标签调用有参构造创建对象完成-->
    <constructor-arg name="bname" value="生死疲劳"/>
    <constructor-arg name="bauthor" value="莫言"/>
    <!--同样也可以使用<constructor-arg index="0" value="生死疲劳"/>进行配置,其中index表示有参构造函数中的第几个参数,0表示第一个参数-->
</bean>

编写测试代码

@Test
public void testBook(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println(book); //Book{bname='生死疲劳', bauthor='莫言'}
}
③使用p名称空间注入(了解)

使用 p 名称空间注入,可以简化基于 xml 配置方式 (1)第一步 添加 p 名称空间在配置文件中

<?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">

(2)第二步 进行属性注入,在 bean 标签里面进行操作

<bean id="book" class="Bean.Book" p:bname="九阳真经" p:bauthor="无名氏"/>

注:这种方式的底层仍是使用set方法进行注入,只是对写法的一种简化,所以实体类也应该具备对属性的set方法和无参构造函数

3.基于xml方式注入属性-字面量
①注入null值

比如,要给下方Book类的address属性注入空值

public class Book {
    private String bname;
    private String bauthor;
    private String address;
    
    public void setBname(String bname) {
        this.bname = bname;
    }

    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

则可以通过<null/>标签进行注入

<bean id="book" class="Bean.Book">
    <property name="bname" value="易筋经"/>
    <property name="bauthor" value="达摩"/>
    <property name="address">
        <null/>
    </property>
</bean>
②注入特殊符号
  1. 把<>使用 &lt; &gt;进行转义
  2. 把带特殊符号内容写到 CDATA
<bean id="book" class="Bean.Book">
    <property name="bname" value="易筋经"/>
    <property name="bauthor" value="达摩"/>
    <!--注入特殊符号-->
    <property name="address">
        <value><![CDATA[<<南京>>]]></value>
    </property>
</bean>
<!--此时address的值就被赋值成了"<<南京>>"-->
4.基于xml方式注入属性-外部bean

(1)创建两个类 service 类和 dao 类
(2)在 service 中需要调用 dao 里面的方法
(3)在 spring 配置文件中进行配置

public class UserDaoImpl implements UserDao {
    @Override
    public void update() {
        System.out.println("daoImpl update........");
    }
}
public class UserService {

    //需要创建UserDao对象属性
    private UserDao userDao;

    //声明set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add(){
        System.out.println("service add........ ");
        userDao.update();
    }
}
<!--配置UserService对象和UserDao对象-->
<bean id="userService" class="Service.UserService">
    <!--注入外部bean属性,此时给属性赋值应使用ref与被赋值的bean对象的id值相连-->
    <!--注入 userDao 对象   name属性——类里面属性名称    ref属性——创建 userDao对象bean标签id值-->
    <property name="userDao" ref="userDaoImpl"/>
</bean>
<bean id="userDaoImpl" class="Dao.Impl.UserDaoImpl"/>
//测试程序
@Test
public void testService(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
    //service add........ 
    //daoImpl update........
    //从输入结果可以看出userService在调用自身的add方法时同样也调用了userDao的update方法,说明对象创建成功并且成功给外部bean属性赋值
}
5.基于xml方式注入属性-内部bean

(1)一对多关系:部门和员工 一个部门有多个员工,一个员工属于一个部门 部门是一,员工是多 (2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示

//首先创建出员工类和部门类,并表示出它们的关联关系
public class Department {
    private String dname;

    public void setDname(String dname) {
        this.dname = dname;
    }
}

public class Employee {
    private String ename;
    private String egender;
    //员工属于某个部门,用对象形式表示
    private Department department;

    public void setEname(String ename) {
        this.ename = ename;
    }

    public void setEgender(String egender) {
        this.egender = egender;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
}

//均重写了toString方法
<!--在Spring文件进行配置-->
<!--内部bean-->
<bean id="employee" class="Bean.Employee">
    <!--先设置两个普通属性-->
    <property name="ename" value="lucy"/>
    <property name="egender" value="女"/>
    <!--内部嵌套bean对象,仍可以使用外部引入bean对象-->
    <property name="department">
        <bean id="department" class="Bean.Department">
            <property name="dname" value="安保部"/>
        </bean>
    </property>
</bean>
//测试代码
@Test
public void testDepartment(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    Employee employee = context.getBean("employee", Employee.class);
    System.out.println(employee);//Employee{ename='lucy', egender='女', department=Department{dname='安保部'}}
}
6.基于xml方式注入属性-级联赋值

同样使用内部bean的例子

①第一种方式:与外部bean的写法相同
<!--级联赋值-->
<!--使用ref引入外部bean对象-->
<bean id="employee" class="Bean.Employee">
    <property name="ename" value="lucy"/>
    <property name="egender" value="女"/>
    <property name="department" ref="department"/>
</bean>
<bean id="department" class="Bean.Department">
    <property name="dname" value="安保部"/>
</bean>
②第二种方式:
<!--级联赋值-->
<bean id="employee" class="Bean.Employee">
    <property name="ename" value="lucy"/>
    <property name="egender" value="女"/>
    <property name="department" ref="department"/>
    <property name="department.dname" value="财务部"/>
</bean>
<bean id="department" class="Bean.Department">
    <property name="dname" value="安保部"/>
</bean>

注:这种方式需要在赋值类提供被赋值类的get方法,以便于能获取到被赋值类对象,然后对被赋值类的属性进行赋值。比如,上述例子若使用第二种方式的级联赋值时,应在employee类中提供一个getDepartment方法返回department对象属性

另外,由于在测试第二种方式时并未删除<property name="dname" value="安保部"/>,但又添加了<property name="department.dname" value="财务部"/>,而输出结果又是财务部,可以发现第二种方式的优先级要高于第一种方式

7.基于xml方式注入集合类型属性
①注入集合类型属性

(1)创建好例子实体类,包括数组,List,Map,Set类型属性,生成对应set方法

public class Student {
    //1.数组类型属性
    private String[] courses;
    //2.List类型属性
    private List<String> list;
    //3.Map类型属性
    private Map<String,String> map;
    //4.Set类型属性
    private Set<String> set;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }
}

(2)配置Spring文件

<bean id="student" class="Bean.Student">
    <!--①注入数组类型属性-->
    <property name="courses">
        <!--注入数组类型属性使用array标签或list标签都可以-->
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property>
    <!--②注入List类型属性-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>小三</value>
        </list>
    </property>
    <!--③注入Map类型属性-->
    <property name="map">
        <map>
            <entry key="JAVA" value="java"/>
            <entry key="PHP" value="php"/>
        </map>
    </property>
    <!--④注入Set类型属性-->
    <property name="set">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>
</bean>

测试代码

@Test
public void testStudent(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Student student = context.getBean("student", Student.class);
    System.out.println(student);//Student{courses=[java课程, 数据库课程], list=[张三, 小三], map={JAVA=java, PHP=php}, set=[MySQL, Redis]}
}
②在集合属性中设置对象类型

首先,新创建一个类Family,包含属性fname并提供set方法

然后,在上述例子Student类的基础上再添加一个List<Family>类型属性families,用于并提供set方法

然后,再在上例的String配置文件中创建多个Family对象,并使用下方方式注入到student对象的families属性

<!--注入值是对象类型的集合属性-->
    <property name="families">
        <list>
            <!--使用ref标签以及其bean属性链接到创建的多个Family对象-->
            <ref bean="family1"/>
            <ref bean="family2"/>
        </list>
    </property>

<!--创建多个family对象-->
<bean id="family1" class="Bean.Family">
    <property name="fname" value="爸爸"/>
</bean>
<bean id="family2" class="Bean.Family">
    <property name="fname" value="妈妈"/>
</bean>

测试结果如下:

@Test
public void testStudent(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Student student = context.getBean("student", Student.class);
    System.out.println(student);//Student{courses=[java课程, 数据库课程], list=[张三, 小三], map={JAVA=java, PHP=php}, set=[MySQL, Redis], families=[Family{fname='爸爸'}, Family{fname='妈妈'}]}
}
③提取集合属性公共部分

防止混乱,重新创建一个类

public class BookList {
    private List<String> bookList;

    public void setBookList(List<String> bookList) {
        this.bookList = bookList;
    }
}

然后,在Spring配置文件中配置注入到属性

<?xml version="1.0" encoding="UTF-8"?>
<!--引入util名称空间-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--1、提取List集合类型属性注入-->
    <!--可根据需要改为map、set等,然后被其他bean对象作为公共部分引入-->
    <util:list id="bookList">
        <value>Java课程设计</value>
        <value>数据结构与算法</value>
        <value>操作系统</value>
    </util:list>
    
    <!--2、提取List集合类型属性注入的使用-->
    <bean id="books" class="Bean.BookList">
        <!--使用ref属性引入公共部分-->
        <property name="bookList" ref="bookList"/>
    </bean>
    <!--有其他bean对象需要引入公共部分也可向上方为例-->
</beans>

测试代码

@Test
public void testBookList(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    BookList books = context.getBean("books", BookList.class);
    System.out.println(books); //BookList{bookList=[Java课程设计, 数据结构与算法, 操作系统]}
}
3.2 Bean管理
1.工厂Bean

1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean) 2、普通 bean:在配置文件中定义的bean类型就是返回类型(前方举例创建的Bean对象都是普通Bean) 3、工厂 bean:在配置文件定义的bean类型可以和返回类型不一样 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

//工厂Bean举例
public class MyBeanFactory implements FactoryBean<Course> {//指定工厂Bean返回的泛型对象类型为Course
    //实现接口中定义的抽象方法
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        //在实现的getObject方法中指定工厂Bean返回的对象类型为Course
        return course;
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    @Override
    public boolean isSingleton() {
        return false;
    }
}
<!--Spring配置文件的配置-->
<bean id="myBeanFactory" class="Bean.MyBeanFactory"/>
//测试代码
@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    Course course = context.getBean("myBeanFactory", Course.class);
    System.out.println(course);//Course{cname="abc"}
}
2.Bean的作用域

1、在 Spring 里面,创建 bean 实例分为单实例还是多实例

2、在 Spring 里面,默认情况下 bean 是单实例对象

<!--配置一个bean对象,此时默认是单实例对象-->
<bean id="book" class="Bean.Book"/>
@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    Book book1 = context.getBean("book", Book.class);
    Book book2 = context.getBean("book", Book.class);
    System.out.println(book1);//Bean.Book@5d11346a
    System.out.println(book2);//Bean.Book@5d11346a
}
//从测试结果可以看出单实例对象两次创建的对象相同

3、配置Bean对象为多实例对象

(1)在spring配置文件 bean 标签里面有属性(scope) 用于设置单实例还是多实例 (2)scope 属性值 第一个值 默认值singleton,表示是单实例对象 第二个值 prototype,表示是多实例对象

<bean id="book" class="Bean.Book" scope="prototype"/>
<!--此时再执行一次测试代码可发现返回的book1和book2引用值不同即返回的对象实例不同-->

4、singleton 和 prototype 区别

  • 第一 singleton 单实例,prototype 多实例
  • 第二 设置 scope 值是 singleton 时,加载 spring 配置文件时候就会创建单实例对象
    设置 scope 值是 prototype 时,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象
  • 另外,scope还有两个值分别为request,session;request表示这个实例对象的作用域为一次请求,而session表示这个实例对象的作用域为一次会话
3.Bean的生命周期

1、生命周期 (1)从对象创建到销毁的过程

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

3、实例演示

//创建一个Order实体类
public class Order {
    private String oname;

    public Order(){
        System.out.println("执行无参构造函数创建bean实例");
    }

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("调用set方法为属性注入值");
    }

    public void initMethod(){
        System.out.println("执行初始化方法");
    }

    public void destroyMethod(){
        System.out.println("执行销毁方法");
    }
}
<!--在Spring配置文件中配置Bean生命周期中执行的方法-->
<bean id="order" class="Bean.Order" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"/>
</bean>
//测试代码
@Test
public void testOrder(){
    //ApplicationContext context = new ClassPathXmlApplicationContext("orderBean.xml");
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("orderBean.xml");
    Order order = context.getBean("order", Order.class);
    System.out.println("成功创建bean实例对象");
    System.out.println(order);
    //手动销毁bean实例
    context.close();//这个close方法是ApplicationContext的子接口ConfigurableApplicationContext新增的方法,
                    //然后在AbstractApplicationContext抽象类中实现,而ClassPathXmlApplicationContext类是继承了该类的子孙类,
                    //所以这里的context对象的类型不能是ApplicationContext
    //输出结果:
    //执行无参构造函数创建bean实例
    //调用set方法为属性注入值
    //执行初始化方法
    //成功创建bean实例对象
    //Bean.Order@10dba097
    //执行销毁方法
}
//从输出结果即可看出Bean的生命周期:①调用构造器创建bean实例→②调用set方法给属性注入值和引用其他bean→③调用初始化方法→④成功创建bean对象→⑤销毁bean对象

4、bean的后置处理器,bean生命周期有七步(即在bean生命周期的基础上多了两步)

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

5、实例演示

//创建一个后置处理器实体类,实现BeanPostProcessor接口
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;
    }
}
<!--在oederBean.xml中配置后置处理器便于创建bean对象-->
<!--配置后置处理器-->
<bean id="myBeanPost" class="Bean.MyBeanPost"/>
<!--注:配置后置处理器后Spring会给当前xml配置文件中的所有bean实例对象都添加后置处理器进行处理-->
再次执行测试代码,运行结果如下:
    ①执行无参构造函数创建bean实例
    ②调用set方法为属性注入值
    ③在初始化前执行
    ④执行初始化方法
    ⑤在初始化后执行
    ⑥成功创建bean实例对象
    Bean.Order@eb21112
    ⑦执行销毁方法
4.xml自动装配

什么时自动装配?根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入

如何实现自动装配?

  • bean 标签属性 autowire,配置自动装配
  • autowire 属性常用两个值:
  • byName 根据属性名称注入
  • byType 根据属性类型注入
①根据属性名自动注入
//创建两个有关联的实体类
public class Department {
    @Override
    public String toString() {
        return "Department{}";
    }
}

public class Employee {
    private Department department;

    public void setDepartment(Department department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "department=" + department +
                '}';
    }
}
<!--配置Spring文件使其实现自动装配>
<!--自动装配:使用autowire属性-->
<bean id="employee" class="Autowire.Employee" autowire="byName">
    <!--普通手动装配方式-->
    <!--<<property name="department" ref="department"/>>-->
</bean>
<bean id="department" class="Autowire.Department"/>

<!--注:注入值 bean 的 id 值需要和类属性名称一样-->
<!--比如,上述例子中,Department类的bean对象的id值要和Employee类中的Department类型属性名称一样,如,我在Employee类中的Department类型属性名为department,那么我在配置Department的bean对象时其id值也应该为department-->
<!--但实际上应该是注入值的id值需要和该类属性的set方法名一样,因为注入的原理是通过set方法来实现的-->
//测试代码
@Test
public void testAutowire(){
    ApplicationContext context = new ClassPathXmlApplicationContext("autowire.xml");
    Autowire.Employee employee = context.getBean("employee", Autowire.Employee.class);
    System.out.println(employee); //Employee{department=Department{}}
    //测试结果说明实现自动装配成功
}
②根据属性类型自动装配
<!--将属性autowire的值改为byType再执行一次测试代码-->
<bean id="employee" class="Autowire.Employee" autowire="byType"/>
<bean id="department" class="Autowire.Department"/>
<!--测试结果仍能实现自动装配-->

需要注意的是,当使用autowire="byType"时,若当前配置文件中有多个相同类型的bean对象则会报错,此时只能使用byName实现自动装载

5.引入外部属性文件

以配置数据库信息为例: (1)配置德鲁伊连接池 (2)引入德鲁伊连接池依赖 jar 包(druid.jar)(此处本人使用maven直接导入)

<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/userDb"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean>

引入外部属性文件配置数据库连接池

(1)创建外部属性文件,properties 格式文件,写好数据库信息

java spring boot 注解XML 转实体类 spring xml注入bean_赋值_02

(2)把外部 properties 属性文件引入到 spring 配置文件中

①引入 context 名称空间

<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">

②在 spring 配置文件使用标签引入外部属性文件

<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${prop.driverClass}"/>
    <property name="url" value="${prop.url}"/>
    <property name="username" value="${prop.username}"/>
    <property name="password" value="${prop.password}"/>
</bean>

今天又是苦逼满课的一天,只能晚上抽点时间把基于注解注入看了😭😭