1. 基于 XML 配置 Bean
在上一篇里已经模拟了通过 id 来配置获取 bean
依然是使用 Monster 类
1.1 通过类型获取
1)xml 配置
XML
<bean id="monster01" class="com.hspedu.spring.beans.Monster">
<property name="monsterId" value="1"/>
<property name="name" value="牛魔王"/>
<property name="skill" value="牛魔王拳"/>
</bean>
2)测试
java
@Test
public void getMonsterByType() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster = ioc.getBean(Monster.class);
System.out.println("monster=" + monster);
Monster monster2 = ioc.getBean(Monster.class);
System.out.println("monster == monster2 的值= " + (monster == monster2));
//是同一个对象
}
3)细节说明
- 按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个
- 这种方式的应用场景:比如 XxxAction/Servlet/Controller, 或 XxxService在一个线程
中只需要一个对象实例(单例)的情况 - 在容器配置文件(比如 beans.xml)中给属性赋值, 底层是通过setter 方法完成的, 这也是为什么我们需要提供 setter 方法的原因
1.2 通过构造器配置
1)xml 配置
index 表示构造器的第几个参数,从 0 开始计数
XML
<bean id="monster02" class="com.hspedu.spring.beans.Monster">
<constructor-arg value="2" index="0"/>
<constructor-arg value="蜘蛛精" index="1"/>
<constructor-arg value="吐口水" index="2"/>
</bean>
还可以通过 name/type 来指定参数方式
XML
<bean id="monster03" class="com.hspedu.spring.beans.Monster">
<constructor-arg value="3" type="java.lang.Integer"/>
<constructor-arg value="白骨精" type="java.lang.String"/>
<constructor-arg value="白骨鞭" type="java.lang.String"/>
</bean>
<bean id="monster04" class="com.hspedu.spring.bean.Monster">
<constructor-arg value="200" name="monsterId"/>
<constructor-arg value="白骨精" name="name"/>
<constructor-arg value="吸人血" name="skill"/>
</bean>
2)测试
java
@Test
public void getMonsterByConstructor() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Object monster02 = ioc.getBean("monster02");
Object monster03 = ioc.getBean("monster03");
Object monster04 = ioc.getBean("monster03");
System.out.println("monster02= " + monster02);
System.out.println("monster03= " + monster03);
System.out.println("monster04= " + monster04);
}
3)细节说明
- 通过 index 属性来区分是第几个参数
- 通过 type 属性来区分是什么类型(按照顺序)
1.3 通过 p 名称空间配置
1)xml 配置
增加命名空间配置
java
xmlns:p="http://www.springframework.org/schema/p"
通过 p 名称空间来配置 bean 对象,其实将光标放在 p 的地方,alt+enter 就可以添加 xmlns
java
<bean id="monster06" class="com.hspedu.spring.bean.Monster"
p:monsterId="500"
p:name="红孩儿"
p:skill="吐火"
/>
2)测试
java
@Test
public void getMonsterByP() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster04 = ioc.getBean("monster06", Monster.class);
System.out.println("monster06=" + monster06);
}
1.4 引用/注入其它 bean 对象
在 spring 的 ioc 容器, 可以通过 ref 来实现 bean 对象的相互引用
1)创建对象
...\spring\dao\MemberDAOImpl.java
java
public class MemberDAOImpl {
public MemberDAOImpl() {
System.out.println("MemberDAOImpl 构造器...");
}
public void add() {
System.out.println("MemberDAOImpl add()方法");
}
}
...\spring\service\MemberServiceImpl.java
java
public class MemberServiceImpl {
private MemberDAOImpl memberDAO;
public MemberServiceImpl() {
System.out.println("MemberServiceImpl 构造器~");
}
public void add() {
System.out.println("MemberServiceImpl add()...");
memberDAO.add();
}
public void setMemberDAO(MemberDAOImpl memberDAO) {
this.memberDAO = memberDAO;
}
public MemberDAOImpl getMemberDAO() {
return memberDAO;
}
}
2)xml 配置
ref 表示 memberDAO 这个属性将引用/指向 id = memberDAOImpl 对象
就体现了 Spring 容器的依赖注入
注:在Spring容器中是作为一个整体运行的,创建引用是不分前后顺序
XML
<bean id="memberServiceImpl" class="com.hspedu.spring.service.MemberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<bean id="memberDAOImpl" class="com.hspedu.spring.dao.MemberDAOImpl"/>
3)测试
java
@Test
public void setBeanByRef() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
MemberServiceImpl memberServiceImpl = ioc.getBean("memberServiceImpl",
MemberServiceImpl.class);
memberServiceImpl.add();
}
4)结构
说明确实是引用关系
1.5 引用/注入内部 bean 对象
在 spring 的 ioc 容器, 可以直接配置内部 bean 对象
1)修改 xml 配置
自己配置一个内部 bean
java
<bean id="memberServiceImpl02" class="com.hspedu.spring.service.MemberServiceImpl">
<property name="memberDAO">
<bean class="com.hspedu.spring.dao.MemberDAOImpl"/>
</property>
</bean>
2)测试
java
@Test
public void setBeanByPro() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
MemberServiceImpl memberServiceImpl02 = ioc.getBean("memberServiceImpl02",
MemberServiceImpl.class);
memberServiceImpl02.add();
}
1.6 引用/注入集合/数组类型
如何给 bean 对象的集合/数组类型属性赋值
1)创建对象
...\spring\beans\Master.java
java
public class Master {
private String name;
private List<Monster> monsterList;
private Map<String, Monster> monsterMap;
private Set<Monster> monsterSet;
private String[] monsterName;
//这个 Properties 是 Hashtable 的子类 , 是 key-value 的形式
//这里 Properties key 和 value 都是 String
private Properties pros;
public Master() {}
public Master(String name) {this.name = name;}
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 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 Properties getPros() {return pros;}
public void setPros(Properties pros) {
this.pros = pros;
}
}
2)xml 配置
给集合属性注入值
XML
<bean id="master01" class="com.hspedu.spring.beans.Master">
<property name="name" value="太上老君"/>
<!-- 给 bean 对象的 list 集合赋值 -->
<property name="monsterList">
<list>
<ref bean="monster03"/>
<ref bean="monster02"/>
</list>
</property>
<!-- 给 bean 对象的 map 集合赋值 -->
<property name="monsterMap">
<map>
<entry>
<key>
<value>monsterKey01</value>
</key>
<ref bean="monster01"/>
</entry>
<entry>
<key>
<value>monsterKey02</value>
</key>
<ref bean="monster02"/>
</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"/>
<bean class="com.hspedu.spring.beans.Monster">
<property name="monsterId" value="10"/>
<property name="name" value="玉兔精"/>
<property name="skill" value="钻地洞"/>
</bean>
</set>
</property>
</bean>
3)测试
java
@Test
public void setCollectionByPro() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Master master01 = ioc.getBean("master01", Master.class);
//获取 list 集合
System.out.println("======list=======");
List<Monster> monster_list = master01.getMonsterList();
for (Monster monster : monster_list) {
System.out.println(monster);
}
//获取 map 集合
System.out.println("======map=======");
Map<String, Monster> monster_map = master01.getMonsterMap();
Set<Map.Entry<String, Monster>> entrySet = monster_map.entrySet();
for (Map.Entry<String, Monster> entry : entrySet) {
System.out.println(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(monster);
}
}
4)细节
Properties 集合的特点:
- Properties 是 Hashtable 的子类 , 是 key-value 的形式
- key 是 string 而 value 也是 string
1.7 通过 util 名称空间创建 list
spring 的 ioc 容器, 可以通过 util 名称空间创建 list 集合
1)创建对象
java
public class BookStore {//书店
private List<String> bookList;
public BookStore() {}
public List<String> getBookList() {
return bookList;
}
public void setBookList(List<String> bookList) {
this.bookList = bookList;
}
}
2)xml 配置
增加配置
XML
xmlns:util="http://www.springframework.org/schema/util"
对象配置:
XML
<util:list id="myListBook">
<value>三国演义</value>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
</util:list>
<bean id="bookStore" class="com.hspedu.spring.beans.BookStore">
<property name="bookList" ref="myListBook"/>
</bean>
3)测试
java
@Test
public void getListByUtil() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
BookStore bookStore = ioc.getBean("bookStore", BookStore.class);
List<String> bookList = bookStore.getBookList();
for (String s : bookList) {
System.out.println(s);
}
}
1.8 级联属性赋值
可以直接给对象属性的属性赋值, 即级联属性赋值
1)创建对象
...\spring\beans\Dept.java
java
public class Dept {
private String name;
public Dept() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
...\spring\beans\Emp.java
java
public class Emp {
private String name;
private Dept dept;
public Emp() {}
public String getName() {return name;}
public void setName(String name) {
this.name = name;
}
public Dept getDept() {return dept;}
public void setDept(Dept dept) {
this.dept = dept;
}
}
2)xml 配置
XML
<bean id="emp" class="com.hspedu.spring.beans.Emp">
<property name="name" value="jack"/>
<property name="dept" ref="dept"/>
<property name="dept.name" value="Java 开发部"/>
</bean>
<bean id="dept" class="com.hspedu.spring.beans.Dept"/>
3)测试
java
@Test
public void setProByRelation() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Emp emp = ioc.getBean("emp", Emp.class);
System.out.println(emp.getDept().getName());
}
1.9 通过静态工厂获取对象
1)创建对象
...\spring\factory\MyStaticFactory.java
java
public class MyStaticFactory {
private static Map<String, Monster> monsterMap;
// 使用 static 代码块进行初始化
static {
monsterMap = new HashMap<String, Monster>();
monsterMap.put("monster01", new Monster(100, "黄袍怪", "一阳指"));
monsterMap.put("monster02", new Monster(200, "九头金雕", "如来神掌"));
}
public static Monster getMonster(String key) {
return monsterMap.get(key);
}
}
2)xml 配置
-
通过静态工厂获取/配置bean
-
class 是静态工厂类的全路径
-
factory-method 表示是指定静态工厂类的哪个方法返回对象
-
constructor-arg value="monster02" value是指定要返回静态工厂的哪个对象
java
<bean id="my_monster01"
class="com.hspedu.spring.factory.MyStaticFactory"
factory-method="getMonster">
<constructor-arg value="monster02"/>
</bean>
3)测试
java
@Test
public void getBeanByStaticFactory() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster my_monster = ioc.getBean("my_monster01", Monster.class);
System.out.println(my_monster);
}
1.10 通过实例工厂获取对象
与静态工厂不同的地方在于,需要先有一个实例工厂才能获得对象
1)创建对象
...\spring\factory\MyInstanceFactory.java
java
public class MyInstanceFactory {
private Map<String, Monster> monster_map;
//非静态代码块
{
monster_map = new HashMap<String, Monster>();
monster_map.put("monster_01", new Monster(100, "猴子精", "吃人"));
monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
}
public Monster getMonster(String key) {
return monster_map.get(key);
}
}
2)xml 配置
java
<bean id="myInstanceFactory" class="com.hspedu.spring.factory.MyInstanceFactory"/>
<bean id="my_monster2" factory-bean="myInstanceFactory"
factory-method="getMonster">
<constructor-arg value="monster_02"/>
</bean>
3)测试
java
@Test
public void getBeanByInstanceFactory() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster my_monster = ioc.getBean("my_monster2", Monster.class);
System.out.println(my_monster);
}
1.11 通过 FactoryBean 获取对象(重点)
1)创建对象
...\spring\factory\MyFactoryBean.java
java
public class MyFactoryBean implements FactoryBean<Monster> {
private String keyVal;
private Map<String, Monster> monster_map;
{ // 代码块完成初始化任务
monster_map = new HashMap<String, Monster>();
monster_map.put("monster_01", new Monster(100, "黄袍怪", "一阳指"));
monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
}
public void setKeyVal(String keyVal) {
this.keyVal = keyVal;
}
@Override
public Monster getObject() throws Exception {
// TODO Auto-generated method stub
return this.monster_map.get(keyVal);
}
@Override
public Class getObjectType() {
// TODO Auto-generated method stub
return Monster.class;
}
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
// 指定是否是返回单例
return true;
}
}
2)xml 配置
java
<bean id="myFactoryBean" class="com.hspedu.spring.factory.MyFactoryBean">
<property name="keyVal" value="monster_01"/>
</bean>
3)测试
java
@Test
public void getBeanByFactoryBean() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster = ioc.getBean("myFactoryBean", Monster.class);
System.out.println(monster);
}
1.12 bean 配置信息重用(继承)
在 spring 的 ioc 容器, 提供了一种继承的方式来实现 bean 配置信息的重用
1)xml 配置
如果要创建一个 配置信息是一样的对象的话,可以进行继承
java
<bean id="monster10" class="com.hspedu.spring.beans.Monster">
<property name="monsterId" value="10"/>
<property name="name" value="蜈蚣精"/>
<property name="skill" value="蜇人"/>
</bean>
<!-- parent="monster10" 就是继承使用了 monster10 的配置信息 -->
<bean id="monster11" class="com.hspedu.spring.beans.Monster" parent="monster10"/>
当我们把某个 bean 设置为 abstract="true" 这个 bean只能被继承,而不能实例化了,类似与java 的抽象类
java
<bean id="monster12" class="com.hspedu.spring.beans.Monster" abstract="true">
<property name="monsterId" value="12"/>
<property name="name" value="美女蛇"/>
<property name="skill" value="吃人"/>
</bean>
<!-- parent="monster12" 就是继承使用了 monster12 的配置信息 -->
<bean id="monster13" class="com.hspedu.spring.beans.Monster" parent="monster12"/>
2)测试
java
@Test
public void getBeanByExtends() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster1 = ioc.getBean("monster11", Monster.class);
System.out.println(monster1);
Monster monster2 = (Monster) ioc.getBean("monster13", Monster.class);
System.out.println(monster2);
}
1.13 bean 创建顺序
在 spring 的 ioc 容器, 默认是按照配置的顺序创建 bean 对象
java
<bean id="student01" class="com.hspedu.bean.Student" />
<bean id="department01" class="com.hspedu.bean.Department" />
会先创建 student01 这个 bean 对象,然后创建 department01 这个 bean 对象
如果这样配置,增加了 depends-on="xxx"
java
<bean id="student01" class="com.hspedu.bean.Student"
depends-on="department01"/>
<bean id="department01" class="com.hspedu.bean.Department" />
会先创建 department01 对象,再创建 student01 对象
1.14 bean 对象的单例和多例
在默认是按照单例创建的,即配置一个 bean 对象后,ioc 容器只会创建一个 bean 实例
想要以多个实例形式创建某个 bean 对象的则可以通过配置scope="prototype" 来指定
1)创建对象
...\spring\beans\Car.java
java
public class Car {
public Car() {
System.out.println("car 构造器");
}
}
2)xml 配置
java
<bean name="car" scope="prototype" class="com.hspedu.spring.beans.Car"/>
3)测试
java
@Test
public void getBeanByPrototype() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
for (int i = 0; i < 3; i++) {
Car car = ioc.getBean("car", Car.class);
System.out.println(car);
}
}
4)细节
- 默认是单例 singleton, 在启动容器时, 默认就会创建 , 并放入到 singletonObjects 集合
- <bean scope="prototype" > 设置为多实例机制后,该 bean 是在 getBean()时才创建
- 如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才创建 , 可指定懒加载
lazy-init="true" (注意默认是 false) - lazy-init 就使用默认值 false , 在开发看来, 用空间换时间是值得的, 除非有特殊的要求
- scope="prototype" 这时你的 lazy-init 属性的值不管是 ture, 还是 false 都是在 getBean 时,才创建对象
1.15 bean 的生命周期
bean 对象创建是由 JVM 完成的,然后执行如下方法
- 执行构造器
- 执行 set 相关方法
- 调用 bean 的初始化的方法(需要配置)
- 使用 bean
- 当容器关闭时候,调用 bean 的销毁方法(需要配置)
1)创建对象
...\spring\beans\House.java
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()...");
this.name = name;
}
public void init() {
System.out.println("House init()..");
}
public void destory() {
System.out.println("House destory()..");
}
}
2)xml 配置
配置 bean 的初始化方法和销毁方法
java
<bean id="house" class="com.hspedu.spring.beans.House"
init-method="init"
destroy-method="destory">
<property name="name" value="北京豪宅"/>
</bean>
3)测试
java
@Test
public void beanLife() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
House house = ioc.getBean("house", House.class);
System.out.println(house);
//关闭容器 ConfigurableApplicationContext 是有 close 方法的
((ConfigurableApplicationContext) ioc).close();
}
4)使用细节
- 初始化 init 方法和 destory 方法, 是程序员来指定,名字也不一定是叫这个名字,在配置文件里进行指定是调用那个方法作为初始化/销毁方法
- 销毁方法就是当关闭容器时,才会被调用
1.16 配置 bean 的后置处理器(重点)
- 在 spring 的 ioc 容器,可以配置 bean 的后置处理器
- 该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用
- 在后置处理器中编写自己的代码
1)创建后置处理器
...\spring\beans\MyBeanPostProcessor.java
要实现 BeanPostProcessor 接口的两个方法作为一个前一个后
java
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 在 bean 初始化之前完成某些任务
* @param bean: 就是 ioc 容器返回的 bean 对象,
* 如果被替换会修改,返回的 bean 对象也会被修改
* @param beanName: 就是 ioc 容器配置的 bean 的名称
* @return Object: 就是返回的 bean 对象
*/
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization被 调 用"
+ beanName + "bean= " + bean.getClass());
return bean;
}
/**
* 在 bean 初始化之后完成某些任务
* @param bean: 就是 ioc 容器返回的 bean 对象, 如果被替换会修改,
* 返回的 bean 对象也会被修改
* @param beanName: 就是 ioc 容器配置的 bean 的名称
* @return Object: 就是返回的 bean 对象
*/
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("postProcessAfterInitialization 被调用 "
+ beanName + " bean= "+ bean.getClass());
return bean;
}
}
2)xml 配置
java
<bean id="house" class="com.hspedu.spring.beans.House"
init-method="init"
destroy-method="destory">
<property name="name" value="北京豪宅"/>
</bean>
bean 后置处理器的配置,配置后这个后置处理器作用在该容器创建的 bean 对象中
java
<bean id="myBeanPostProcessor"
class="com.hspedu.spring.beans.MyBeanPostProcessor" />
</beans>
3)测试
java
@Test
public void testBeanPostProcessor() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans02.xml");
House house = ioc.getBean("house", House.class);
System.out.println(house);
//关闭容器
((ConfigurableApplicationContext) ioc).close();
}
就已经可以触发后置处理器了!!!
4)说明
1. 怎么执行到这个方法?
使用 AOP(反射+动态代理+IO+容器+注解)
2、有什么用?
可以对 IOC 容器中所有的对象进行统一处理 ,比如 日志处理/权限的校验/安全的验证/事务管理
3、针对容器的所有对象吗?
是的 => 这也就是AOP切面编程特点
1.17 通过属性文件给 bean 注入值
1)创建 my.properties
在 src 目录下创建 注以是 Unicode 编码
java
name=\u9EC4\u888D\u602A
id=10
skill=\u72EE\u5B50\u543C
2)xml 配置
-
通过属性文件给 bean 注入值,
-
需要导入: xmlns:context 名字空间,并指定属性文件路径 location="xxxx"
XML
<context:property-placeholder location="classpath:my.properties"/>
<bean id="monster100" class="com.hspedu.spring.beans.Monster">
<property name="monsterId" value="${id}"/>
<property name="name" value="${name}"/>
<property name="skill" value="${skill}"/>
</bean>
3)测试
java
@Test
public void setProByProFile() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster100 = ioc.getBean("monster100", Monster.class);
System.out.println(monster100);
}
1.18 基于 XML 的 bean 的自动装配
1)创建对象
...\spring\dao\OrderDao.java
java
public class OrderDao {
public void saveOrder() {
System.out.println("保存 一个订单...");
}
}
...\spring\service\OrderService.java
java
public class OrderService {
private OrderDao orderDao;
public OrderDao getOrderDao() {
return orderDao;
}
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
}
...\spring\action\OrderAction.java
java
public class OrderAction {
private OrderService orderService;
public OrderService getOrderService() {
return orderService;
}
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
2)xml 配置
autowire="byType" 表示根据类型进行自动组装
比如OrderService 对象有 private OrderDao orderDao,就会在容器中去找有没有 OrderDao类型对象;如果有,就会自动的装配 --> 这个容器中,不能有两个的OrderDao类型对象
XML
<bean id="orderAction" autowire="byType"
class="com.hspedu.spring.action.OrderAction"/>
<bean id="orderService" autowire="byType"
class="com.hspedu.spring.service.OrderService"/>
<bean id="orderDao" class="com.hspedu.spring.dao.OrderDao"/>
autowire = "byName" 表示通过名字完成自动装配
OrderService 属性 private OrderDao orderDao,根据这个属性的setXxx()方法的 xxx 来找对象id,public void setOrderDao() 就会找id=orderDao对象来进行自动装配,如果没有就装配失败
XML
<bean autowire="byName" class="com.hspedu.spring.service.OrderService"
id="orderService"/>
<bean autowire="byName" class="com.hspedu.spring.web.OrderAction"
id="orderAction"/>
<bean id="orderDao" class="com.hspedu.spring.dao.OrderDao"/>
3)测试
java
@Test
public void setProByAutowire() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
OrderAction orderAction = ioc.getBean("orderAction", OrderAction.class);
orderAction.getOrderService().getOrderDao().saveOrder();
}
4)说明
这个知识点作为了解即可,后面主要还是使用基于注解的方式
1.19 spring el 表达式
支持运行时查询并可以操作对象
1)创建对象
...\spring\beans\SpELBean.java
java
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;}
}
2)xml 配置
XML
<bean id="spELBean" class="com.hspedu.spring.beans.SpELBean">
<!-- sp el 给字面量 -->
<property name="name" value="#{'hsp教育'}"/>
<!-- sp el 引用其它 bean -->
<property name="monster" value="#{monster01}"/>
<!-- sp el 引用其它 bean 的属性值 -->
<property name="monsterName" value="#{monster02.name}"/>
<!-- sp el 调用普通方法赋值 -->
<property name="crySound" value="#{spELBean.cry('喵喵的..')}"/>
<!-- sp el 调用静态方法 赋值 -->
<property name="bookName" value="#{T(com.hspedu.spring.beans.SpELBean).
read('天龙八部')}"/>
<!-- sp el 通过运算赋值 -->
<property name="result" value="#{89*1.2}"/>
</bean>
3)测试
java
@Test
public void setProBySpel() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.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());
}
4)说明
不是重点,如果看到有人这样使用,能看懂即可
2. 基于注解配置 bean
基于注解的方式配置 bean, 主要是项目开发中的组件,比如 Controller、Service、和 Dao
组件注解的形式有:
- @Component 表示当前注解标识的是一个组件
- @Controller 表示当前注解标识的是一个控制器,通常用于 Servlet
- @Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于 Service 类
- @Repository 表示当前注解标识的是一个持久化层的类,通常用于 Dao 类
2.1 快速入门
1)引入包 spring-aop-5.3.8.jar
2)创建对象
java
@Repository
public class UserDao {
}
@Service
public class UserService {
}
@Controller
public class UserAction {
}
@Component
public class MyComponent {
}
3)xml 配置
配置自动扫描的包,注意需要加入 context 名称空间
- component-scan 要对指定包下的类进行扫描, 并创建对象到容器
- base-package 指定要扫描的包
- 当spring容器创建/初始化时,就会扫描com.hspedu.spring.component包下的所有的 有注解 @Controller / @Service / @Respository / @Component类 将其实例化,生成对象,放入到ioc容器
- resource-pattern="User*.class" 表示只扫描com.hspedu.spring.component 和它的子包下的User打头的类(使用较少,其实只要不加注解就能解决)
XML
<context:component-scan base-package="com.hspedu.spring.component" />
4)测试
java
@Test
public void getBeanByAnnotation() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
UserAction userAction = ioc.getBean(UserAction.class);
System.out.println(userAction);
UserDao userDao = ioc.getBean(UserDao.class);
System.out.println(userDao);
MyComponent myComponent = ioc.getBean(MyComponent.class);
System.out.println(myComponent);
UserService userService = ioc.getBean(UserService.class);
System.out.println(userService);
}
调试查看 ioc 容器
5)注意
Spring 的 IOC 容器不能检测一个使用了@Controller 注解的类到底是不是一个真正的控制器
注解的名称是用于程序员自己识别当前标识的是什么组件。
其它的@Service @Repository 也是一样的道理 (也就是说 spring 的 IOC 容器只要检查到注解就会生成对象,但是这个注解的含义 spring 不会识别,注解是给程序员编程方便看的)
6)排除某种类型下的某种类型的注解
XML
<context:component-scan base-package="com.hspedu.spring.component">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
</context>
- <context:exclude-filter> 放在<context:component-scan>内,表示扫描过滤掉当前包的某些类
- type="annotation" 按照注解类型进行过滤
- expression :就是注解的全类名,这里就是 @Service 注解的全类名
- 上面表示过滤掉 com.hspedu.spring.component 包下,加入了@Service 注解的类
7)指定自动扫描哪些注解类
目的:只扫描 Service Controller
XML
<context:component-scan base-package="com.hspedu.spring.component"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
8)注解类名
默认情况:标记注解后,类名首字母小写作为 id 的值
也可以使用注解的 value 属性指定 id 值,并且 value 可以省略
java
@Controller(value="userAction01")
@Controller("userAction01")
9)拓展
链接:彻底弄懂@Controller 、@Service、@Component
2.2 自动装配
基于注解配置 bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource
1) @AutoWired 的规则说明
- 在 IOC 容器中查找待装配的组件的类型,如果有唯一的 bean 匹配,则使用该 bean 装
配 - 如待装配的类型对应的 bean 在 IOC 容器中有多个,则使用待装配的属性的属性名作
为 id 值再进行查找, 找到就装配,找不到就抛异常
2)@Resource 的规则说明
- @Resource 有两个属性是比较重要的,分是 name 和 type, Spring 将@Resource 注解的
name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型.所以如果使用 name 属
性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略 - 如果@Resource 没有指定 name 和 type ,则先使用byName注入策略, 如果匹配不上,
再使用 byType 策略, 如果都不成功,就会报错
3)使用推荐
不管是@Autowired 还是 @Resource 都保证属性名是规范的写法就可以注入
但是更推荐使用@Resource
4)注意说明
java
@Autowired
@Qualifier(value = "userService02")
//指定 id=userService02 的 UserService 对象进行组装
private UserService userService;
指定 id 进行组装, 这时,是装配的 id=userService02 , 需要两个注解都需要写上
2.3 泛型依赖注入
为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的注入机制
在继承关系复杂情况下,泛型依赖注入就会有很大的优越性
1)创建对象
java
public class Book {}
======================
public class Phone {}
======================
public abstract class BaseDao<T> {
public abstract void save();
}
======================
@Repository
public class BookDao extends BaseDao<Book> {
@Override
public void save() {
System.out.println("BookDao 的 save()");
}
}
======================
@Repository
public class PhoneDao extends BaseDao<Phone> {
@Override
public void save() {
System.out.println("PhoneDao 的 save()");
}
}
======================
public class BaseService<T> {
@Autowired
private BaseDao<T> baseDao;
public void save() {
baseDao.save();
}
}
======================
@Service
public class BookService extends BaseService<Book> {}
======================
@Service
public class PhoneService extends BaseService<Phone> {}
2)xml 配置
XML
<context:component-scan base-package="com.hspedu.spring.depinjection"/>
3)测试
XML
@Test
public void setProByDepinjectionAutowired() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
BookService bookService = ioc.getBean(BookService.class);
bookService.save();
PhoneService phoneService = ioc.getBean(PhoneService.class);
phoneService.save();
}
debug,可以看到 baseDao 的 BookDao 已经注入到 bookService 中