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容器结构/机制
- 在beanFactory下面有beanDefintionMap, beanDefintionMap类型是ConcurrentHashMap集合,其存放beans.xml中bean节点配置的bean对象的信息
- 在beanDefintionMap中有属性table,table是数组,类型是ConcurrentHashMap$Node。因为是数组,所以可以存放很多的bean对象信息,就是beans.xml配置。初始化是512,当超过时,会自动扩容。
- 通过hash算法,我们的Monster01对象的信息就保存在index=217的位置,保存是以ConcurrentHashMap$Node类型保存;key就是beans.xml中配置的id(monster01),value是monster01对象的信息[比如: 属性/属性值/类信息/是不是懒加载]
- propertyValues中记录的就是beans.xml中配置的monster01对象的属性名/属性值
- 在beanFactory中,有属性singletonObjects, 类型是ConcurrentHashMap;singletonObjects下面还有一个属性table,类型是ConcurrentHashMap$Node。如果你在beans.xml文件中配置的对象是 单例的 就会初始化在table中。
- 在beanFactory中,还有一个属性beanDefinitionNames。记录了我们在beans.xml中配置的bean名称,方便查找。
四. 手动开发简单的Spring基于XML配置的程序
- 需求说明
自己写一个简单的Spring容器,通过读取beans.xml,获取第一个Javabean: Monster01对象,并给该对象属性赋值,放入到容器中,输出该对象信息。 - 思路分析
- 完成步骤
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完成的,然后执行方法如下:
- 执行构造器
- 执行set相关的方法
- 调用bean的初始化的方法(需要配置)
- 使用bean
- 当容器关闭时,调用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的后置处理器(难点)
- 在spring的IOC容器, 可以配置bean的后置处理器
- 该处理器/对象会在 bean初始化方法调用前和bean初始化方法调用后被调用
- 程序员可以在后置处理器中编写自己的代码
<!--演示配置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()执行~~
*/
}
其他说明:
- 怎么执行到这个方法(postProcessBeforeInitialization / postProcessAfterInitialization) ===> 使用AOP[反射+动态代理+IO+容器+注解]
- 有什么用?===> 可以对IOC容器中所有的对象进行统一处理,比如: 日志处理/权限的校验/安全的验证/事务管理
- 针对容器的所有的对象吗? ===> 是的,切面编程
十. 通过属性文件给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表达式(知道即可)
- SpEL和EL表达式一样, SpEL根据Javabean风格的getXxx()、setXxx()方法定义的属性访问对象。
- 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());
}