解耦
工厂模式+反射+配置文件
bean.properties配置文件
Beanfactory工厂类
1 | public class Beanfactory { |
2 | private static Properties prop; |
3 | //初始化properties实例,用于装载配置文件 |
4 | //配置文件为beanName=beanPath的形式,beanPath为全限定类名 |
5 | //静态代码块装载配置文件 |
6 | static { |
7 | try { |
8 | InputStream is=Beanfactory.class.getResourceAsStream("bean.properties"); |
9 | //获取到resources文件夹下的bean.properties配置文件 |
10 | prop.load(is); |
11 | //通过输入流获取配置文件加载到properties实例 |
12 | } catch (IOException e) { |
13 | e.printStackTrace(); |
14 | } |
15 | } |
16 | //通过方法获取beanName对应的bean对象 |
17 | public Object getBean(String beanName){ |
18 | Object bean=null; |
19 | String beanPath=prop.getProperty(beanName); |
20 | //获取beanName在配置文件中对应的beanPath |
21 | try { |
22 | bean=Class.forName(beanPath).newInstance(); |
23 | //通过反射用beanPath创建bean对象实例 |
24 | } catch (Exception e) { |
25 | e.printStackTrace(); |
26 | } |
27 | return bean; |
28 | } |
29 | } |
这样一来,就不会频繁的new对象了,减少了对象之间的耦合。
整成单例(恶汉式)
刚刚的代码,每次都会调用newInstance()方法,会创建多个对象。在这里将其改成单例模式,在加载的时候便将所有实例创建好。
1 | public class BeanfactorySingle { |
2 | private static Properties prop; |
3 | //初始化properties实例,装载配置文件 |
4 | //配置文件中为beanName=beanPath的形式,beanPath为全限定类名 |
5 | private static Map<String,Object> beans; |
6 | //存放bean的容器<beanName,bean对象> |
7 | static { |
8 | try { |
9 | InputStream is=Beanfactory.class.getResourceAsStream("bean.properties"); |
10 | //获取到resources文件夹下的bean.properties配置文件 |
11 | prop.load(is); |
12 | //通过输入流获取配置文件加载到properties实例 |
13 | beans=new HashMap<String, Object>(); |
14 | Enumeration keys=prop.keys(); |
15 | //取出配置文件所有的key |
16 | while (keys.hasMoreElements()){ |
17 | String key=keys.nextElement().toString(); |
18 | //取出每个key |
19 | String beanPath=prop.getProperty(key); |
20 | //获得每个key的value |
21 | Object value=Class.forName(beanPath).newInstance(); |
22 | //根据全限定类名用反射创建实例 |
23 | beans.put(key,value); |
24 | //将所有beanName和bean实例存入容器 |
25 | } |
26 | } catch (Exception e) { |
27 | e.printStackTrace(); |
28 | } |
29 | } |
30 | //从beans容器中取出即可 |
31 | public Object getBean(String beanName){ |
32 | return beans.get(beanName); |
33 | } |
34 | } |
IOC(控制反转)
将对象的创建控制权转接给了工厂,根据名字由工厂提供,每次需要对象的时候找工厂拿即可。
核心容器
ApplicationContext
ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在就加载不了。
FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件(必须要有访问权限)。
AnnotationConfigApplicationContext:用于读取注解创建容器。
引发的问题
ApplicationContext:构建核心容器时,立即加载配置文件中的内容。适用于单例对象。实际开发中多用这个接口。
BeanFacroty:采取的是懒加载的方式,当根据id获取对象时,才开始创建对象。适用于多例对象。
bean的管理
创建bean的三种方式
1,用默认构造函数
使用bean标签,配置id和class属性,且没有其他标签和属性时,采用的是默认无参构造函数,如果没有就无法创建。
1 | <bean id="accountMapper" class="com.czw.mapper.impl.AccountMapperImpl"></bean> |
2,使用某个类中的方法创建
存在一个工厂类,类中有一个方法可以返回一个car对象
1 | public class InstanceFactory{ |
2 | public IAccountMapper getAccountMapper(){ |
3 | return new AccountMapperImpl(); |
4 | } |
5 | } |
此时,将工厂存入容器
1 | <bean id="InstanceFactory" class="com.czw.factory.InstanceFactory"></bean> |
2 | <bean id="accountMapperByFactory" factory-bean="InstanceFactory" factory-method="getAccountMapper"></bean> |
通过指定工厂(InstanceFactory)的创建实例方法(getAccountMapper)得到需要的对象。
3,使用静态工厂的静态方法
1 | <bean id="accountMapperStatic" class="com.czw.factory.StaticFactory" factory-method="getAccountMapper"></bean> |
bean作用范围
bean对象默认是单例的,可以通过bean标签的scope属性修改
取值:
singleton(默认单例),
prototype(多例),
request(作用于web应用请求范围),
session(作用于web应用会话范围),
global-session(作用于集群环境的会话范围,当不是集群环境,相当于session)
bean生命周期
1,单例对象
容器创建时,对象出生。容器在,对象在。容器毁,对象死。和容器完全一致。
1 | <bean id="accountMapper" class="省略" init-method="init" destroy-method="destroy"></bean> |
init()和destroy()为对象的初始化和销毁方法。
在容器创建时,init方法执行了。
可以不使用接口类型创建容器,而直接使用实现类创建,然后调用close方法手动关闭容器,此时发现容器销毁时,destroy方法执行了。
2,多例对象
使用对象时,spring才创建对象,使用过程中一直存活,spring不会销毁对象,当对象长时间不用,且没有别的对象引用时,由Java垃圾回收器回收。
依赖注入( Dependency Injection)
当需要用到其他类的对象,由spring提供,我们只需要在配置文件中说明。
能注入的类型:
基本类型和String,其他bean类型(在配置文件或注解配置过的),复杂/集合类型
注入的方式:构造函数提供,set方法提供,注解提供
复杂类型注入
在property标签里使用子标签:array,list,set,map,props
1 | <property name="aList"> |
2 | <list> |
3 | <value>aaa</value> |
4 | ... |
5 | </list> |
6 | </property> |
7 | <!-- array,set与这个一样--> |
8 | <property name="aMap"> |
9 | <map> |
10 | <entry key="ccc"> |
11 | <value>111</value> |
12 | </entry> |
13 | <entry key="ccc" value="222"></entry> |
14 | </map> |
15 | </property> |
16 | <!-- property与这个类似,entry改为prop,并且在两个prop标签中直接写内容--> |
注意:list结构的,map结构的,标签可以互换,比如list对象中用set标签,map对象中用props标签
构造函数注入
经常变化的数据并不适用。
在bean标签内部,使用constructor-arg标签
1 | <bean id="test" class="hello.world.test"> |
2 | <constructor-arg type="java.lang.String" value="test" index="0" name="name"></constructor-arg> |
3 | </bean> |
type:要注入的数据类型
value:要注入的值(用于基本类型,String类型)
index:要注入的参数索引
name:要注入的参数名字(常用)
ref:注入关联的bean对象(当遇到value不能转换的类型时使用,比如日期,复杂类型)
优势:获取bean对象时,注入数据是必须的操作,否则无法创建
缺点:改变了bean对象实例化的方式,使得在创建对象时,用不到这些参数也要提供。
set方法注入
在bean标签内部,使用property标签
1 | <bean id="test" class="hello.world.test"> |
2 | <property name="name">czw</property> |
3 | <property name="age" value="18"></property> |
4 | </bean> |
name对应的不是属性名称,而是set方法后的名称,调用set方法赋值。
优势:创建对象时,没有明确限制,可以直接使用默认构造方法
缺点:如果某个变量必须有值,则获取对象时可能set方法没有执行
总结
头发-1