Spring----Spring容器机制+Spring配置bean

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        1. 一个bean,就是一个java对象
        2. class属性用于指定类的全路径 ===> spring底层使用反射
        3. id属性表示改java对象在spring容器中的id,通过id可以获取到对象
        4. <property name="monsterId" value="100"></property> 用于给该对象属性赋值
    -->
    <bean class="com.zzti.spring.entity.Monster" id="monster01">
        <property name="monsterId" value="100"></property>
        <property name="monsterName" value="牛魔王"></property>
        <property name="skill" value="芭蕉扇"></property>
    </bean>
    <bean class="com.zzti.spring.entity.Monster" id="monster02">
        <property name="monsterId" value="200"></property>
        <property name="monsterName" value="孙悟空"></property>
        <property name="skill" value="金箍棒"></property>
    </bean>

</beans>
package com.zzti.spring.test;


import com.zzti.spring.entity.Monster;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.File;

public class SpringBootTest {

    @Test
    public void getMonster(){

        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:beans.xml");
        //默认返回的是Object,但是运行类型是Monster
        //Object monster01 = applicationContext.getBean("monster01");
        //运行类型是= class com.zzti.spring.entity.Monster
        //System.out.println("monster01= " + monster01 +",运行类型是= "+ monster01.getClass());
        //System.out.println("monster01.name=" + monster01);

        //也可以直接类型强转
        Monster monster011 = (Monster) applicationContext.getBean("monster01");
        //monster011= Monster{monsterId=100, monsterName='牛魔王', skill='芭蕉扇'}
        System.out.println("monster011= " + monster011 );
        System.out.println("monster011.name=" + monster011.getMonsterName());

        //也可以在获取的时候,直接指定class类型
        Monster monster012 = applicationContext.getBean("monster01", Monster.class);
        System.out.println("monster012= " + monster012);

    }

    //2. 验证类加载路径
    @Test
    public void classPath(){
        File file = new File(this.getClass().getResource("/").getPath());

        //D:\Exercise_items\hsp_spring\target\classes
        System.out.println(file);
    }

	//3. 查看容器中注入了那些bean对象,会输出bean的id
    @Test
    public void getBeansId(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:beans.xml");
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println("~~~" + beanDefinitionName);
        }

    }

}

三. Spring容器结构/机制

在这里插入图片描述
在这里插入图片描述

  1. 在beanFactory下面有beanDefintionMap, beanDefintionMap类型是ConcurrentHashMap集合,其存放beans.xml中bean节点配置的bean对象的信息
  2. 在beanDefintionMap中有属性table,table是数组,类型是ConcurrentHashMap$Node。因为是数组,所以可以存放很多的bean对象信息,就是beans.xml配置。初始化是512,当超过时,会自动扩容。
  3. 通过hash算法,我们的Monster01对象的信息就保存在index=217的位置,保存是以ConcurrentHashMap$Node类型保存;key就是beans.xml中配置的id(monster01),value是monster01对象的信息[比如: 属性/属性值/类信息/是不是懒加载]
    在这里插入图片描述
  4. propertyValues中记录的就是beans.xml中配置的monster01对象的属性名/属性值
    在这里插入图片描述
  5. 在beanFactory中,有属性singletonObjects, 类型是ConcurrentHashMap;singletonObjects下面还有一个属性table,类型是ConcurrentHashMap$Node。如果你在beans.xml文件中配置的对象是 单例的 就会初始化在table中。
    在这里插入图片描述
  6. 在beanFactory中,还有一个属性beanDefinitionNames。记录了我们在beans.xml中配置的bean名称,方便查找。
    在这里插入图片描述

四. 手动开发简单的Spring基于XML配置的程序

  1. 需求说明
    自己写一个简单的Spring容器,通过读取beans.xml,获取第一个Javabean: Monster01对象,并给该对象属性赋值,放入到容器中,输出该对象信息。
  2. 思路分析
    在这里插入图片描述
  3. 完成步骤
package com.zzti.spring.applicationcontext;
/**
 * 这个程序用于实现spring的一个简单容器机制, 后面会有详细的实现
 * 这里我们实现如何将beans.xml文件进行解析, 并生成对象,放入容器中
 * 提供一个getBean(id),返回对应的对象
 * 来简单的理解一下spring容器的机制
 */
public class SpringApplicationContext {
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();

    //接收一个容器的配置文件(beans.xml),该文件默认放在src下面
    public SpringApplicationContext(String iocBeanXmlFile) throws Exception {
        //1. 得到类加载的路径
        String path = this.getClass().getResource("/").getPath();
        //path= /D:/Exercise%20items/hsp_spring/target/classes/
        System.out.println("path decode前= " + path);
        //添加如下代码,不然系统找不到path路径
        path = java.net.URLDecoder.decode(path, "utf-8");
        //path decode后= /D:/Exercise items/hsp_spring/target/classes/
        System.out.println("path decode后= " + path);
        //2. 创建saxReader
        SAXReader saxReader = new SAXReader();
        //3. 得到document对象
        Document document = saxReader.read(new File(path + iocBeanXmlFile));
        //4. 得到rootDocument
        Element rootElement = document.getRootElement();
        //5. 得到第一个bean(monster01)
        Element bean = (Element) rootElement.elements("bean").get(0);
        //6. 得到第一个bean的相关属性
        String id = bean.attributeValue("id");
        String classFullPath = bean.attributeValue("class");
        List<Element> property = bean.elements("property");
        //获取到相关属性值
        Integer monsterId = Integer.parseInt(property.get(0).attributeValue("value"));
        String monsterName = property.get(1).attributeValue("value");
        String skill = property.get(2).attributeValue("value");

        //7. 使用反射,创建对象
        Class<?> aClass = Class.forName(classFullPath);
        // 这里 o 对象就是Monster对象
        Monster o = (Monster) aClass.newInstance();
        //给 o 对象赋值, 通过反射中的setter方法来赋值的,这里就简写
        Method[] declaredMethods = aClass.getDeclaredMethods();
        o.setMonsterId(monsterId);
        o.setMonsterName(monsterName);
        o.setSkill(skill);

        //将床架好的对象,放入到ConcurrentHashMap中的singletonObjects
        singletonObjects.put(id,o);

    }

    public Object getBean(String id){
        return singletonObjects.get(id);
    }
}
package com.zzti.spring.test;

import com.zzti.spring.applicationcontext.SpringApplicationContext;
import com.zzti.spring.entity.Monster;

public class SpringApplicationContextTest {

    public static void main(String[] args) throws Exception {
        SpringApplicationContext springApplicationContext =
                new SpringApplicationContext("beans.xml");
        Monster monster01 = (Monster) springApplicationContext.getBean("monster01");

        System.out.println("monster01= " + monster01);
        System.out.println("monster01.Name= " + monster01.getMonsterName());
        System.out.println("monster01.Skill= " + monster01.getSkill());
        System.out.println("ok~~");
    }
}

问题1: 假如没有在beans.xml文件中分配id,运行时会不会报错?
不会报错,能正常运行;系统会默认分配id,分配规则是: 全类名+#+数字
在这里插入图片描述

package com.zzti.spring.homework;

import com.zzti.spring.entity.Monster;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HomeWork01 {
    @Test
    public void getMonster(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Monster monster01 = applicationContext.getBean("com.zzti.spring.entity.Monster#0", Monster.class);
        System.out.println("monster01= " + monster01);
        System.out.println("monster01.minsterId= " + monster01.getMonsterId());

        Monster monster02 = applicationContext.getBean("com.zzti.spring.entity.Monster#1", Monster.class);
        System.out.println("monster02= " + monster02);
        System.out.println("monster02.minsterId= " + monster02.getMonsterId());

        System.out.println("测试没有分配id的情况~~~ok~~");

    }
}
========================测试结果================================
monster01= Monster{monsterId=100, monsterName='牛魔王', skill='芭蕉扇'}
monster01.minsterId= 100
monster02= Monster{monsterId=200, monsterName='孙悟空', skill='金箍棒'}
monster02.minsterId= 200
测试没有分配id的情况~~~ok~~

五. Spring配置/管理bean介绍

1. Bean管理包括两方面

创建bean对象
给bean注入属性

2.Bean配置方式

基于XML文件配置方式
基于注解方式

3. 介绍基于XML配置bean
3.1 通过类型来获取bean
 <bean class="com.zzti.spring.entity.Monster" id="monster01">
        <property name="monsterId" value="100"></property>
        <property name="monsterName" value="牛魔王"></property>
        <property name="skill" value="芭蕉扇"></property>
 </bean>
@Test
public void getMonsterByType(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster = applicationContext.getBean(Monster.class);
    System.out.println("monster= " + monster);
}
=======================================
monster= Monster{monsterId=100, monsterName='牛魔王', skill='芭蕉扇'}
注意细节:
	按类型来获取bean,要求IOC容器中的同一个类的bean只能有一个,否则会抛出异常(bean不唯一)
这种方式的应用场景: 比如:xxxAction/xxxServlet/xxxController,或者xxxService;在一个线程中只需要一个对象实例(单例)的情况
    在容器配置文件(beans.xml)中给属性赋值,底层是通过setter()完成的,这也是为什么我们需要提供setter()的原因。	
3.2 通过构造器配置bean
<!--通过构造器来配置bean-->
    <bean id="monster03" class="com.zzti.spring.entity.Monster">
        <constructor-arg value="2" index="0"></constructor-arg>
        <constructor-arg value="蜘蛛精" index="1"></constructor-arg>
        <constructor-arg value="吐口水" index="2"></constructor-arg>
    </bean>

    <bean id="monster031" class="com.zzti.spring.entity.Monster">
        <constructor-arg value="3" type="java.lang.Integer"></constructor-arg>
        <constructor-arg value="白骨精" type="java.lang.String"></constructor-arg>
        <constructor-arg value="白骨鞭" type="java.lang.String"></constructor-arg>
    </bean>
@Test
public void getMonsterByConstructor(){
   ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
   Monster monster03 = (Monster) applicationContext.getBean("monster03");
   Monster monster031 = (Monster) applicationContext.getBean("monster031");
   System.out.println("monster03= " + monster03);
   System.out.println("monster031= " + monster031);
}
=====================================================
monster03= Monster{monsterId=2, monsterName='蜘蛛精', skill='吐口水'}
monster031= Monster{monsterId=3, monsterName='白骨精', skill='白骨鞭'}
使用细节:
	1. 通过index属性来区分是第几个参数
	2. 通过type属性来区分是什么类型(按照顺序)
3.3 通过P名称空间配置bean
xmlns:p="http://www.springframework.org/schema/p"
<!--通过P命名空间来配置bean-->
<bean id="monster06" class="com.zzti.spring.entity.Monster"
      p:monsterId="600" p:monsterName="红孩儿" p:skill="三味真火" />
@Test
public void getMonsterForP(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster06 = (Monster) applicationContext.getBean("monster06");
    System.out.println("monster06= " + monster06);
}
3.4 通过ref引用 / 注入其他bean对象
<!--通过ref引用 / 注入其他bean对象-->
<bean id="memberServiceImpl" class="com.zzti.spring.service.MemberServiceImpl">
    <!--ref表示memberDao这个属性将引用或指向id=memberDaoImpl对象-->
    <property name="memberDao" ref="memberDaoImpl"></property>
</bean>
<bean id="memberDaoImpl" class="com.zzti.spring.dao.MemberDaoImpl"></bean>
@Test
public void getBeanByRef(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    MemberServiceImpl memberServiceImpl = applicationContext.getBean("memberServiceImpl", MemberServiceImpl.class);
    memberServiceImpl.add();
}
===============================================
MemberServiceImpl 无参构造器~~
MemberDaoImpl 无参构造器~~
MemberServiceImpl add()方法
MemberDaoImpl add()方法~~
3.5 引用 / 注入内部bean对象
 <!--通过property注入内部bean对象, 直接在配置bean时注入-->
    <bean id="memberServiceImpl02" class="com.zzti.spring.service.MemberServiceImpl">
        <property name="memberDao">
            <bean class="com.zzti.spring.dao.MemberDaoImpl"></bean>
        </property>
    </bean>
@Test
public void getBeanByProperty(){
   ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
   MemberServiceImpl memberServiceImpl02 = applicationContext.getBean("memberServiceImpl02", MemberServiceImpl.class);
   memberServiceImpl02.add();
}
=========================================================
MemberServiceImpl 无参构造器~~
MemberDaoImpl 无参构造器~~
MemberServiceImpl 无参构造器~~
MemberDaoImpl 无参构造器~~
MemberServiceImpl add()方法
MemberDaoImpl add()方法~~
3.6 引用 / 注入 集合/ 数组类型
<!--给集合属性注入值-->
    <bean id="master01" class="com.zzti.spring.entity.Master">
        <property name="name" value="太上老君"></property>
        <!--给bean对象的list集合赋值-->
        <property name="monsterList">
            <list>
                <ref bean="monster02"></ref>
                <ref bean="monster03"></ref>
            </list>
        </property>
        <!--给bean对象的map集合赋值-->
        <property name="monsterMap">
            <map>
                <entry>
                    <key>
                        <value>monsterKey01</value>
                    </key>
                    <ref bean="monster01"></ref>
                </entry>
                <entry>
                    <key>
                        <value>monsterKey02</value>
                    </key>
                    <ref bean="monster031"></ref>
                </entry>
            </map>
        </property>
        <!--给bean对象的properties集合赋值-->
        <property name="pros">
            <props>
                <prop key="k1">Java工程师</prop>
                <prop key="k2">前端工程师</prop>
                <prop key="k3">大数据工程师</prop>
            </props>
        </property>
        <!--给bean对象的数组属性赋值-->
        <property name="monsterName">
            <array>
                <value>金角大王</value>
                <value>银角大王</value>
            </array>
        </property>
        <!--给bean对象的set属性赋值-->
        <property name="monsterSet">
            <set>
                <ref bean="monster01"></ref>
                <bean class="com.zzti.spring.entity.Monster">
                    <property name="monsterId" value="123"></property>
                    <property name="monsterName" value="玉兔精"></property>
                    <property name="skill" value="吃唐僧肉"></property>
                </bean>
            </set>
        </property>

    </bean>

@Test
public void setCollectionByProperty(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Master master01 = applicationContext.getBean("master01", Master.class);
    //获取list集合
    System.out.println("===============list==================");
    List<Monster> monsterList = master01.getMonsterList();
    for (Monster monster : monsterList) {
        System.out.println("获取list= " + monster);
    }
    //获取map集合
    System.out.println("===========map====================");
    Map<String, Monster> monsterMap = master01.getMonsterMap();
    Set<Map.Entry<String, Monster>> entrySet = monsterMap.entrySet();
    for (Map.Entry<String, Monster> entry : entrySet) {
        System.out.println("获取map中entry= " + entry);
    }
    //获取properties
    System.out.println("=============properties====================");
    Properties pros = master01.getPros();
    String property1 = pros.getProperty("k1");
    String property2 = pros.getProperty("k2");
    String property3 = pros.getProperty("k3");
    System.out.println(property1 +"\t"+ property2 +"\t"+ property3);

    //获取数组
    System.out.println("=============数组======================");
    String[] monsterName = master01.getMonsterName();
    for (String s : monsterName) {
        System.out.println("妖怪名称= " + s);
    }

    //获取set
    System.out.println("=================set==============");
    Set<Monster> monsterSet = master01.getMonsterSet();
    for (Monster monster : monsterSet) {
        System.out.println("monsterSet= " + monster);
    }
}
===============list==================
获取list= Monster{monsterId=200, monsterName='孙悟空', skill='金箍棒'}
获取list= Monster{monsterId=2, monsterName='蜘蛛精', skill='吐口水'}
===========map====================
获取map中entry= monsterKey01=Monster{monsterId=100, monsterName='牛魔王', skill='芭蕉扇'}
获取map中entry= monsterKey02=Monster{monsterId=3, monsterName='白骨精', skill='白骨鞭'}
=============properties====================
Java工程师	前端工程师	大数据工程师
=============数组======================
妖怪名称= 金角大王
妖怪名称= 银角大王
=================set==============
monsterSet= Monster{monsterId=100, monsterName='牛魔王', skill='芭蕉扇'}
monsterSet= Monster{monsterId=123, monsterName='玉兔精', skill='吃唐僧肉'}
注意细节:
	1. 主要掌握List/Map/Properties三种集合的使用
	2. Properties集合的特点。properties是hashtable的子类,是key-value的形式;key和value都是string
3.7 通过util名称空间创建bean
<!--通过util名称空间来创建list集合,可以当做创建bean对象的工具来使用-->
    <util:list id="myBookList">
        <value>三国演义</value>
        <value>西游记</value>
        <value>红楼梦</value>
        <value>水浒传</value>
    </util:list>
    <bean id="book" class="com.zzti.spring.entity.Book">
        <property name="bookList" ref="myBookList"></property>
    </bean>
@Test
public void getListByUtil() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Book book = applicationContext.getBean("book", Book.class);
    List<String> bookList = book.getBookList();
    for (String s : bookList) {
        System.out.println("bookList= " + s);
    }
}
=====================================================
bookList= 三国演义
bookList= 西游记
bookList= 红楼梦
bookList= 水浒传
3.8 级联属性赋值(直接给对象的属性赋值)
 <!--级联属性赋值-->
    <bean id="emp" class="com.zzti.spring.entity.Emp">
        <property name="name" value="pt"></property>
        <property name="dept" value="dept"></property>
        <property name="dept.name" value="java开发部"></property>
    </bean>
    <bean id="dept" class="com.zzti.spring.entity.Dept">

    </bean>
@Test
 public void setProByRelation() {
     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
     Emp emp = applicationContext.getBean("emp", Emp.class);
     System.out.println("emp.dept= " + emp.getDept().getName());
     System.out.println("emp.name= " + emp.getName());
 }
==========================================================
emp.dept= java开发部
emp.name= pt
3.9 通过静态工厂获取对象
<!--通过静态工厂来获取bean对象-->
    <!--
        1. class是静态工厂的路径
        2. factory-method 表示是指定静态工厂类的那个方法返回对象
        3. constructor-arg value="monster_01", value是指定要返回静态工厂的那个对象
    -->
<bean id="staticFactory_monster" class="com.zzti.spring.factory.MyStaticFactory" factory-method="getMonster">
    <!--constructor-arg标签提供key-->
    <constructor-arg value="monster_01"></constructor-arg>

</bean>
package com.zzti.spring.factory;

import com.zzti.spring.entity.Monster;

import java.util.HashMap;
import java.util.Map;

public class MyStaticFactory {
    private static Map<String, Monster> monsterMap;

    static {
        monsterMap = new HashMap<String,Monster>();
        monsterMap.put("monster_01",new Monster(100,"欧阳修","蛤蟆功"));
        monsterMap.put("monster_02",new Monster(200,"黄老邪","打狗棒"));
    }

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

}
====================================================================
@Test
public void getBeanByStaticFactory(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Monster my_monster = applicationContext.getBean("staticFactory_monster", Monster.class);
    System.out.println("my_monster= " + my_monster);
}
============================================================
my_monster= Monster{monsterId=200, monsterName='黄老邪', skill='打狗棒'}
3.10 通过实例工厂获取对象
<!--通过实例工厂来获取bean对象-->
    <bean id="instanceFactory" class="com.zzti.spring.factory.MyInstanceFactory">

    </bean>
    <!--
        1. factory-bean 指定使用那个实例工厂对象返回bean
        2. factory-method 指定使用实例工厂对象的那个方法返回bean
        3. constructor-arg value="monster_03" 指定获取到实例工厂中那个对象
    -->
    <bean id="my_monster" factory-bean="instanceFactory" factory-method="getMonster">
        <constructor-arg value="monster_03"></constructor-arg>
    </bean>
package com.zzti.spring.factory;

import com.zzti.spring.entity.Monster;

import java.util.HashMap;
import java.util.Map;

public class MyInstanceFactory {
	 private Map<String, Monster> monsterMap;
	
	 {
	     monsterMap = new HashMap<String,Monster>();
	     monsterMap.put("monster_03",new Monster(300,"欧阳修2","蛤蟆功"));
	     monsterMap.put("monster_04",new Monster(400,"黄老邪2","打狗棒"));
	 }
	
	 //提供一个方法,返回Monster对象
	 public  Monster getMonster(String key){
	     return monsterMap.get(key);
	 }
}
=======================================================
@Test
public void getBeanByInstanceFactory(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Monster my_monster = applicationContext.getBean("my_monster", Monster.class);
    System.out.println("my_monster= " + my_monster);
}
========================================================
my_monster= Monster{monsterId=300, monsterName='欧阳修2', skill='蛤蟆功'}

注意事项:

静态工厂时: 当创建两个对象(my_monster,my_monster02)时,在分别调用staticFactory_monster与staticFactory_monster02时,同时这两个bean中constructor-arg调用的是同一个对象,这两个是同一个对象。也就是my_monster == my_monster02
同理当实例工厂时,也是分别调用两个bean时,创建的两个对象是不一样的。
因为静态工厂创建的是静态类,静态类是不会改变的。
<bean id="staticFactory_monster" class="com.zzti.spring.factory.MyStaticFactory" factory-method="getMonster">
   <!--constructor-arg标签提供key-->
   <constructor-arg value="monster_02"></constructor-arg>
</bean>
<bean id="staticFactory_monster02" class="com.zzti.spring.factory.MyStaticFactory" factory-method="getMonster">
    <!--constructor-arg标签提供key-->
    <constructor-arg value="monster_02"></constructor-arg>
</bean>
Monster my_monster = applicationContext.getBean("staticFactory_monster", Monster.class);
Monster my_monster02 = applicationContext.getBean("staticFactory_monster02", Monster.class);
System.out.println(my_monster == my_monster02 );//true
3.11 通过FactoryBean获取对象(重点)
<!--通过FactoryBean来获取bean对象-->
    <!--
        1. class 表示 指定使用的是那个FactoryBean
        2. key 表示就是MyFactoryBean 属性key
        3. value 就是你要获取的对象对应的key
    -->
    <bean id="factoryBean" class="com.zzti.spring.factory.MyFactoryBean">
        <property name="key" value="monster_06"></property>
    </bean>
package com.zzti.spring.factory;

import com.zzti.spring.entity.Monster;
import org.springframework.beans.factory.FactoryBean;

import java.util.HashMap;
import java.util.Map;

public class MyFactoryBean implements FactoryBean<Monster> {

    private String key;
    private Map<String, Monster> monsterMap;

    {
        monsterMap = new HashMap<String,Monster>();
        monsterMap.put("monster_05",new Monster(500,"欧阳修3","蛤蟆功"));
        monsterMap.put("monster_06",new Monster(600,"黄老邪3","打狗棒"));

    }

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

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

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}
===========================================================
@Test
public void getBeanByFactoryBean(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Monster factoryBean = applicationContext.getBean("factoryBean", Monster.class);
    System.out.println("factoryBean= " + factoryBean);
}
3.11 通过继承配置bean
<!--通过继承配置bean-->
    <bean id="monster_bean"
          class="com.zzti.spring.entity.Monster"
          parent="monster02">

    </bean>
    
    <bean class="com.zzti.spring.entity.Monster" id="monster02">
        <property name="monsterId" value="200"></property>
        <property name="monsterName" value="孙悟空"></property>
        <property name="skill" value="金箍棒"></property>
    </bean>
     <!--
        若bean指定了abstract="true",表示该bean对象用于被继承,
        本身这个bean不能被获取(实例化)
    -->
    <bean id="monster_bean02" class="com.zzti.spring.entity.Monster" abstract="true">
        <constructor-arg value="2" index="0"></constructor-arg>
        <constructor-arg value="蜘蛛精~" index="1"></constructor-arg>
        <constructor-arg value="吐口水~" index="2"></constructor-arg>
    </bean>
    
    <bean id="monster_bean03" class="com.zzti.spring.entity.Monster" parent="monster_bean02">

@Test
public void getBeanByExtends(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster_bean = applicationContext.getBean("monster_bean", Monster.class);
    System.out.println("monster_bean= " + monster_bean);
    //Monster monster_bean02 = applicationContext.getBean("monster_bean02", Monster.class);
    //Error creating bean with name 'monster_bean02': Bean definition is abstract
    //System.out.println("monster_bean02= " + monster_bean02);

    Monster monster_bean03 = applicationContext.getBean("monster_bean03", Monster.class);
    System.out.println("monster_bean03= " + monster_bean03);

}
======================================================================
monster_bean= Monster{monsterId=200, monsterName='孙悟空', skill='金箍棒'}
monster_bean03继承monster_bean02时输出结果:
monster_bean= Monster{monsterId=200, monsterName='孙悟空', skill='金箍棒'}
monster_bean03= Monster{monsterId=2, monsterName='蜘蛛精~', skill='吐口水~'}

六. Bean创建顺序

<!--bean的黄建顺序-->
<bean id="student" class="com.zzti.spring.entity.Student" depends-on="department"></bean>
<bean id="department" class="com.zzti.spring.entity.Department"></bean>
默认情况下, bean的创建顺序是按照xml文件中配置的顺序创建,但是若加上depends-on属性,则就根据depends-on="xxx"先创建, 再创建其本身。
@Test
public void testBeanByCreate(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    System.out.println("ok~~~");
}

引出一个问题:

1. 如下的两个bean创建的顺序是什么?
<bean id="memberServiceImpl" class="com.zzti.spring.service.MemberServiceImpl">
    <property name="memberDao" ref="memberDaoImpl"></property>
</bean>
<bean id="memberDaoImpl" class="com.zzti.spring.dao.MemberDaoImpl"></bean>
执行流程:
2. 先创建id="memberServiceImpl"
3. 再创建id="memberDaoImpl"
4. 用memberServiceImpl,setMemeberDao(),完成引用
==========================================================
<bean id="memberDaoImpl" class="com.zzti.spring.dao.MemberDaoImpl"></bean>
<bean id="memberServiceImpl" class="com.zzti.spring.service.MemberServiceImpl">
    <property name="memberDao" ref="memberDaoImpl"></property>
</bean>
执行流程:
1. 先创建id="memberDaoImpl"
2. 再创建id="memberServiceImpl" 
3. 用memberServiceImpl,setMemeberDao(),完成引用

七. Bean对象的单例和多例

<!--bean的单例和多例-->
<!--
   1. 在默认情况下, scope属性是singleton
   2. 在applicationContext容器中只有这一个对象
   3. 当执行getBean()时, 返回的是同一个对象
   4. 如果我们希望每一次返回一个新的对象,则可以scope="prototype"
-->
<bean id="cat" class="com.zzti.spring.entity.Cat" scope="prototype" >
   <property name="id" value="100"></property>
   <property name="name" value="小花猫"></property>
</bean>
@Test
public void testBeanByScope(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Cat cat = applicationContext.getBean("cat", Cat.class);
    Cat cat1 = applicationContext.getBean("cat", Cat.class);
    Cat cat2 = applicationContext.getBean("cat", Cat.class);
    /**
     * 加上scope="prototype"
     * cat= com.zzti.spring.entity.Cat@5db6b9cd
     * cat1= com.zzti.spring.entity.Cat@210ab13f
     * cat2= com.zzti.spring.entity.Cat@20b2475a
     * =====================================================
     * 默认情况下是scope="singleton",可省略不写
     * cat= com.zzti.spring.entity.Cat@6f43c82
     * cat1= com.zzti.spring.entity.Cat@6f43c82
     * cat2= com.zzti.spring.entity.Cat@6f43c82
     */
    System.out.println("cat= " + cat);
    System.out.println("cat1= " + cat1);
    System.out.println("cat2= " + cat2);
    System.out.println("ok~~~");
}

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

八. Bean对象的生命周期

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

  1. 执行构造器
  2. 执行set相关的方法
  3. 调用bean的初始化的方法(需要配置)
  4. 使用bean
  5. 当容器关闭时,调用bean的销毁方法(需要配置)
 <!--演示bean的生命周期-->
    <bean id="house" class="com.zzti.spring.entity.House"
          init-method="init" destroy-method="destroy">
        <property name="name" value="汤臣一品"></property>
    </bean>
package com.zzti.spring.entity;

public class House {
private String name;

public House() {
    System.out.println("House 的无参构造函数执行~~");
}

public House(String name) {
    this.name = name;
}

public String getName() {
    return name;
}

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

public void init(){
    System.out.println("House init()执行~~");
}

public void destroy(){
    System.out.println("House destroy()执行~~");
}

@Override
public String toString() {
    return "House{" +
            "name='" + name + '\'' +
            '}';
}
}
@Test
public void testBeanByLife() {
   ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
   House house = applicationContext.getBean("house", House.class);
   System.out.println("house= " + house);
   applicationContext.close();
   /**
    * 注意细节:
    * 1) 初始化init() 和 destroy() 是需要程序员自己指定的
    * 2) destroy()就是当容器关闭时, 才会被调用
    * =========================================
    * House 的无参构造函数执行~~
    * House setName()=汤臣一品
    * House init()执行~~
    * house= House{name='汤臣一品'}
    * House destroy()执行~~
    */
}

九. 配置Bean的后置处理器(难点)

  1. 在spring的IOC容器, 可以配置bean的后置处理器
  2. 该处理器/对象会在 bean初始化方法调用前bean初始化方法调用后被调用
  3. 程序员可以在后置处理器中编写自己的代码
<!--演示配置bean的后置处理器-->
<bean id="house" class="com.zzti.spring.entity.House"
      init-method="init" destroy-method="destroy">
    <property name="name" value="大豪宅"></property>
</bean>

<bean id="house02" class="com.zzti.spring.entity.House"
      init-method="init" destroy-method="destroy">
    <property name="name" value="北京豪宅"></property>
</bean>

<!--配置bean的后置处理器-->
<bean id="myBeanPostProcessor" class="com.zzti.spring.entity.MyBeanPostProcessor">

</bean>
package com.zzti.spring.entity;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * 在bean初始化之前完成某些任务
     * @param bean 就是IOC容器返回的bean对象, 如果这里被替换会修改,则返回的bean对象也会被修改
     * @param beanName 就是IOC容器配置的bean的名称
     * @return 就是返回的bean对象
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization....bean= " + bean +"beanName= " + beanName);
        //如果bean是house类型,就把所有的bean改成 上海豪宅
        if (bean instanceof House) {
            ((House) bean).setName("上海豪宅");
        }
        return bean;
    }

    /**
     * 在bean初始化之后完成某些任务
     * @param bean 就是IOC容器返回的bean对象, 如果这里被替换会修改,则返回的bean对象也会被修改
     * @param beanName 就是IOC容器配置的bean的名称
     * @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 getBeanByPostProcess(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans02.xml");
    House house = ioc.getBean("house", House.class);
    System.out.println("house= " + house);
    /**
     * 注意这里的写法
     * ApplicationContext(是一个接口) 是编译类型; ClassPathXmlApplicationContext试运行类型
     * (ClassPathXmlApplicationContext)ioc ==> ApplicationContext
     */
    ((ClassPathXmlApplicationContext)ioc).close();
    /**
     * 执行结果如下:
     * House 的无参构造函数执行~~
         * House setName()=大豪宅
         * postProcessBeforeInitialization....bean= House{name='大豪宅'}beanName= house
         * House setName()=上海豪宅
         * House init()执行~~
         * postProcessAfterInitialization....bean= House{name='上海豪宅'}beanName= house
         * House 的无参构造函数执行~~
         * House setName()=北京豪宅
         * postProcessBeforeInitialization....bean= House{name='北京豪宅'}beanName= house02
         * House setName()=上海豪宅
         * House init()执行~~
         * postProcessAfterInitialization....bean= House{name='上海豪宅'}beanName= house02
         * house= House{name='上海豪宅'}
         * House destroy()执行~~
         * House destroy()执行~~
     */
}

其他说明:

  1. 怎么执行到这个方法(postProcessBeforeInitialization / postProcessAfterInitialization) ===> 使用AOP[反射+动态代理+IO+容器+注解]
  2. 有什么用?===> 可以对IOC容器中所有的对象进行统一处理,比如: 日志处理/权限的校验/安全的验证/事务管理
  3. 针对容器的所有的对象吗? ===> 是的,切面编程

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

my.properties

monsterId=300
#中文转Unicode编码
monsterName=\u4e4c\u9f9f\u7cbe
skill=\u7f29\u8116\u5b50
<!--通过属性文件给bean注入值-->
<context:property-placeholder location="classpath:my.properties" />

<bean id="monsterByFile" class="com.zzti.spring.entity.Monster">
    <property name="monsterId" value="${monsterId}"></property>
    <property name="monsterName" value="${monsterName}"></property>
    <property name="skill" value="${skill}"></property>

</bean>
@Test
public void setBeanByFile(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster monsterByFile = ioc.getBean("monsterByFile", Monster.class);
    System.out.println("monsterByFile= " + monsterByFile);
}
=========================================
monsterByFile= Monster{monsterId=300, monsterName='乌龟精', skill='缩脖子'}

十一. 基于XML文件的bean自动装配

<?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="orderDao" class="com.zzti.spring.dao.OrderDao"></bean>

    <!--基于xml的bean的自动转配
        1. autowire="byType"方式. 表示根据类型进行自动转配,这里就是在创建orderService时,
        通过类型的方式 给对象属性 自动完成赋值 / 引用
        比如OrderService 对象有 private OrderDao orderDao,就会在容器中去找有没有OrderDao类型对象
        如果有, 就会自动装配. 但是在这里容器中,不能有两个orderDao对象, 否则就会找不到
        若class类对象中没有属性, autowire就没有必要写


        2. autowire="byName"方式. 会自动去找id为setXxxx,后面的Xxxx的bean自动组装,
        如果找到就装配,如果找不到就会报错
        比如这里的: <bean id="orderController" autowire="byType" class="com.zzti.spring.controller.OrderController"/>
        就会找OrderController中定义的setOrderService的id="orderService"的OrderService
        bean找到就组装,找不到就组装失败


    -->
    <bean id="orderService" autowire="byType" class="com.zzti.spring.service.OrderService"></bean>

    <bean id="orderController" autowire="byType" class="com.zzti.spring.controller.OrderController"></bean>

</beans>
public class OrderDao {
    public void saveOrder(){
        System.out.println("保存一个订单~~");
    }
}
public class OrderService {

    private OrderDao orderDao;

    public OrderDao getOrderDao() {
        return orderDao;
    }

    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }
}
public class OrderController {
    private OrderService orderService;

    public OrderService getOrderService() {
        return orderService;
    }

    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }
}
@Test
public void setBeanByAutowire(){
     ApplicationContext ioc = new ClassPathXmlApplicationContext("beans03.xml");
     OrderController orderController = ioc.getBean("orderController", OrderController.class);
     orderController.getOrderService().getOrderDao().saveOrder();
 }
===============================================================
保存一个订单~~

十二. Spring的EL表达式(知道即可)

  1. SpEL和EL表达式一样, SpEL根据Javabean风格的getXxx()、setXxx()方法定义的属性访问对象。
  2. SpEL使用#{…}作为定界符,所有在大括号中的字符都将被认为是SpEL表达式。
<?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 class="com.zzti.spring.entity.Monster" id="monster01">
        <property name="monsterId" value="100"></property>
        <property name="monsterName" value="牛魔王"></property>
        <property name="skill" value="芭蕉扇"></property>
    </bean>

    <bean class="com.zzti.spring.entity.Monster" id="monster02">
        <property name="monsterId" value="200"></property>
        <property name="monsterName" value="孙悟空"></property>
        <property name="skill" value="金箍棒"></property>
    </bean>

    <bean id="spELBean" class="com.zzti.spring.entity.SpELBean">
        <!--sp el给自变量-->
        <property name="name" value="#{'韩顺平教育'}"></property>
        <!--引用其他bean-->
        <property name="monster" value="#{monster01}"></property>
        <!--引用其他bean的属性值-->
        <property name="monsterName" value="#{monster02.monsterName}"></property>
        <!--调用普通方法-->
        <property name="crySound" value="#{spELBean.cry('喵喵的...')}"></property>
        <!--调用静态方法-->
        <property name="bookName" value="#{T(com.zzti.spring.entity.SpELBean).read('天龙八部')}"></property>
        <!--通过运算赋值-->
        <property name="result" value="#{5*8}"></property>
    </bean>

</beans>
package com.zzti.spring.entity;

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;
    }

    public String cry(String sound){
        return "发出" + sound + "叫声...";
    }

    public static String read(String bookName){
        return "正在看 " + bookName;
    }
}
@Test
public void setBeanBySpEL(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans04.xml");
    SpELBean spELBean = ioc.getBean("spELBean", SpELBean.class);
    System.out.println(spELBean.getName());
    System.out.println(spELBean.getMonster());
    System.out.println(spELBean.getMonsterName());
    System.out.println(spELBean.getCrySound());
    System.out.println(spELBean.getBookName());
    System.out.println(spELBean.getResult());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值