Spring的DI

bean.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <!-- spring中的依赖注入
        依赖注入:
            Dependency Injection
        IOC的作用:
            降低程序间的耦合(依赖关系)
        依赖关系的管理:
            以后都交给spring来维护
        在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
        依赖关系的维护:
            就称之为依赖注入。
         依赖注入:
            能注入的数据:有三类
                基本类型和String
                其他bean类型(在配置文件中或者注解配置过的bean)
                复杂类型/集合类型
             注入的方式:有三种
                第一种:使用构造函数提供
                第二种:使用set方法提供
                第三种:使用注解提供(明天的内容)
     -->
 
 
    <!--构造函数注入:
        使用的标签:constructor-arg
        标签出现的位置:bean标签的内部
        标签中的属性
            type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
            index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
            name:用于指定给构造函数中指定名称的参数赋值                                        常用的
            =============以上三个用于指定给构造函数中哪个参数赋值===============================
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
 
        优势:
            在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
        弊端:
            改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
    -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="泰斯特"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
 
    <!-- 配置一个日期对象 -->
    <bean id="now" class="java.util.Date"></bean>
 
 
 
    <!-- set方法注入                更常用的方式
        涉及的标签:property
        出现的位置:bean标签的内部
        标签的属性
            name:用于指定注入时所调用的set方法名称
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
        优势:
            创建对象时没有明确的限制,可以直接使用默认构造函数
        弊端:
            如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
    -->
    <bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
        <property name="name" value="TEST" ></property>
        <property name="age" value="21"></property>
        <property name="birthday" ref="now"></property>
    </bean>
 
 
    <!-- 复杂类型的注入/集合类型的注入
        用于给List结构集合注入的标签:
            list array set
        用于个Map结构集合注入的标签:
            map  props
        结构相同,标签可以互换
    -->
    <bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
 
        <property name="myList">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
 
        <property name="mySet">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
 
        <property name="myMap">
            <props>
                <prop key="testC">ccc</prop>
                <prop key="testD">ddd</prop>
            </props>
        </property>
 
        <property name="myProps">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>BBB</value>
                </entry>
            </map>
        </property>
    </bean>
 
 
 
 
 
 
 
 
 
 
 
</beans>

AccountServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.itheima.service.impl;
 
import com.itheima.service.IAccountService;
 
import java.util.Date;
 
/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
 
    //如果是经常变化的数据,并不适用于注入的方式
    private String name;
    private Integer age;
    private Date birthday;
 
    public AccountServiceImpl(String name,Integer age,Date birthday){
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
 
    public void  saveAccount(){
        System.out.println("service中的saveAccount方法执行了。。。"+name+","+age+","+birthday);
    }
 
 
}

AccountServiceImpl2.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.itheima.service.impl;
 
import com.itheima.service.IAccountService;
 
import java.util.Date;
 
/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl2 implements IAccountService {
 
    //如果是经常变化的数据,并不适用于注入的方式
    private String name;
    private Integer age;
    private Date birthday;
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
 
    public void  saveAccount(){
        System.out.println("service中的saveAccount方法执行了。。。"+name+","+age+","+birthday);
    }
 
 
}

AccountServiceImpl3.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.itheima.service.impl;
 
import com.itheima.service.IAccountService;
 
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Map;
 
/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl3 implements IAccountService {
 
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProps;
 
    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }
 
    public void setMyList(List<String> myList) {
        this.myList = myList;
    }
 
    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }
 
    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }
 
    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }
 
    public void  saveAccount(){
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
    }
 
 
}

Spring创建Bean的三种方式的使用和区别

在学习Spring的时候,发现Spring的IOC(控制反转)为我们提供的三种创建Bean的方式。

1.Spring创建Bean的三种方式

这里采用XML配置,分别演示三种创建Bean的方式和代码。

先创建一个Bean   User类  三种方式都是为了得到这个User的对象

1
2
3
4
5
6
/**
 * User对象
 */
public class User {
   // 这里只是一个空对象
}

1.1 采用默认的无参构造创建实例

  XML配置:

<!– 默认的无参构建 –>

1
<bean id="user" class="ioc.pojo.User"></bean>

  测试:

1
2
3
4
5
6
7
@Test
public void testUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 默认的无参构造创建
User user = (User) context.getBean("user");
System.out.println("默认的无参构造创建:" + user);
}

 控制台输出:  默认的无参构造创建:ioc.pojo.User@3b088d51

1.2 采用静态工厂创建实例

 配置工厂类:

1
2
3
4
5
6
7
8
9
10
/**
 * User对象的工厂类
 */
public class UserFactory {
// 静态方法
public static User getUser1() {
return new User();
}
  
}

 XML配置:

  <!– 使用静态工厂创建user –>

1
 <bean id="user1" class="ioc.service.UserFactory" factory-method="getUser1"></bean>

 class 指的是该工厂类的包路径,factory-method 指的是该工厂类创建Bean的静态方法。注意:这里一定要静态方法

 测试:

1
2
3
4
5
6
7
@Test
public void testUser1(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 静态工厂创建
User user1 = (User) context.getBean("user1");
System.out.println("静态工厂创建:" + user1);
}

控制台输出结果:静态工厂创建:ioc.pojo.User@3b088d51

1.3 采用实例工厂创建实例

 配置工厂类:

1
2
3
4
5
6
7
8
9
/**
 * User对象的工厂类
 */
public class UserFactory {
//普通方法
public User getUser2() {
return new User();
}
}

XML配置:

<!– 使用实例工厂创建 user –>

1
2
    <bean id="userFactory" class="ioc.service.UserFactory"></bean>
    <bean id="user2" factory-bean="userFactory" factory-method="getUser2"></bean>

测试:

1
2
3
4
5
6
7
@Test
public void testUser2(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 实例工厂创建
User user2 = (User) context.getBean("user2");
System.out.println("实例工厂创建:" + user2);
}

控制台输出结果:实例工厂创建:ioc.pojo.User@3b088d51

好了,实现了Spring三种Bean,感觉很顺利。

—————————————————————————————————————————————————————–

那么问题来了,为什么Spring要提供三种创建Bean的方式呢?

这三种创建Bean的方式又有什么区别呢?接下来开始做实验。

实验一: 三种方式创建的Bean是否有联系?

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 默认的无参构造创建
User user = (User) context.getBean("user");
System.out.println("默认的无参构造创建:" + user);
// 静态工厂创建
User user1 = (User) context.getBean("user1");
System.out.println("静态工厂创建:" + user1);
// 实例工厂创建
User user2 = (User) context.getBean("user2");
System.out.println("实例工厂创建:" + user2);
}

控制台输出结果:

默认的无参构造创建:ioc.pojo.User@3b088d51

静态工厂创建:ioc.pojo.User@1786dec2

实例工厂创建:ioc.pojo.User@74650e52

结论:三种方式都是创建一个新的实例对象。实例对象都是独立的,没有联系!

实验二: 三种方式创建的Bean的时机是否不同?

这里采用 bean的配置 init-method 初始化方法来查看Bean的实例是什么时候被加载的

是在加载配置文件的时候?还是在调用getBean()方法的时候?

修改User类,添加init()方法

1
2
3
4
5
6
7
8
9
/**
 * User对象
 */
public class User {
  
public void init(){
System.out.println("user被初始化啦");
}
}

XML配置:

1
2
3
4
5
6
7
8
9
    <!-- 默认的无参构建 -->
    <bean id="user" class="ioc.pojo.User" init-method="init"></bean>
  
    <!-- 使用静态工厂创建user -->
    <bean id="user1" class="ioc.service.UserFactory" factory-method="getUser1" init-method="init"></bean>
  
    <!-- 使用实例工厂创建 user -->
    <bean id="userFactory" class="ioc.service.UserFactory"></bean>
    <bean id="user2" factory-bean="userFactory" factory-method="getUser2" init-method="init"></bean>

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println("=====================================");
// 默认的无参构造创建
User user = (User) context.getBean("user");
System.out.println("默认的无参构造创建:" + user);
// 静态工厂创建
User user1 = (User) context.getBean("user1");
System.out.println("静态工厂创建:" + user1);
// 实例工厂创建
User user2 = (User) context.getBean("user2");
System.out.println("实例工厂创建:" + user2);
}

控制台输出结果:

user被初始化啦

user被初始化啦

user被初始化啦

=====================================

默认的无参构造创建:ioc.pojo.User@3b088d51

静态工厂创建:ioc.pojo.User@1786dec2

实例工厂创建:ioc.pojo.User@74650e52

结论:从初始化方法可以看出,Spring这三种创建实例的方式都是一样的,在加载配置文件的时候就创建了实例,证明这三种方式实例加载的时机是一样的。

========================================================================================

很明显,这三种方式最根本的区别还是创建方式的不同。

第一种,通过默认的无参构造方式创建,其本质就是把类交给Spring自带的工厂(BeanFactory)管理、由Spring自带的工厂模式帮我们维护和创建这个类。如果是有参的构造方法,也可以通过XML配置传入相应的初始化参数,这种也是开发中用的最多的。

第二种,通过静态工厂创建,其本质就是把类交给我们自己的静态工厂管理,Spring只是帮我们调用了静态工厂创建实例的方法,而创建实例的这个过程是由我们自己的静态工厂实现的,在实际开发的过程中,很多时候我们需要使用到第三方jar包提供给我们的类,而这个类没有构造方法,而是通过第三方包提供的静态工厂创建的,这是时候,如果我们想把第三方jar里面的这个类交由spring来管理的话,就可以使用Spring提供的静态工厂创建实例的配置。

第三种,通过实例工厂创建,其本质就是把创建实例的工厂类交由Spring管理,同时把调用工厂类的方法创建实例的这个过程也交由Spring管理,看创建实例的这个过程也是有我们自己配置的实例工厂内部实现的。在实际开发的过程中,如Spring整合Hibernate就是通过这种方式实现的。但对于没有与Spring整合过的工厂类,我们一般都是自己用代码来管理的。

<!–把对象的创建交给spring来管理–>

    <!–spring对bean的管理细节

        1.创建bean的三种方式

        2.bean对象的作用范围

        3.bean对象的生命周期

    –>

    <!–创建Bean的三种方式 –>

    <!– 第一种方式:使用默认构造函数创建。

            在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。

            采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>

    –>

    <!– 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

    <bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>

    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

    –>

    <!– 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

    <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>

    –>

    <!– bean的作用范围调整

        bean标签的scope属性:

            作用:用于指定bean的作用范围

            取值: 常用的就是单例的和多例的

                singleton:单例的(默认值)

                prototype:多例的

                request:作用于web应用的请求范围

                session:作用于web应用的会话范围

                global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"></bean>

    –>

    <!– bean对象的生命周期

            单例对象

                出生:当容器创建时对象出生

                活着:只要容器还在,对象一直活着

                死亡:容器销毁,对象消亡

                总结:单例对象的生命周期和容器相同

            多例对象

                出生:当我们使用对象时spring框架为我们创建

                活着:对象只要是在使用过程中就一直活着。

                死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收

     –>