Spring-IOC—基于XML配置Bean

  |   0 评论   |   0 浏览

Spring-IOC—基于XML配置Bean

1.Spring 配置/管理 bean 介绍

1.Bean 管理包括两方面

1.创建bean对象

2.给bean注入属性

2.Bean配置方式

1.基于xml文件配置方式

2.基于注解方式

2.基于XML配置bean

1.通过类型来获取bean

1.应用案例

<!--2.通过类型获取bean-->
<bean class="com.llp.spring.bean.Monster">
    <property name="monsterId" value="3"/>
    <property name="name" value="琪琪"/>
    <property name="skill" value="魅惑"/>
</bean>
@Test
    public void getBeanByType(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        //通过类型获取bean,要求 ioc 容器中的同一个类的 bean 只能有一个!!
        Monster bean = ioc.getBean(Monster.class);
        System.out.println(bean);
    }

2.使用细节

1、按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个, 否则会抛出异常

org.springframework.beans.NotWritablePropertyException: Invalid property 'skill' of bean class [com.llp.spring.bean.Monster]: Bean property 'skill' is not writable or has an invalid setter method.

2、这种方式的应用场景:比如 XxxAction/Servlet/Controller, 或 XxxService 在一个线程

3、在容器配置文件(比如 beans.xml)中给属性赋值, 底层是通过setter 方法完成的, 这也是为什么我们需要提供 setter 方法的原因

2.通过构造器配置bean

1.应用案例

beans.xml

<!--3.通过构造器配置bean-->
    <!--
        1. constructor-arg标签可以指定使用构造器的参数
        2. index表示构造器的第几个参数 从0开始计算的
        3. 除了可以通过index 还可以通过 name / type 来指定参数方式
        4. 类的构造器,不能有完全相同类型和顺序的构造器,所以可以通过type来指定
    -->
    <!--index方式-->
    <bean class="com.llp.spring.bean.Monster" id="monster03">
        <constructor-arg value="4" index="0"/>
        <constructor-arg value="白骨精" index="1"/>
        <constructor-arg value="吸血" index="2"/>
    </bean>

    <!--name方式-->
    <bean class="com.llp.spring.bean.Monster" id="monster04">
        <constructor-arg value="5" name="monsterId"/>
        <constructor-arg value="蜘蛛精" name="name"/>
        <constructor-arg value="吸血" name="skill"/>
    </bean>

    <!--type方式-->
    <!--为什么可以? 类的构造器,不能有完全相同类型和顺序的构造器,所以可以通过type来指定-->
    <bean class="com.llp.spring.bean.Monster" id="monster05">
        <constructor-arg value="6" type="java.lang.Integer"/>
        <constructor-arg value="黑熊精" type="java.lang.String"/>
        <constructor-arg value="掏心" type="java.lang.String"/>
    </bean>
@Test
    public void getBeanByConstructor(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Monster monster03 = ioc.getBean("monster03",Monster.class);
        System.out.println(monster03);
        Monster monster04 = ioc.getBean("monster04",Monster.class);
        System.out.println(monster04);
        Monster monster05 = ioc.getBean("monster05",Monster.class);
        System.out.println(monster05);
    }

image-20220514142428710

2.使用细节

  1. 通过 index 属性来区分是第几个参数
  2. 通过 type 属性来区分是什么类型(按照顺序)

3.通过p名称空间配置bean

1.应用案例

image-20220514142754303

image-20220514142850714

<!--通过 p 名称空间配置 bean-->
    <bean class="com.llp.spring.bean.Monster" id="monster06"
         p:monsterId="6"
         p:name="红孩儿"
         p:skill="三味真火"
    />
@Test
    public void getBeanByPNameSpace(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Monster bean = ioc.getBean("monster06",Monster.class);
        System.out.println(bean);
    }

4.引用/注入其他bean对象

1.应用案例

beans.xml

<!--1. ref="memberDAO"表示  MemberServiceImpl对象属性memberDAO引用的对象是id=memberDAO的对象
        2. 这里就体现出spring容器的依赖注入
        3. 注意再spring容器中, 他是作为一个整体来执行的, 即如果你引用到一个bean对象, 对你配置的顺序没有要求
        4. 建议还是按顺序,好处是阅读的时候,比较方便-->
    <bean class="com.llp.spring.service.MemberServiceImpl" id="memberService" p:memberDao-ref="memberDao">
        <!--<property name="memberDao" ref="memberDao"/>-->
    </bean>

    <!--引用/注入其他bean对象
	 注意这里的memberDao和MemberServiceImpl配置中的ref一致-->
    <bean class="com.llp.spring.dao.MemberDaoImpl" id="memberDao"/>
//通过ref来设置bean属性
    @Test
    public void setBeanByRef(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        MemberServiceImpl memberService = ioc.getBean("memberService", MemberServiceImpl.class);
        memberService.add();
    }

image-20220514144706964

5.引入/注入内部bean对象

1.应用案例

<!--引用/注入其他bean对象-->
    <bean class="com.llp.spring.dao.MemberDaoImpl" id="memberDao"/>

    <!--配置MemberServiceImpl对象,使用内部bean的方式-->
    <bean class="com.llp.spring.service.MemberServiceImpl" id="memberService2">
        <!--MemberServiceImpl对象中有一个属性memberDao,
            自己配置一个内部的bean-->
        <property name="memberDao">
            <bean class="com.llp.spring.dao.MemberDaoImpl"/>
        </property>
    </bean>
/**
     * 通过内部bean设置属性
     */
    @Test
    public void setBeanByPro(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        MemberServiceImpl memberService = ioc.getBean("memberService2", MemberServiceImpl.class);
        memberService.add();
    }

image-20220514150343688

6.引入/注入集合/数组类型

1.应用案例

Master.java实体类

public class Master {

    private String name;//主人名

    private List<Monster> monsterList;
    private Map<String, Monster> monsterMap;
    private Set<Monster> monsterSet;

    //数组
    private String[] monsterName;

    //Java基础
    //这个Properties 是 Hashtable的子类 , 是key-value的形式
    //这里Properties  key和value 都是String
    private Properties pros;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Monster> getMonsterList() {
        return monsterList;
    }

    public void setMonsterList(List<Monster> monsterList) {
        this.monsterList = monsterList;
    }

    public Map<String, Monster> getMonsterMap() {
        return monsterMap;
    }

    public void setMonsterMap(Map<String, Monster> monsterMap) {
        this.monsterMap = monsterMap;
    }

    public Set<Monster> getMonsterSet() {
        return monsterSet;
    }

    public void setMonsterSet(Set<Monster> monsterSet) {
        this.monsterSet = monsterSet;
    }

    public String[] getMonsterName() {
        return monsterName;
    }

    public void setMonsterName(String[] monsterName) {
        this.monsterName = monsterName;
    }

    public Properties getPros() {
        return pros;
    }

    public void setPros(Properties pros) {
        this.pros = pros;
    }

    @Override
    public String toString() {
        return "Master{" +
                "name='" + name + '\'' +
                ", monsterList=" + monsterList +
                ", monsterMap=" + monsterMap +
                ", monsterSet=" + monsterSet +
                ", monsterName=" + Arrays.toString(monsterName) +
                ", pros=" + pros +
                '}';
    }
}
<!--配置Master对象
    体会 spring 容器配置特点 依赖注入-非常灵活
    -->
    <bean class="com.llp.spring.bean.Master" id="master">
        <property name="name" value="太上老君"/>
        <!--给list属性赋值-->
        <property name="monsterList">
            <list>
                <!--引用的方法-->
                <ref bean="monster01"/>
                <ref bean="monster02"/>
                <!--内部bean-->
                <bean class="com.llp.spring.bean.Monster">
                    <property name="name" value="老鼠精"/>
                    <property name="monsterId" value="100"/>
                    <property name="skill" value="吃粮食"/>
                </bean>
            </list>
        </property>
        <!--给map属性赋值-->
        <property name="monsterMap">
            <map>
                <entry>
                    <key>
                        <value>monster03</value>
                    </key>
                    <!--这里使用的外部bean,引入-->
                    <ref bean="monster03"/>
                </entry>
                <entry>
                    <key>
                        <value>monster04</value>
                    </key>
                    <ref bean="monster04"/>
                </entry>
            </map>
        </property>
        <!--给set属性赋值-->
        <property name="monsterSet">
            <set>
                <ref bean="monster05"/>
                <ref bean="monster06"/>
                <bean class="com.llp.spring.bean.Monster">
                    <property name="name" value="金角大王"/>
                    <property name="skill" value="吐水"/>
                    <property name="monsterId" value="666"/>
                </bean>
            </set>
        </property>
        <!--给数组属性赋值
        array标签中使用 value 还是 bean , ref .. 要根据你的业务决定
        -->
        <property name="monsterName">
            <array>
                <value>小妖怪</value>
                <value>大妖怪</value>
                <value>老妖怪</value>
            </array>
        </property>
        <!--给Properties属性赋值 结构k(String)-v(String)-->
        <property name="pros">
            <props>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
                <prop key="ip">127.0.0.1</prop>
            </props>
        </property>
    </bean>

2.使用细节

  1. 主要掌握 List/Map/Properties 三种集合的使用.

  2. Properties 集合的特点

    1)这个 Properties 是 Hashtable 的子类 , 是 key-value 的形式

    2)key 是 string 而 value 也是 string

7.通过util名称空间创建list

1.应用案例

xmlns:util="http://www.springframework.org/schema/util"

image-20220514155228394

beans.xml

<util:list id="bookList">
        <value>三国演义</value>
        <value>被讨厌的勇气</value>
        <value>西游记</value>
        <value>三字经</value>
    </util:list>
    <!--通过util名称空间创建list-->
    <bean class="com.llp.spring.bean.BookStore" id="bookStore">
        <property name="bookList" ref="bookList"/>
    </bean>

BookStore.java

public class BookStore {
    private List<String> bookList;

    public List<String> getBookList() {
        return bookList;
    }

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

    @Override
    public String toString() {
        return "BookStore{" +
                "bookList=" + bookList +
                '}';
    }
}

测试

@Test
    public void getBeanByUtilList(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        BookStore bean = ioc.getBean("bookStore",BookStore.class);
        System.out.println(bean);
    }

image-20220514155618237

8.级联属性赋值

1.应用案例

<!--级联属性赋值-->
    <bean class="com.llp.spring.bean.Emp" id="emp">
        <property name="name" value="小红"/>
        <property name="dept" ref="dept"/>
        <property name="dept.name" value="后勤"/>
    </bean>
    <bean class="com.llp.spring.bean.Dept" id="dept"/>
/**
     * 级联属性赋值
     */
    @Test
    public void getBeanByRelation(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Emp bean = ioc.getBean("emp", Emp.class);
        System.out.println(bean);
    }

image-20220514160126012

9.通过静态工厂获取对象

1.应用案例

<!--配置monster对象,通过静态工厂获取
        1. 通过静态工厂获取/配置bean
        2. class 是静态工厂类的全路径
        3. factory-method 表示是指定静态工厂类的哪个方法返回对象
        4. constructor-arg value="monster02" value是指定要返回静态工厂的哪个对象-->
    <bean id="my_monster01" class="com.llp.spring.factory.MyStaticFactory" factory-method="getBean">
        <!--注意这里的value,通过value从map中获取monster对象	   ==>MyStaticFactory.getBean(monsterMap.get(monster02))-->
        <constructor-arg value="monster02"/>
    </bean>

MyStaticFactory工厂类

/**
 * 可以创建返回Monster对象
 */
public class MyStaticFactory {
    private static Map<String, Monster> monsterMap;

    //使用static 代码块进行初始化
    static{
        monsterMap = new HashMap<>();
        monsterMap.put("monster01",new Monster(100,"阿牛","芭蕉扇"));
        monsterMap.put("monster02",new Monster(200,"狐狸精","美人计"));
    }

    //提供一个方法返回monster对象
    public static Monster getBean(String key){
        return monsterMap.get(key);
    }

}

测试类

@Test
    public void getBeanByStaticFactory(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Monster bean = ioc.getBean("my_monster01", Monster.class);
        System.out.println(bean);
    }

测试结果

image-20220514161635486

10.通过实例工厂获取对象

1.应用案例

beans.xml

<!--配置一个实例工厂对象-->
<bean class="com.llp.spring.factory.MyInstanceFactory" id="instanceFactory"/>
<!--配置monster对象,通过实例工厂
    1. factory-bean 指定使用哪个实例工厂对象返回bean
    2. factory-method 指定使用实例工厂对象的哪个方法返回bean
    3. constructor-arg value="monster03" 指定获取到实例工厂中的哪个monster
-->
<bean id="my_monster02" factory-bean="instanceFactory" factory-method="getMonster">
    <!--创建MyInstanceFactory工厂类
        1.MyInstanceFactory instanceFactory = new MyInstanceFactory();
        通过value传入getMonster中获取Monster对象
        2.instanceFactory.getMonster(monster_Map.get(monster03));
        -->
    <constructor-arg value="monster03"/>
</bean>

实例工厂类

public class MyInstanceFactory {
    private static Map<String, Monster> monster_Map;
    {
        monster_Map = new HashMap<>();
        monster_Map.put("monster03",new Monster(300,"孙悟空","耍光棍"));
        monster_Map.put("monster04",new Monster(400,"蜘蛛精","盘丝"));
    }

    public Monster getMonster(String key){
        return monster_Map.get(key);
    }
}

测试类

@Test
    public void getBeanByInstanceFactory(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Monster bean01 = ioc.getBean("my_monster02", Monster.class);
        Monster bean02 = ioc.getBean("my_monster02", Monster.class);
        System.out.println(bean01);
        /**
         * 这里调用my_monster02 都是调用已经创建好的MyInstanceFactory对象的getMonster方法
         * 注意的是,MyInstanceFactory是单独配置的并不会创建两次
         */
        System.out.println(bean01 == bean02);//true 
    }

测试结果

image-20220514163106197

11.通过FactoryBean获取对象(重点)

1.应用案例

FactoryBean

/**
 * 一个FactoryBean
 */
public class MyFactoryBean implements FactoryBean<Monster> {

    //这个就是你配置时候,指定要获取的对象对应key
    private String key;
    private Map<String, Monster> monster_map;

    {   //代码块,完成初始化
        monster_map = new HashMap<>();
        monster_map.put("monster03", new Monster(300, "牛魔王~", "芭蕉扇~"));
        monster_map.put("monster04", new Monster(400, "狐狸精~", "美人计~"));
    }

    public void setKey(String key) {
        this.key = key;
    }

    @Override
    public Monster getObject() throws Exception {
        return monster_map.get(key);
    }

    @Override
    public Class<?> getObjectType() {
        return Monster.class;
    }

    @Override
    public boolean isSingleton() {//这里指定是否返是单例
        return true;
    }
}

beans.xml

<!--配置Monster对象,通过FactoryBean获取
        1. class 指定使用的FactoryBean
        2. key表示就是 MyFactoryBean 属性key
        3. value就是你要获取的对象对应key
    -->
    <bean id="my_monster03" class="com.llp.spring.factory.MyFactoryBean">
        <!--
            MyFactoryBean myFactoryBean = new MyFactoryBean();
            myFactoryBean.setKey(monster_map.get(monster04))
        -->
        <property name="key" value="monster04"/>
    </bean>

测试类

@Test
public void getBeanByFactoryBean(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster my_monster03 = ioc.getBean("my_monster03", Monster.class);
    System.out.println(my_monster03);
}

测试结果

image-20220514164717458

12.bean配置信息重用(继承)

1.应用案例

beans.xml

<!--配置Monster对象-->
<bean id="monster10" class="com.llp.spring.bean.Monster">
    <property name="monsterId" value="10"/>
    <property name="name" value="蜈蚣精"/>
<property name="skill" value="蜇人"/></bean>

<!--
1.配置Monster对象
2.但是这个对象的属性值和id=monster10的对象属性一致
3.parent="monster10"表示指定当前配置对象的属性值从id=monster10的对象获取
-->
<bean id="monster11" class="com.llp.spring.bean.Monster" parent="monster10"/>

测试类

/**
     * 配置bean通过继承(extends)
     */
    @Test
    public void getBeanByExtends(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Monster monster10 = ioc.getBean("monster10", Monster.class);
        Monster monster11 = ioc.getBean("monster11", Monster.class);
        System.out.println(monster10);
        System.out.println(monster11);
        //monster11对象的属性值继承至monster10,但monster11和monster10并不是同一个对象
        System.out.println(monster10 == monster11);//false
    }

测试结果

image-20220514165622683

注意

<!--配置Monster对象
1.如果bean指定了abstract="true",表示该对象适用于被继承的
2.这个bean本身是不能被实例化的
3.这个bean的价值就是专门用来被继承的
-->
<bean id="monster12" class="com.llp.spring.bean.Monster" abstract="true">
    <property name="monsterId" value="10"/>
    <property name="name" value="蜈蚣精"/>
    <property name="skill" value="蜇人"/>
</bean>

image-20220514165904854

13.bean创建顺序

1.应用案例

beans.xml

<!--测试bean对象的创建顺序
        1. 在默认情况下, bean创建的顺序是按照配置顺序来的(从上到下)
        2. 但是如果我们增加了 depends-on="department01" 这时就会先创建id= department01对象
    -->
    <bean id="student01" class="com.llp.spring.bean.Student" depends-on="department01"/>
    <bean id="department01" class="com.llp.spring.bean.Department"/>

测试类

@Test
    public void getBeanByCreate(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Student student = ioc.getBean("student01", Student.class);
        Department department = ioc.getBean("department01", Department.class);
    }

测试结果

image-20220514171640316

2.一个问题?

● 问题说明

  1. 先看下面的配置, 请问两个 bean 创建的顺序是什么? 并分析执行流程

​ 1)先创建 id=memberDAOImpl

​ 2)再创建 id = memberServiceImpl

​ 3)调用 memberServiceImpl.setMemberDAO() 完成引用

image-20220514171835715

  1. 先看下面的配置, 请问两个 bean 创建的顺序是什么, 并分析执行流程

​ 1)先创建 id = memberServiceImpl

​ 2)再创建 id=memberDAOImpl

​ 3)用 memberServiceImpl.setMemberDAO() 完成引用

image-20220514171945488

14.bean对象的单例和多例

1.应用案例

在 spring 的 ioc 容器, 在默认是按照单例创建的,即配置一个 bean 对象后,ioc 容器只会创建一个 bean 实例。

如果,我们希望 ioc 容器配置的某个 bean 对象,是以多个实例形式创建的则可以通过配置scope="prototype" 来指定

Cat.java

public class Cat {
    private Integer id;
    private String name;

    public Cat() {
        System.out.println("Cat() 被执行...");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //@Override
    //public String toString() {
    //    return "Cat{" +
    //            "id=" + id +
    //            ", name='" + name + '\'' +
    //            '}';
    //}
}

beans.xml

<!--配置Cat对象
    1. 在默认情况下 scope属性是 singleton
    2. 在ioc容器中, 只要有一个这个bean对象
    3. 当程序员执行getBean时, 返回的的是同一个对象
    4. 如果我们希望每次getBean返回一个新的Bean对象,则可以scope="prototype"
    5. 如果bean的配置是 scope="singleton" lazy-init="true" 这时,ioc容器就不会提前创建该对象
       , 而是当执行getBean方法的时候,才会创建对象
-->
 <bean id="cat" class="com.llp.spring.bean.Cat" scope="prototype" lazy-init="false">
     <property name="id" value="100"/>
     <property name="name" value="小花猫"/>
 </bean>

测试

@Test
    public void getBeanByPrototype(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        Cat cat01 = ioc.getBean("cat", Cat.class);
        Cat cat02 = ioc.getBean("cat", Cat.class);
        System.out.println(cat01);
        System.out.println(cat02);
        //false
        System.out.println(cat01==cat02);
    }

测试结果

image-20220514172850339

2.使用细节

  1. 默认是单例singleton, 在启动容器时, 默认就会创建 , 并放入到singletonObjects集合
  2. 当 < bean scope="prototype" > 设置为多实例机制后, 该 bean 是在 getBean()时才创建
  3. 如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才 创 建 , 可 以 指 定 懒 加 载 lazy-init="true" (注意默认是 false)
  4. 通常情况下, lazy-init 就使用默认值 false , 在开发看来, 用空间换时间是值得的, 除非有特殊的要求.
  5. 如果 scope="prototype" 这时你的 lazy-init 属性的值不管是 ture, 还是 false 都是在getBean 时候才创建对象.

15.bean的生命周期

1.应用案例

● 说明: bean 对象创建是由 JVM 完成的,然后执行如下方法

  1. 执行构造器
  2. 执行 set 相关方法
  3. 调用 bean 的初始化的方法(需要配置)
  4. 使用 bean
  5. 当容器关闭时候,调用 bean 的销毁方法(需要配置)

House.java

public class House {
    private String name;

    public House() {
        System.out.println("House() 构造器...");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("House setName()=" + name);
        this.name = name;
    }

    //1. 这个方法是程序员来编写的.
    //2. 根据自己的业务逻辑来写.
    public void init() {
        System.out.println("House init()..");
    }

    //1. 这个方法是程序员来编写的.
    //2. 根据自己的业务逻辑来写.
    //3. 名字也不是固定的
    public void destroy() {
        System.out.println("House destroy()..");
    }

    @Override
    public String toString() {
        return "House{" +
                "name='" + name + '\'' +
                '}';
    }
}

beans.xml

<!--配置House对象,演示整个Bean的生命周期
        1. init-method="init" 指定bean的初始化方法 , 在setter方法后执行
        2. init方法执行的时机,有spring容器来控制
        3. destroy-method="destroy" 指定bean的销毁方法, 在容器关闭的时候执行
        4. destroy方法执行的时机,有spring容器来控制
    -->
    <bean id="house" class="com.llp.spring.bean.House" init-method="init" destroy-method="destroy" >
        <property name="name" value="北京豪宅"/>
    </bean>

测试

//测试Bean的生命周期
    @Test
    public void testBeanLife() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        House house = ioc.getBean("house", House.class);
        System.out.println("使用house=" + house);
        //关闭容器
        //1. ioc的编译类型 ApplicationContext , 运行类型 ClassPathXmlApplicationContext
        //2. 因为ClassPathXmlApplicationContext 实现了 ConfigurableApplicationContext
        //3. ClassPathXmlApplicationContext 是有close
        //4. 将ioc 转成ClassPathXmlApplicationContext,再调用close
        //ioc.close();
        //关闭ioc容器.
        ((ConfigurableApplicationContext) ioc).close();
    }

image-20220514180742054

2.使用细节

  1. 初始化 init 方法和 destory 方法, 是程序员来指定
  2. 销毁方法就是当关闭容器时,才会被调用.

16.配置bean的后置处理器【比较难】

1.应用案例

● 说明

  1. 在 spring 的 ioc 容器,可以配置 bean 的后置处理器
  2. 该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用
  3. 程序员可以在后置处理器中编写自己的代码

● 应用实例演示

bean—House

public class House {
    private String name;

    public House() {
        System.out.println("House() 构造器...");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("House setName()=" + name);
        this.name = name;
    }

    //1. 这个方法是程序员来编写的.
    //2. 根据自己的业务逻辑来写.
    public void init() {
        System.out.println("House init()..");
    }

    //1. 这个方法是程序员来编写的.
    //2. 根据自己的业务逻辑来写.
    //3. 名字也不是固定的
    public void destroy() {
        System.out.println("House destroy()..");
    }

    @Override
    public String toString() {
        return "House{" +
                "name='" + name + '\'' +
                '}';
    }
}

beans2.xml

<!--配置house对象-->
<bean class="com.llp.spring.bean.House" id="house" init-method="init" destroy-method="destroy">
    <property name="name" value="我的房子"/>
</bean>

    <!--配置后置处理器对象
    1. 当我们在beans02.xml 容器配置文件 配置了 MyBeanPostProcessor
    2. 这时后置处理器对象,就会作用在该容器创建的Bean对象
    3. 已经是针对所有对象编程->切面编程AOP
    -->
<bean class="com.llp.spring.bean.MyBeanPostProcessor"/>

后置处理器

/**
 * 后置处理器
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * 什么时候被调用:在Bean的init方法前被调用
     * @param bean 传入的在ioc容器中创建/配置的bean
     * @param beanName 传入的ioc容器中创建/配置bean的id
     * @return Object 程序员对传入的bean 进行修改/处理【如果有需要的话】 ,返回
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization(),bean="+bean+",beanName="+beanName);
        return bean;
    }

    /**
     * 什么时候被调用:在Bean的init方法后被调用
     * @param bean 传入的在ioc容器中创建/配置的bean
     * @param beanName 传入的ioc容器中创建/配置bean的id
     * @return 程序员对传入的bean 进行修改/处理【如果有需要的话】 ,返回
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization(),bean="+bean+",beanName="+beanName);
        return bean;
    }
}
@Test
public void testBeanPostProcessor(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans02.xml");
    House house = ioc.getBean("house", House.class);
    System.out.println(house);
}

image-20220514212043596

2.其他说明

1、怎么执行到这个方法?=> 使用 AOP(反射+动态代理+IO+容器+注解)

2、有什么用?=> 可以对 IOC 容器中所有的对象进行统一处理 ,比如 日志处理/权限的校验

-初步体验案例: 如果类型是 House 的统一改成 上海豪宅

3、针对容器的所有对象吗? 是的=>切面编程特点

17.通过属性文件给 bean 注入值

1.应用实例

beans02.xml

<!--指定属性文件
location 表示指定属性文件的位置-->
<context:property-placeholder location="classpath:my.properties"/>
<!--配置Monster对象
1.通过属性文件给monster对象的属性赋值
2.这是我们的属性值通过${属性名}
3.这里说的属性名就是my.properties文件中的k=v的k-->
<bean class="com.llp.spring.bean.Monster" id="monster1000">
    <property name="monsterId" value="${monsterId}"/>
    <property name="name" value="${name}"/>
    <property name="skill" value="${skill}"/>
</bean>

测试

@Test
public void setBeanByFile(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans02.xml");
    Monster monster1000 = ioc.getBean("monster1000", Monster.class);
    //Monster{monsterId=100, name='琪琪', skill='魅惑'}
    System.out.println(monster1000);
}

18.基于XML的bean的自动装配

1.应用实例-byType

public class OrderDao {
    public void saveOrder(){
        System.out.println("保存 一个订单...");
    }
}
public class OrderServiceImpl {
    private OrderDao orderDao;

    public OrderDao getOrderDao() {
        return orderDao;
    }

    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void saveOrder(){
        orderDao.saveOrder();
    }
}
public class OrderAction {
    private OrderServiceImpl orderService;

    public OrderServiceImpl getOrderService() {
        return orderService;
    }

    public void setOrderService(OrderServiceImpl orderService) {
        this.orderService = orderService;
    }

    public void saveOrder(){
        orderService.saveOrder();
    }
}
<bean class="com.llp.spring.dao.OrderDao" id="orderDao"/>

    <!--配置OrderService对象
    1. autowire="byType" 表示 在创建 orderService时
       通过类型的方式 给对象属性 自动完成赋值/引用
    2. 比如OrderService 对象有 private OrderDao orderDao
    3. 就会在容器中去找有没有 OrderDao类型对象
    4. 如果有,就会自动的装配, 如果是按照 byType 方式来装配, 这个容器中,不能有两个
      的OrderDao类型对象
    5. 如果你的对象没有属性,  autowire就没有必要写
    6. 其它类推..
    -->
<bean class="com.llp.spring.service.OrderServiceImpl" id="orderService" autowire="byType"/>
<bean class="com.llp.spring.web.OrderAction" id="orderAction" autowire="byType"/>

image-20220514221547177

2.应用实例-byName

<bean class="com.llp.spring.dao.OrderDao" id="orderDao"/>

    <!--配置OrderService对象
        1. 如果我们设置的是 autowire="byName" 表示通过名字完成自动装配
        2. 比如下面的 autowire="byName" class="com.llp.spring.service.OrderService"
           1) 先看 OrderService 属性 private OrderDao orderDao
           2) 再根据这个属性的setXxx()方法的 xxx 来找对象id
           3) public void setOrderDao() 就会找id=orderDao对象来进行自动装配
           4) 如果没有就装配失败
    -->
<bean class="com.llp.spring.service.OrderServiceImpl" id="orderService" autowire="byName"/>
<bean class="com.llp.spring.web.OrderAction" id="orderAction" autowire="byName"/>
@Test
public void testAutowaireByName(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans03.xml");

    OrderAction orderAction = ioc.getBean("orderAction", OrderAction.class);
    System.out.println(orderAction.getOrderService());
    System.out.println(orderAction.getOrderService().getOrderDao());
}

19.Sping el表达式

1.应用实例

● 说明

  1. Spring Expression Language,Spring 表达式语言,简称 SpEL。支持运行时查询并可以操作对象。
  2. 和 EL 表达式一样,SpEL 根据 JavaBean 风格的 getXxx()、setXxx()方法定义的属性访问对象
  3. SpEL 使用#{…}作为定界符,所有在大框号中的字符都将被认为是 SpEL 表达式。
public class SpELBean {

    private String name;
    private Monster monster;
    private String monsterName;
    private String crySound; //叫声
    private String bookName;
    private Double result;

    public SpELBean() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Monster getMonster() {
        return monster;
    }

    public void setMonster(Monster monster) {
        this.monster = monster;
    }

    public String getMonsterName() {
        return monsterName;
    }

    public void setMonsterName(String monsterName) {
        this.monsterName = monsterName;
    }

    public String getCrySound() {
        return crySound;
    }

    public void setCrySound(String crySound) {
        this.crySound = crySound;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public Double getResult() {
        return result;
    }

    public void setResult(Double result) {
        this.result = result;
    }

    //cry 方法会返回字符串
    public String cry(String sound) {
        return "发出 " + sound + "叫声...";
    }

    //read 返回字符串
    public static String read(String bookName) {
        return "正在看 " + bookName;
    }

    @Override
    public String toString() {
        return "SpELBean{" +
                "name='" + name + '\'' +
                ", monster=" + monster +
                ", monsterName='" + monsterName + '\'' +
                ", crySound='" + crySound + '\'' +
                ", bookName='" + bookName + '\'' +
                ", result=" + result +
                '}';
    }
}
<!--配置一个monster对象-->
<bean id="monster01" class="com.llp.spring.bean.Monster">
    <property name="monsterId" value="100"/>
    <property name="name" value="蜈蚣精~"/>
    <property name="skill" value="蜇人~"/>
</bean>

<!-- spring el 表达式使用
 1. 通过spel给bean的属性赋值
 -->
<bean id="spELBean" class="com.llp.spring.bean.SpELBean">
    <!-- sp el 给字面量 -->
    <property name="name" value="#{'llp'}"/>
    <!-- sp el 引用其它bean -->
    <property name="monster" value="#{monster01}"/>
    <!-- sp el 引用其它bean的属性值 -->
    <property name="monsterName" value="#{monster01.name}"/>
    <!-- sp el 调用普通方法(返回值)  赋值 -->
    <property name="crySound" value="#{spELBean.cry('喵喵的..')}"/>
    <!-- sp el 调用静态方法(返回值) 赋值 -->
    <property name="bookName" value="#{T(com.llp.spring.bean.SpELBean).read('天龙八部')}"/>
    <!-- sp el 通过运算赋值 -->
    <property name="result" value="#{89*1.2}"/>
</bean>
//通过spring el 对属性赋值
@Test
public void setBeanBySpel() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans04.xml");
    SpELBean spELBean = ioc.getBean("spELBean", SpELBean.class);
    System.out.println("spELBean=" + spELBean);
}

测试结果

image-20220514223804096


标题:Spring-IOC—基于XML配置Bean
作者:llp
地址:https://llinp.cn/articles/2022/05/14/1652523349135.html