1 Spring 配置/管理 bean 介绍
Bean 管理包括两方面 :创建 bean 对象;给 bean 注入属性
Bean 配置方式:基于 xml 文件配置方式;基于注解方式
2 基于 XML 配置 bean
2.1 通过类型来获取 bean
方法:给getBean传入一个Class对象
示例:
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster = ioc.getBean(Monster.class);
说明:
2.2 通过构造器配置 bean
(1)constructor-arg标签可以指定使用构造器的参数
(2)index表示构造器的第几个参数 从0开始计算的
(3)除了可以通过index 还可以通过 name / type 来指定参数方式,name表示参数的名字,type表示参数的类型
(4)类的构造器,不能有完全相同类型和顺序的构造器,所以可以通过type来指定
<bean id="monster03" class="com.spring.bean.Monster"><constructor-arg value="200" index="0"/><constructor-arg value="白骨精" index="1"/><constructor-arg value="吸人血" index="2"/>
</bean><bean id="monster04" class="com.spring.bean.Monster"><constructor-arg value="200" name="monsterId"/><constructor-arg value="白骨精" name="name"/><constructor-arg value="吸人血" name="skill"/>
</bean><bean id="monster05" class="com.spring.bean.Monster"><constructor-arg value="300" type="java.lang.Integer"/><constructor-arg value="白骨精~" type="java.lang.String"/><constructor-arg value="吸人血~" type="java.lang.String"/>
</bean>
说明:
- 通过 index 属性来区分是第几个参数
- 通过 type 属性来区分是什么类型(按照参数顺序)
2.3 通过 p 名称空间配置 bean
在 xml 文件配置, 增加命名空间配置
将光标放在p , 输入alt+enter , 就会自动的添加xmlns
示例:
<!--通过p名称空间来配置bean将光标放在p , 输入alt+enter , 就会自动的添加xmlns
-->
<bean id="monster06" class="com.spring.bean.Monster"p:monsterId="500"p:name="红孩儿"p:skill="吐火"
/>
2.4 引用/注入外部 bean 对象
在 spring 的 ioc 容器, 可以通过 ref 来实现 bean 对象的相互引用
应用案例:
public class MemberDAOImpl {public MemberDAOImpl() {System.out.println("MemberDAOImpl 构造器...");}public void add() {System.out.println("MemberDAOImpl add()方法");}
}
(2)创 建 MemberServiceImpl.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;}
}
(3)在 beans.xml 配置
<!-- bean 对象的相互引用
1. 其它含义和前面一样
2. ref 表示 memberDAO 这个属性将引用/指向 id = memberDAOImpl 对象
-->
<bean id="memberServiceImpl" class="com.spring.service.MemberServiceImpl"><property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<bean id="memberDAOImpl" class="com.spring.dao.MemberDAOImpl"/>
(5)测试
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans03.xml");
MemberServiceImpl bean = (MemberServiceImpl)ioc.getBean("memberServiceImpl", MemberServiceImpl.class);
System.out.println(bean);
细节说明:
- 这里就体现出spring容器的依赖注入
- 注意在spring容器中, xml是作为一个整体来执行的, 即如果你引用到一个bean对象, 对你配置的顺序没有要求,即bean的顺序没有要求
- 建议还是按顺序,好处是阅读的时候,比较方便
2.5 引用/注入内部 bean 对象
在 spring 的 ioc 容器中, 可以直接在 bean 内部配置内部 依赖 bean 对象
<!--配置MemberServiceImpl对象-使用内部bean-->
<bean class="com.spring.service.MemberServiceImpl" id="memberService2"><!--自己配置一个内部bean--><property name="memberDAO"><bean class="com.spring.dao.MemberDAOImpl"/></property>
</bean>
2.6 引用/注入集合/数组类型
在 spring 的 ioc 容器, 给 bean 对象的集合/数组类型属性赋值
应用案例:
(1)创建 Master.java
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;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 都是 Stringprivate 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)配置 beans.xml
<!--配置Master对象
体会 spring 容器配置特点 依赖注入-非常灵活
-->
<bean class="com.spring.bean.Master" id="master"><property name="name" value="太上老君"/><!--给list属性赋值--><property name="monsterList"><list><!--引用的方法--><ref bean="monster01"/><ref bean="monster02"/><!--内部bean--><bean class="com.spring.bean.Monster"><property name="name" value="老鼠精"/><property name="monsterId" value="100"/><property name="skill" value="吃粮食"/></bean></list></property><!--给map属性赋值--><property name="monsterMap"><map><entry><key><value>monster03</value></key><!--这里使用的外部bean,引入--><ref bean="monster03"/></entry><entry><key><value>monster04</value></key><ref bean="monster04"/></entry></map></property><!--给set属性赋值--><property name="monsterSet"><set><ref bean="monster05"/><ref bean="monster06"/><bean class="com.spring.bean.Monster"><property name="name" value="金角大王"/><property name="skill" value="吐水"/><property name="monsterId" value="666"/></bean></set></property><!--给数组属性赋值array标签中使用 value 还是 bean , ref .. 要根据你的业务决定--><property name="monsterName"><array><value>小妖怪</value><value>大妖怪</value><value>老妖怪</value></array></property><!--给Properties属性赋值 结构k(String)-v(String)--><property name="pros"><props><prop key="username">root</prop><prop key="password">123456</prop><prop key="ip">127.0.0.1</prop></props></property>
</bean>
(3)测试
public static void main(String[] args) {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Master master01 = ioc.getBean("master", 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("username");String property2 = pros.getProperty("password");String property3 = pros.getProperty("ip");System.out.println(property1 + "\t" + property2 + "\t" + property3);//获取数组System.out.println("======数组=======");String[] monsterName = master01.getMonsterName();for (String s : monsterName) {System.out.println("妖怪名= " + s);}//获取 setSystem.out.println("======set=======");Set<Monster> monsterSet = master01.getMonsterSet();for (Monster monster : monsterSet) {System.out.println(monster);}
}
运行效果:
细节说明:
- 这个 Properties 是 Hashtable 的子类 , 是 key-value 的形式
- key 是 string 而 value 也是 string
2.7 通过 util 名称空间创建 list
spring 的 ioc 容器, 可以通过 util 名称空间创建 list
<!--通过 util 名称空间来创建 list 集合,可以当做创建 bean 对象的工具来使用
-->
<util:list id="myListBook"><value>三国演义</value><value>西游记</value><value>红楼梦</value><value>水浒传</value>
</util:list>
<bean id="bookStore" class="com.spring.bean.BookStore"><property name="bookList" ref="myListBook"/>
</bean>
集合
应用案例:
(1)创建 BookStore.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)修改 beans.xml , 增加配置
<!--通过 util 名称空间来创建 list 集合,可以当做创建 bean 对象的工具来使用
-->
<util:list id="myListBook"><value>三国演义</value><value>西游记</value><value>红楼梦</value><value>水浒传</value>
</util:list>
<bean id="bookStore" class="com.spring.bean.BookStore"><property name="bookList" ref="myListBook"/>
</bean>
(3)测试
@Test
public void bookStoreTest() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");BookStore bookStore = ioc.getBean("bookStore", BookStore.class);System.out.println(bookStore.getBookList());
}
2.8 级联属性赋值
spring 的 ioc 容器, 可以直接给对象属性的属性赋值, 即级联属性赋值
应用案例:
(1)创建 Dept.java
public class Dept {private String name;public Dept() {}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
(2)创建 Emp.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;}
}
(3)修改 beans.xml , 增加配置
<!--配置Dept对象-->
<bean class="com.spring.bean.Dept" id="dept"/>
<!--配置Emp对象-->
<bean class="com.spring.bean.Emp" id="emp"><property name="name" value="jack"/><property name="dept" ref="dept"/><!--给dept的name属性指定值[级联属性赋值]--><property name="dept.name" value="Java开发部门"/>
</bean>
2.9 通过静态工厂获取对象
在 spring 的 ioc 容器, 可以通过静态工厂获取 bean 对象
应用案例:
(1)创建 MyStaticFactory.java
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);}
}
(2)修改 beans.xml , 增加配置
<!-- 通过静态工厂来获取 bean 对象 -->
<bean id="my_monster" class="com.spring.factory.MyStaticFactory"factory-method="getMonster"><!-- constructor-arg 标签提供 key --><constructor-arg value="monster_01"/>
</bean>
(3)测试
@Test
public void test02() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster = ioc.getBean("my_monster", Monster.class);System.out.println(monster);
}
2.10 通过实例工厂获取对象
在 spring 的 ioc 容器, 可以通过实例工厂获取 bean 对象
应用案例:
(1)创建 MyInstanceFactory.java
import java.util.HashMap;
import java.util.Map;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)配置 beans.xml
<!-- 通过实例工厂来获取 bean 对象 -->
<bean id="myInstanceFactory" class="com.spring.factory.MyInstanceFactory"/>
<bean id="my_monster2" factory-bean="myInstanceFactory"factory-method="getMonster"><constructor-arg value="monster_02"/>
</bean>
(3)测试代码
@Test
public void getBeanByInstanceFactory() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster my_monster = ioc.getBean("my_monster2", Monster.class);System.out.println(my_monster);
}
(4)结果
2.11 通过 FactoryBean 获取对象
在 spring 的 ioc 容器,通过 FactoryBean 获取 bean 对象
应用案例:
(1)创建MyFactoryBean.java
说明:
- 该类实现了 FactoryBean 接口,并指定了泛型Monster
- 创建了一个map集合,用于存放创建好的对象
- 重写了getObject方法,返回keyVal对应的对象,即this.monster_map.get(keyVal)
- 重写了getObjectType方法,返回对象的运行类型
- 重写isSingleton方法,表示对象是否为单例,true为是
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;}@Overridepublic Monster getObject() throws Exception {
// TODO Auto-generated method stubreturn this.monster_map.get(keyVal);}@Overridepublic Class getObjectType() {
// TODO Auto-generated method stubreturn Monster.class;}@Overridepublic boolean isSingleton() {
// TODO Auto-generated method stubreturn true;}
}
(2)配置 beans.xml
<!--1. 通过 FactoryBean 来获取 bean 对象2. name="keyVal" 就是 MyFactoryBean 定义的 setKeyVal 方法3. value="monster_01" ,就是给 keyVal 的值
-->
<bean id="myFactoryBean" class="com.spring.factory.MyFactoryBean"><property name="keyVal" value="monster_01"/>
</bean>
(3)测试代码
@Test
public void getBeanByFactoryBean() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster = ioc.getBean("myFactoryBean", Monster.class);System.out.println(monster);
}
2.12 bean 配置信息重用(继承)
在 spring 的 ioc 容器中, 提供了一种继承的方式来实现 bean 配置信息的重用
应用案例:
(1)配置beans.xml
<!-- 继承的方式来实现 bean 配置信息的重用 -->
<bean id="monster10" class="com.spring.bean.Monster"><property name="monsterId" value="10"/><property name="name" value="蜈蚣精"/><property name="skill" value="蜇人"/>
</bean>
<!-- parent="monster10" 就是继承使用了 monster10 的配置信息 -->
<bean id="monster11" class="com.spring.bean.Monster" parent="monster10"/>
<!-- 当我们把某个bean设置为 abstract="true" 这个bean只能被继承,而不能实例化了 -->
<bean id="monster12" class="com.spring.bean.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.spring.bean.Monster" parent="monster12"/>
说明:
- 可通过parent="monster10"方式继承其他bean,实现bean 配置信息的重用
- 把某个bean设置为 abstract="true" ,这个bean只能被继承,而不能实例化了
测试代码:
@Test
public void getBeanByExtends() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster1 = ioc.getBean("monster11", Monster.class);System.out.println(monster1);Monster monster2 = ioc.getBean("monster13", Monster.class);System.out.println(monster2);
}
2.13 bean 创建顺序
说明:
(1)在 spring 的 ioc 容器, 默认是按照配置的顺序创建 bean 对象
<bean id="student01" class="com.hspedu.bean.Student" />
<bean id="department01" class="com.hspedu.bean.Department" />
会先创建 student01 这个 bean 对象,然后创建 department01 这个 bean 对象
(2)如果这样配置,表示 student01 对象依赖于 department01 对象
<bean id="student01" class="com.hspedu.bean.Student" depends-on="department01"/>
<bean id="department01" class="com.hspedu.bean.Department" />
就会先创建 department01 对象,再创建 student01 对象.
(3)如果使用ref关联两个bean,无需关注配置顺序
说明:spring会先把有关联的对象都创建好,再处理引用关系
案例:
- 先创建 id=memberDAOImpl
- 再创建 id = memberServiceImpl
- 调用 memberServiceImpl.setMemberDAO() 完成引用
- 先创建 id = memberServiceImpl
- 再创建 id=memberDAOImpl
- 最后调用 memberServiceImpl.setMemberDAO() 完成引用
2.14 bean 对象的单例和多例
在 spring 的 ioc 容器, 默认是按照单例创建的,即配置一个 bean 对象后,ioc 容器只会创建一个 bean 实例。
如果,我们希望 ioc 容器配置的某个 bean 对象,是以多个实例形式创建的则可以通过配置 scope="prototype" 来指定,这样spring在获取对象时才会临时创建该对象,而不是像单例对象一样提前创建好
应用案例:
(1)创建 Car.java
public class cat {public cat() {System.out.println("cat 构造器");}
}
(2)配置 beans.xml
<!--
如果希望 ioc 容器配置的某个 bean 对象,
是以多个实例形式创建可以通过配置 scope="prototype" 来指定
-->
<bean name="cat" scope="prototype" class="com.spring.bean.Cat"/>
(3)测试代码
@Test
public void getBeanByPrototype() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");for (int i = 0; i < 3; i++) {Cat cat = ioc.getBean("cat", Cat.class);System.out.println(cat);}
}
细节说明:
- 在默认情况下 scope属性是 singleton,在启动容器时, 默认就会创建 , 并放入到 singletonObjects 集合
- 在ioc容器中, 只有一个这个bean对象
- 当执行getBean时, 返回的的是同一个对象
- 如果我们希望每次getBean返回一个新的Bean对象,则可以scope="prototype"
- 如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才 创 建 , 可 以 指 定 懒 加 载lazy-init="true" (注意默认是 false)
- 通常情况下, lazy-init 就使用默认值 false , 在开发看来, 用空间换时间是值得的, 除非有特殊的要求.
- 如果 scope="prototype" 这时你的 lazy-init 属性的值不管是 ture, 还是 false 都是在getBean 时候,才创建对象.
2.15 bean 的生命周期
:bean 对象创建是由 JVM 完成的,依次执行如下方法:
(1)创建 House.java
public class House {private String name;public House() {System.out.println("House() 构造器");}public String getName() {return name;}public void setName(String name) {System.out.println("House setName()...");this.name = name;}//初始化的方式public void init() {System.out.println("House init()..");}//销毁的方法public void destory() {System.out.println("House destory()..");}
}
(2)配置beans.xml,配置 bean 的初始化方法和销毁方法
<!-- 配置 bean 的初始化方法和销毁方法 -->
<bean id="house" class="com.spring.bean.House"init-method="init" destroy-method="destory"><property name="name" value="北京豪宅"/>
</bean>
(3)测试代码
@Test
public void beanLife() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");House house = ioc.getBean("house", House.class);System.out.println(house);//关闭容器((ConfigurableApplicationContext) ioc).close();
}
细节说明:
- 初始化 init 方法和 destory 方法, 是程序员来指定
- 销毁方法就是当关闭容器时,才会被调用
2.16 配置 bean 的后置处理器
说明:
(1)在 spring 的 ioc 容器,可以配置 bean 的后置处理器
(2)该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用
(3)程序员可以在后置处理器中编写自己的代码
应用案例:
(1)创 建 后 置处 理 器 MyBeanPostProcessor.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 stubSystem.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;}
}
0(2)配置beans03.xml
当我们在xml 容器配置文件 配置了 MyBeanPostProcessor,这时后置处理器对象,就会作用在该容器创建的所有Bean对象上
<!-- 配置 bean 的初始化方法和销毁方法 -->
<bean id="house" class="com.spring.bean.House"init-method="init" destroy-method="destory"><property name="name" value="北京豪宅"/>
</bean>
<!-- bean 后置处理器的配置 -->
<bean id="myBeanPostProcessor" class="com.spring.bean.MyBeanPostProcessor" />
(3)测试代码
@Test
public void testBeanPostProcessor() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans03.xml");House house = ioc.getBean("house", House.class);System.out.println(house);//关闭容器((ConfigurableApplicationContext) ioc).close();
}
疑惑点说明:
- 怎么执行到这个方法:使用 AOP(反射+动态代理+IO+容器+注解)
- 有什么用:可以对 IOC 容器中所有的对象进行统一处理 ,比如 日志处理/权限的校验/安全的验证/事务管理.
- 针对容器的所有对象吗:是的=>切面编程特点
2.17 通过属性文件给 bean 注入值
在 spring 的 ioc 容器,通过属性文件给 bean 注入值
应用案例:
(1)resources/ 下创建my.properties
可以在Unicode编码转换 - 站长工具 (chinaz.com)网站将中文转成unicode编码
monsterId=1000
name=\u4e4c\u9f9f\u7cbe
skill=\u7f29\u8116\u5b50
(2)修改 src\beans.xml , 继续完成配置
location="classpath:my.properties" 表示在类路径下的my.properties读取
这时我们的属性值通过${属性名}获取,这里说的 属性名 就是 my.properties文件中的 k=v 的k
<!-- 1. 通过属性文件给 bean 注入值, 2. 需要导入: xmlns:context 名字空间,并指定属性文件路径
-->
<context:property-placeholder location="classpath:my.properties"/>
<bean id="monster100" class="com.spring.bean.Monster"><property name="monsterId" value="${monsterId}"/><property name="name" value="${name}"/><property name="skill" value="${skill}"/>
</bean>
(3)测试代码
@Test
public void setProByProFile() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster100 = ioc.getBean("monster100", Monster.class);System.out.println(monster100);
}
2.18 基于 XML 的 bean 的自动装配
在 spring 的 ioc 容器,可以实现自动装配 bean
应用案例:
public class OrderDao {public void saveOrder() {System.out.println("保存 一个订单...");}
}
创建 OrderService.java
public class OrderService {private OrderDao orderDao;public OrderDao getOrderDao() {return orderDao;}public void setOrderDao(OrderDao orderDao) {this.orderDao = orderDao;}
}
创建 OrderAction.java
public class OrderAction {private OrderService orderService;public OrderService getOrderService() {return orderService;}public void setOrderService(OrderService orderService) {this.orderService = orderService;}
}
(2)配置 beans.xml
根据类型进行自动组装
<!--autowire="byType" 表示根据类型进行自动组装.
--><bean id="orderAction" autowire="byType" class="com.spring.action.OrderAction" /><bean id="orderService" autowire="byType" class="com.spring.service.OrderService"/><bean id="orderDao" class="com.spring.dao.OrderDao"/>
根据名称进行自动组装
<!--
1. 说明: autowire = "byName" 会自动去找 id 为 setXxxx 后面 Xxxx 的 bean 自动组装.
,如果找到就装配,如果找不到就报错, 比如这里的
2. <bean id="orderAction" autowire="byName" class="com.spring.bean.OrderAction" />
就 会 去 找 OrderAction 类 中 定 义 的 setOrderService 的 id 为 orderService 的
OrderService bean 组装,找到就阻装,找不到就组装失败
-->
<bean id="orderAction" autowire="byName" class="com.spring.action.OrderAction"/>
<bean id="orderService" autowire="byName" class="com.spring.service.OrderService"/>
<bean id="orderDao" class="com.spring.dao.OrderDao"/>
说明:
(1)autowire="byType" 表示 在创建 orderService时通过类型的方式 给对象属性 自动完成赋值/引用
(2)比如OrderService 对象有 private OrderDao orderDao
(3)就会在容器中去找有没有 OrderDao类型对象
(4)如果有,就会自动的装配, 如果是按照 byType 方式来装配, 这个容器中,不能有两个OrderDao类型对象
(5)如果对象没有属性, autowire就没有必要写
(6)如果设置的是 autowire="byName" 表示通过名字完成自动装配
(7)比如下面的 autowire="byName" class="com.hspedu.spring.service.OrderService"
- 先看 OrderService 属性 private OrderDao orderDao
- 再根据这个属性的setXxx()方法的 xxx 来找对象id(而不是根据属性名)
- public void setOrderDao() 就会找id=orderDao对象来进行自动装配
- 如果没有就装配失败
2.19 spring el 表达式
(1)Spring Expression Language,Spring 表达式语言,简称 SpEL。支持运行时查询并可以操 作对象。
(2)和 EL 表达式一样,SpEL 根据 JavaBean 风格的 getXxx()、setXxx()方法定义的属性访问对象
(3)SpEL 使用#{…}作为定界符,所有在大框号中的字符都将被认为是 SpEL 表达式。
应用案例:
(1)创建 SpELBean.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)配置 beans.xml
<!-- spring el 表达式 -->
<bean id="spELBean" class="com.spring.bean.SpELBean"><!-- sp el 给字面量 --><property name="name" value="#{'小苏'}"/><!-- sp el 引用其它 bean --><property name="monster" value="#{monster01}"/><!-- sp el 引用其它 bean 的属性值 --><property name="monsterName" value="#{monster01.name}"/><!-- sp el 调用普通方法(返回值) 赋值 --><property name="crySound" value="#{spELBean.cry('喵喵的..')}"/><!-- sp el 调用静态方法(返回值) 赋值 --><property name="bookName" value="#{T(com.spring.bean.SpELBean).read(' 天龙八部')}"/><!-- sp el 通过运算赋值 --><property name="result" value="#{89*1.2}"/>
</bean><!--配置一个monster对象-->
<bean id="monster01" class="com.spring.bean.Monster"><property name="monsterId" value="100"/><property name="name" value="蜈蚣精~"/><property name="skill" value="蜇人~"/>
</bean>
(3)测试代码
@Test
public void setProBySpel() {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());
}
3 基于注解配置 bean
3.1 基本介绍
基于注解的方式配置 bean, 主要用于项目开发中的组件,比如 Controller、Service、和 Dao.
组件注解的形式有:
(1)@Component 表示当前注解标识的是一个组件
(2)@Controller 表示当前注解标识的是一个控制器,通常用于 Servlet
(3)@Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于 Service 类
(4)@Repository 表示当前注解标识的是一个持久化层的类,通常用于 Dao 类
3.2 快速入门案例
使用注解的方式来配置 Controller / Service / Respository / Component
(1)创建 UserAction.java UserService.java, UserDao.java MyComponent.java
/*** @Controller 标识该类是一个控制器Controller, 通常这个类是一个Servlet*/
@Controller
public class UserAction {}
/*** @Service 标识该类是一个Service类/对象*/
@Service
public class UserService {
}
/*** 使用 @Repository 标识该类是一个Repository是一个持久化层的类/对象*/
@Repository
public class UserDao {
}
/*** @Component 标识该类是一个组件, 是一个通用的注解*/
@Component
public class MyComponent {
}
(2)在xml文件中配置要扫描的包
<!--配置容器要扫描的包
1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器
2. base-package 指定要扫描的包
3. 含义是当spring容器创建/初始化时,就会扫描com.spring.component包下的所有的 有注解 @Controller / @Service / @Respository / @Component类将其实例化,生成对象,放入到ioc容器
-->
<context:component-scan base-package="com.spring.component"/>
(3)测试代码
@Test
public void getBeanByAnnotation() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.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);
}
运行结果:
(5)细节说明:
- 必须在 Spring 配置文件中指定"自动扫描的包",IOC 容器才能够检测到当前项目中哪些类被标识了注解, 注意到导入 context 名称空间。可以使用通配符 * 来指定 ,比如 com.spring.* 表示。
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.spring.component" />
- Spring 的 IOC 容器不能检测一个使用了 @Controller 注解的类到底是不是一个真正的控制器。注解的名称是用于程序员自己识别当前标识的是什么组件。其它的 @Service@Repository 也是一样的道理( 也就是说 spring 的 IOC 容器只要检查到注解就会生成对象,但是这个注解的含义 spring 不会识别,注解是给程序员编程方便看的)
- <context:component-scan base-package="com.hspedu.spring.component" resource-pattern="User*.class" />,resource-pattern="User*.class": 表示只扫描com.spring.component 和它的子包下的User打头的类
- 排除指定类
如果我们希望排除某个包/子包下的某种类型的注解,可以通过exclude-filter来指定
(1)context:exclude-filter 指定要排除哪些类
(2)type 指定排除方式 ,annotation表示按照注解来排除
(3)expression="org.springframework.stereotype.Service" 指定要排除的注解的全路径
<context:component-scan base-package="com.spring.component"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
以上配置表示不扫描带Service和Repository注解的类
- 扫描指定类
如果我们希望按照自己的规则,来扫描包/子包下的某些注解, 可以通过 include-filter
(1)use-default-filters="false" 表示不使用默认的过滤机制/扫描机制(这个一定要有)
(2)context:include-filter 表示要去扫描哪些类
(3)type="annotation" 按照注解方式来扫描/过滤
(4)expression="org.springframework.stereotype.Service" 指定要扫描的注解的全路径
<context:component-scan base-package="com.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:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
以上配置表示只扫描com.spring.component包下,带Service、Repository、Controller注解的类
- 标记注解后,默认类名首字母小写作为 id 的值。也可以使用注解的 value 属性 指定 id 值,并且 value 可以省略。
@Controller(value="userAction01")//value可以省略
@Controller("userAction01")
3.3 实现简单的 Spring 基于注解配置的程序
3.3.1 实现思路
3.3.2 代码实现
说明:以下实现代码基于上方入门案例中的四个类
(1)创建ComponentScan.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {//表示ComponentScan注解可以传入一个value属性String value() default "";
}
(2)创建WwjSpringConfig.java
/*** 这是一个配置类,作用类似于原生Spring的 beans.xml 容器配置文件*/
@ComponentScan(value = "com.spring.component")
public class WwjSpringConfig {
}
(3)创建WwjSpringApplicationCo ntext.java
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** 该类作用类似于Spring原生ioc容器*/
public class WwjSpringApplicationContext {//拿到配置类.class文件private Class configClass;//ioc存放的就是通过反射创建的对象private final ConcurrentHashMap<String,Object> ioc = new ConcurrentHashMap<>();//构造器public WwjSpringApplicationContext(Class configClass){this.configClass = configClass;
// System.out.println("this.configClass=" + this.configClass);//获取要扫描的包//1.先获取到ComponentScan注解ComponentScan componentScan = (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);//2.获取到注解的value值String path = componentScan.value();
// System.out.println("要扫描的包= " + path);//得到要扫描的包下的所有class文件(在target目录下)//1.得到类的加载器ClassLoader classLoader = WwjSpringApplicationContext.class.getClassLoader();//2.通过类的加载器获取到要扫描的包的资源路径path = path.replace(".","/");//一定要把.替换成/URL resource = classLoader.getResource(path);
// System.out.println(resource);//3.将要加载的资源(.class) 路径下的文件进行遍历File file = new File(resource.getFile());if (file.isDirectory()){File[] files = file.listFiles();for (File f : files) {
// System.out.println(f.getAbsolutePath());//获取到.class文件的绝对路径String fileAbsolutePath = f.getAbsolutePath();//这里只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//获取到类全类名//1.获取到类名String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
// System.out.println(className);//2.拼接成全类名String classFullName = path.replace("/",".") + "." + className;
// System.out.println(classFullName);//3.判断该类是不是需要注入容器,看该类是不是有注解 @Component @Service @Repository @Controllertry {//这时,就得到了该类的Class对象//1. Class clazz = Class.forName(classFullName) 可以反射加载类//2. classLoader.loadClass(classFullName); 也可以反射类的Class//3. 区别是 : 上面方式会调用该类的静态方法, 下面方法不会//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @ComponentClass<?> aClass = classLoader.loadClass(classFullName);//判断该类是否有 @Component @Service @Repository @Controllerif (aClass.isAnnotationPresent(Component.class) ||aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Repository.class) ||aClass.isAnnotationPresent(Controller.class)){String keyVal = StringUtils.uncapitalize(className);//获取到value值,替换指定idif (aClass.isAnnotationPresent(Component.class)){Component annotation = aClass.getDeclaredAnnotation(Component.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}} else if (aClass.isAnnotationPresent(Service.class)) {Service annotation = aClass.getDeclaredAnnotation(Service.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}} else if (aClass.isAnnotationPresent(Repository.class)) {Repository annotation = aClass.getDeclaredAnnotation(Repository.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}} else if (aClass.isAnnotationPresent(Controller.class)) {Controller annotation = aClass.getDeclaredAnnotation(Controller.class);String value = annotation.value();if (!value.equals("")){keyVal = value;}}//这时候就可以创建该类的对象,并放入到ioc容器中Class<?> clazz = Class.forName(classFullName);Object o = clazz.newInstance();
// String defaultID = className.substring(0,1).toLowerCase() + className.substring(1);
// System.out.println(defaultID);//org.springframework.util.StringUtils包中的静态方法可以将字符串首字母小写ioc.put(keyVal,o);}} catch (Exception e) {e.printStackTrace();}}}}}public Object getBean(String id){return ioc.get(id);}
}
(4)创建测试主程序WwjSpringApplicationContextTest.java
public class WwjSpringApplicationContextTest {public static void main(String[] args) {WwjSpringApplicationContext ioc = new WwjSpringApplicationContext(WwjSpringConfig.class);System.out.println(ioc.getBean("yss1"));System.out.println(ioc.getBean("yss2"));System.out.println(ioc.getBean("yss3"));}
(5)运行结果
3.4 自动装配
基于注解配置 bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource
3.4.1 @AutoWired 的规则说明
3.4.2 @Resource 的规则说明
3.4.3 应用案例
(1)实现 UserAction 和 UserService 的两级自动组装
@Service
public class UserService {//方法..public void hi(){System.out.println("UserService hi()~");}
}
/*** @Controller 标识该类是一个控制器Controller, 通常这个类是一个Servlet*/
@Controller
public class UserAction {//@Autowired 自动装配 UserService, 这时是以 UserService,class 类型对对象进行组装//如待装配的类型对应的 bean 在 IOC 容器中有多个,则是以 id=userService 的 UserService 对象进行组装@Autowiredprivate UserService userService;public void sayOk() {System.out.println("UserAction 的sayOk()");System.out.println("userAction 装配的 userService属性=" + userService);userService.hi();}//不写这个方法,也可以完成组装public void setUserService(UserService userService) {this.userService = userService;}
}
(2)测试代码
@Test
public void setProByAnnotationAutowired() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");UserAction userAction01 = ioc.getBean(UserAction.class);userAction01.sayOk();
}
3.5 泛型依赖注入
基本说明:
(1)为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的注入机制
(2)在继承关系复杂情况下,泛型依赖注入就会有很大的优越性
应用实例:
(1)各个类关系图
(2)传统方法是将 PhoneDao /BookDao 自动装配到 BookService/PhoneSerive 中,当这种继承关系多时,就比较麻烦,可以使用 spring 提供的泛型依赖注入
(3)创建 Book.java,Phone.java等类
package com.spring.depinjection;
public class Book {
}package com.spring.depinjection;public class Phone {
}package com.spring.depinjection;public abstract class BaseDao<T> {public abstract void save();
}package com.spring.depinjection;
import org.springframework.stereotype.Repository;@Repository
public class BookDao extends BaseDao<Book> {@Overridepublic void save() {System.out.println("BookDao 的 save()");}
}package com.spring.depinjection;
import org.springframework.stereotype.Repository;@Repository
public class PhoneDao extends BaseDao<Phone> {@Overridepublic void save() {System.out.println("PhoneDao 的 save()");}
}package com.spring.depinjection;
import org.springframework.beans.factory.annotation.Autowired;public class BaseService<T> {@Autowiredprivate BaseDao<T> baseDao;public void save() {baseDao.save();}
}package com.spring.depinjection;
import org.springframework.stereotype.Service;@Service
public class BookService extends BaseService<Book> {
}package com.spring.depinjection;
import org.springframework.stereotype.Service;@Service
public class PhoneService extends BaseService<Phone> {
}
(4)修改 beans06.xml , 增加配置
<context:component-scan base-package="com.hspedu.spring.depinjection"/>
(5)测试代码
@Test
public void setProByDepinjectionAutowired() {ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml");BookService bookService = ioc.getBean(BookService.class);bookService.save();PhoneService phoneService = ioc.getBean(PhoneService.class);phoneService.save();
}
(6)测试结果