优化的工厂模式
不加修改时,循环地调用工厂模式,将产生多个对象。这组对象将单独,独立地调用自己的方法。
工厂利用反射创建对象的语句:
bean=Class.forName(beanPath).newInstance();
由于使用了newInstance(),每次调用都必然调用默认构造函数,创建新对象。
使用单例对象,避免创建大量对象,可以提高程序效率。
解决方案:只使用一次newInstance方法,用容器(map)保存它。
代码如下:
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String,Object>();
//取出配置文件中所有的Key
Enumeration keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个Key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key,value);
}
}catch(Exception e){
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
由于静态代码块只在类加载时执行一次,故此时只会创建一个对象。这时,重定义get方法,直接根据传入的bean名返回对象即可。
优化后,工厂模式的整个流程,自顶向下地描述为:
- main()方法调用factory.getBean(beanName)尝试获取组件。
- 加载工厂类,静态地创建容器。
- 使用类加载器获取配置文件流,替代绝对路径与相对路径。
- 根据配置文件(properties),填充容器。
- get方法根据传入的beanName,将容器中存储的对应对象传出。
在具体开发中,几乎每个类,都定义了接口,我不确定这是否必要,程序需要多层抽象,业务逻辑和实现逻辑完全分层。通过在高层类中实例化低层类的私有对象来实现。
控制反转(IOC)
使用New创建对象,相当于程序主动寻求资源(来创建对象);使用工厂模式,结构变化为:程序-寻求资源->工厂-控制资源->资源。这样就降低了依赖。
控制反转:需要创建对象的类,将创建对象的权利交给工厂。
Spring的IOC
Spring提供了上述部分 工厂 的功能。
这就是为什么我们需要一个额外的配置文件的原因:在项目pom.xml之外,需要新创建一个xml文件(在resoures文件夹下),它替代了此前properties配置文件的功能。
在这个xml文件中,我们指定spring负责工厂的功能,并指定Id和反射对象。
通过查找spring文档获取用于指定spring负责工厂的xml代码。
通过类似:
<bean id="" class=""></bean>
来添加反射相关配置。
那么类似上面的思路,先获取spring的核心容器,再根据id获取对象:
public static void main(String[] args){
//获取核心容器对象,前面是接口 这是实现类
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//或者使用,不推荐
//ApplicationContext ac = new FileSystemXmlApplicationContext("绝对路径");
//根据id获取bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
//和上面的工作一致,第二个参数完成类型转换
IAccountDao adao = ac.getBean("accountDao", IAccountDao.class)
}
Spring管理bean对象
创建Bean对象
- 在配置中使用Bean标签,只配有id和class属性,没有其他属性和标签,调用默认构造函数创建对象,如果没有显式声明(java需要显式声明)则创建失败。
- 使用工厂/某个类中的方法创建对象,并存入Spring容器。
<bean id="instanceFactory" class="com.test.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
第二个标签项指向了第一个,并告知spring通过其中的getAccountService方法创建对象。
3. 使用工厂/某个类中的静态方法来创建对象,并存入Spring容器。
<bean id="accountService" class="com.test.factory.StaticFactory" factory-method="getAccountService"></bean>
类似第二种方法,但是通过factory-method指定了调用访问到的类的方法。
bean的作用范围
通过指定bean 标签的scope属性,确定bean的作用范围。
<bean id="accountService" class="com.test.factory.StaticFactory" scope="singleton"></bean>
取值:
singleton: 单例的(默认值)
prototype: 多例的
request
session
global-session
bean的生命周期
单例对象:和容器相同,加载类时就会创建(立即创建)
多例对象:使用对象时,spring对象才会创建它。(延迟创建)长时间不使用时,由java执行回收。
依赖注入
使用构造函数注入
<bean id="accounetService" class="com.test.service.impl.AccountServiceImpl">
<!-- name是参数名 -->
<constructor-arg name="name1" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="data" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
所有的配置都被存入容器之中,通过id来访问。
value指向基本类型和String类型
ref指向其他bean类型。
两者结合起来,保证能向构造函数传递任意类型的数据。(对于其他类型,也是由基本类型产生的)
set方法注入(更常用)
在类中定义各个参数的set方法,之后在xml中添加:
<bean id="accounetService" class="com.test.service.impl.AccountServiceImpl">
<!-- name是参数名 -->
<property name="name1" value="test"></property>
<property name="age" value="18"></property>
<property name="data" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
由于set方法,在构造函数中,可以任意地指定参数,不会报警,更灵活了,这是优势,也是劣势(相较于构造函数注入来说)
集合类型的注入:
在property标签内,嵌入类似
<array><value>AAA</value>...</array>
其中,array可以替换为list set等集合类型。
也可以给map结构集合注入:
使用 map props 标签
听课笔记,仅供参考,如果您发现错误,欢迎在评论区指出。