0%

Spring春之初体验

解耦

工厂模式+反射+配置文件

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