0%

(二)Spring学习笔记-Bean管理

1. Bean实例化方式

下面演示一下Spring中的Bean实例化过程。

1.1 无参构造方法方式(默认)

  1. 编写如下类:
    1
    2
    3
    4
    5
    6
    public class Bean1 {
    public Bean1() {
    super();
    System.out.println("Bean1的无参构造方法执行了。。。");
    }
    }
  1. 在applicationContext.xml进行如下配置:

    1
    2
    <!-- 无参构造方法的方式 -->
    <bean id="bean1" class="com.shoto.spring.demo2.Bean1"></bean>
  2. 运行测试:

    1
    2
    3
    4
    5
    6
    @Test
    public void testBean1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    //输出结果:Bean1的无参构造方法执行了。。。
    Bean1 bean1 = (Bean1) applicationContext.getBean("bean1");
    }

    注意:如果当前的类Bean1中没有无参构造方法,则会发生错误。

    1.2 静态工厂实例化的方式

  3. 编写Bean2类和Bean2Factory工厂类:

    1
    2
    3
    4
    5
    6
    public class Bean2 {
    public Bean2() {
    super();
    System.out.println("Bean2的无参构造方法执行了。。。");
    }
    }
    1
    2
    3
    4
    5
    6
    public class Bean2Factory {
    public static Bean2 createBean2() {
    System.out.println("Bean2Factory中createBean2方法执行了。。。");
    return new Bean2();
    }
    }
  4. 在applicationContext.xml进行如下配置:

    1
    2
     <!--静态工厂实例化的方式  factory-method的值为Bean2Factory中的同名方法,用于调用工厂类方法-->
    <bean id="bean2" class="com.shoto.spring.demo2.Bean2Factory" factory-method="createBean2"></bean>
  5. 运行测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Test
    public void testBean2() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    /*
    * 输出结果:
    * Bean2Factory中createBean2方法执行了。。。
    * Bean2的无参构造方法执行了。。。
    */
    Bean2 bean2 = (Bean2) applicationContext.getBean("bean2");
    }

1.3 实例工厂实例化的方式

  1. 编写Bean3类和对应的实例工厂类Bean3Factory:

    1
    2
    3
    4
    5
    6
    7
    public class Bean3 {

    public Bean3() {
    super();
    System.out.println("Bean3的无参构造方法执行了。。。");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    public class Bean3Factory {

    public Bean3 createBean3() {
    System.out.println("Bean3的实例化工厂执行了。。。");
    return new Bean3();
    }
    }
  2. 在applicationContext.xml进行如下配置:

    1
    2
    3
    4
    5
    <!-- 实例工厂实例化方法 
    必须实例化工厂类(factory-bean)后才能调用工厂方法,用于实例化工厂类.
    -->
    <bean id="bean3Factory" class="com.shoto.spring.demo2.Bean3Factory"></bean>
    <bean id="bean3" class="com.shoto.spring.demo2.Bean3" factory-bean="bean3Factory" factory-method="createBean3"></bean>
  3. 运行测试同理,不再赘述。

2. 依赖注入(XML)

2.1 构造方法方式

  1. 编写User类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class User {
    private Integer uid;
    private String username;
    private String password;
    private Infor infor;

    public User(Integer uid, String username, String password, Infor infor) {
    super();
    this.uid = uid;
    this.username = username;
    this.password = password;
    this.infor = infor;
    }

    @Override
    public String toString() {
    return "User [uid=" + uid + ", username=" + username + ", password=" + password + ", infor=" + infor + "]";
    }
    }
  2. 编写Infor类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    private String gender;
    private String address;

    public Infor() {
    super();
    }

    public Infor(String gender, String address) {
    super();
    this.gender = gender;
    this.address = address;
    }
    //***getter,setter,toString
    }
  3. 在applicationContext.xml进行如下配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- 构造方法方式属性注入 -->
    <bean id="user" class="com.shoto.spring.demo3.User">
    <!-- index的值分别代表构造方法的参数下标 -->
    <constructor-arg index="0" value="001"></constructor-arg>
    <constructor-arg index="1" value="张三"></constructor-arg>
    <constructor-arg index="2" value="abc123!"></constructor-arg>
    <!-- 这里引用Infor类的Bean,ref中的值为Infor的Bean的id值 -->
    <constructor-arg index="3" ref="infor"></constructor-arg>
    </bean>

    <bean id="infor" class="com.shoto.spring.demo3.Infor">
    <constructor-arg index="0" value="男"></constructor-arg>
    <constructor-arg index="1" value="广州"></constructor-arg>
    </bean>
  4. 运行测试:

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void testConstructorDI() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = (User) applicationContext.getBean("user");

    System.out.println(user);//User [uid=1, username=张三, password=abc123!, infor=Infor [gender=男, address=广州]]
    }

2.2 set方法方式

  1. 在上面的User类中添加对应属性的setter方法以及无参构造方法,Spring默认使用无参构造方法来创建Bean的。

  2. 在applicationContext.xml进行如下配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <bean id="infor" class="com.shoto.spring.demo3.Infor">
    <constructor-arg index="0" value="男"></constructor-arg>
    <constructor-arg index="1" value="广州"></constructor-arg>
    </bean>

    <!-- setter方法方式属性注入 -->
    <bean id="user" class="com.shoto.spring.demo3.User">
    <property name="uid" value="002"></property>
    <property name="username" value="李四"></property>
    <property name="password" value="123456"></property>
    <property name="infor" ref="infor"></property>
    </bean>
  3. 运行测试同理,不再赘述。

    2.3 P名称空间方式

    2.3.1 写法

  • 普通属性:p:属性名=”值”
  • 对象属性:p:属性名-ref=”值”

2.3.2 引入

在这里插入图片描述

2.3.3 使用

在这里插入图片描述

2.4 SpEL方式

SpEL:Spring Expression Language,Spring的表达式语言。
使用语法:#{SpEL}

2.4.1 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- SpEL方式 -->
<bean id="infor" class="com.shoto.spring.demo3.Infor">
<property name="gender" value="#{'女'}"></property>
<property name="address" value="#{'北京'}"></property>
</bean>

<bean id="user" class="com.shoto.spring.demo3.User">
<property name="uid" value="#{003}"></property>
<property name="username" value="#{'王五'}"></property>
<!-- 为了测试,这里使用Infor类的address作为User类的password的值
另外也可以使用如下的方式:
<property name="password" value="#{infor.getAddress()}"></property>
-->
<property name="password" value="#{infor.address}"></property>
<!-- 这里引用Infor的Bean -->
<property name="infor" value="#{infor}"></property>
</bean>

注意:SpEL的功能远远不止用于属性的注入,它还可以进行运算、集合匹配和提供正则表达式进行匹配等,有时间在补充一下吧。

2.5 集合类型属性注入

  1. 编写CollectionBean类,如下示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class CollectionBean {
    private String[] arrs;
    private List<String> list;
    private Map<Integer,String> map;
    private Set<String> set;
    private Properties props;

    public CollectionBean() {
    super();
    }
    //***setter,getter
    }
  2. applicationContext.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
    <!-- Spring的集合属性注入 -->
    <bean id="collection" class="com.shoto.spring.demo3.CollectionBean">
    <!-- 注入数组类型 -->
    <property name="arrs">
    <array>
    <value>张三</value>
    <value>李四</value>
    <value>王五</value>
    </array>
    </property>
    <!-- 注入List集合类型 -->
    <property name="list">
    <list>
    <value>Smith</value>
    <value>Jack</value>
    <value>Jan</value>
    </list>
    </property>
    <!-- 注入Map集合类型 -->
    <property name="map">
    <map>
    <entry key="1" value="aaa"/>
    <entry key="2" value="bbb"/>
    <entry key="3" value="ccc"/>
    </map>
    </property>

    <!-- 注入Set集合类型 -->
    <property name="set">
    <set>
    <value></value>
    <value></value>
    <value></value>
    </set>
    </property>

    <!-- 注入Properties集合类型 -->
    <property name="props">
    <props>
    <prop key="prop1">prop1的值</prop>
    <prop key="prop2">prop2的值</prop>
    <prop key="prop3">prop3的值</prop>
    </props>
    </property>
    </bean>

    上面我们都是对字符串的各个集合的装载,下面我们对自定义类进行装载。

  3. 先编写三个POJO,代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Role {
    private Long id;
    private String roleName;
    private String note;

    public Role() {
    super();

    }

    public Role(Long id, String roleName, String note) {
    super();
    this.id = id;
    this.roleName = roleName;
    this.note = note;
    }
    //getter,setter,toString
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class User {
    private Long id;
    private String username;
    private String note;

    public User() {
    super();

    }
    public User(Long id, String username, String note) {
    super();
    this.id = id;
    this.username = username;
    this.note = note;
    }
    //getter,setter,toString
    }
    1
    2
    3
    4
    5
    6
    7
    public class UserRoleAssembly {
    private Long id;
    private List<Role> list;
    private Map<Role, User> map;
    private Set<Role> set;
    //getter,setter
    }
  4. 在applicationContext.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
    <!-- 配置Role -->
    <bean id="role1" class="com.shoto.spring.demo4.Role">
    <property name="id" value="1"/>
    <property name="roleName" value="roleName1"/>
    <property name="note" value="roleNote1"/>
    </bean>

    <bean id="role2" class="com.shoto.spring.demo4.Role">
    <property name="id" value="2"/>
    <property name="roleName" value="roleName2"/>
    <property name="note" value="roleNote2"/>
    </bean>

    <!-- 配置User -->
    <bean id="user1" class="com.shoto.spring.demo4.User">
    <property name="id" value="1"/>
    <property name="username" value="username1"/>
    <property name="note" value="userNote1"/>
    </bean>

    <bean id="user2" class="com.shoto.spring.demo4.User">
    <property name="id" value="2"/>
    <property name="username" value="username2"/>
    <property name="note" value="userNote2"/>
    </bean>

    <!-- 配置UserRoleAssembly-->
    <bean id="userRoleAssembly" class="com.shoto.spring.demo4.UserRoleAssembly">
    <property name="id" value="1"/>
    <!-- 给装载有自定义对象的List集合属性注入值 -->
    <property name="list">
    <list>
    <ref bean="role1"/>
    <ref bean="role2"/>
    </list>
    </property>
    <!-- 给装载有自定义对象的Map集合属性注入值 -->
    <property name="map">
    <map>
    <entry key-ref="role1" value-ref="user1"/>
    <entry key-ref="role2" value-ref="user2"/>
    </map>
    </property>
    <!-- 给装载有自定义对象的Set集合属性注入值 -->
    <property name="set">
    <set>
    <ref bean="role1"/>
    <ref bean="role2"/>
    </set>
    </property>
    </bean>
  5. 测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Test
    public void testCollection() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserRoleAssembly userRoleAssembly = (UserRoleAssembly) applicationContext.getBean("userRoleAssembly");

    System.out.println("输出List集合:");
    List<Role> list = userRoleAssembly.getList();
    for (Role r : list) {
    System.out.println(r);
    }
    System.out.println("输出Map集合:");
    Map<Role, User> map = userRoleAssembly.getMap();
    Set<Entry<Role, User>> entrySet = map.entrySet();
    for (Entry<Role, User> entry : entrySet) {
    System.out.println(entry.getKey() + "==" + entry.getValue());
    }
    System.out.println("输出Set集合:");
    Set<Role> set = userRoleAssembly.getSet();
    for (Role r : set) {
    System.out.println(r);
    }

    }

3. IoC的注解开发

3.1 入门案例

  1. 创建Web项目,引入Jar包
    在这里插入图片描述
    注意:在Spring4版本中,除了引入基本的开发包和日志相关包以外,还需要引入aop的包。

  2. 引入Spring的配置文件,即在src下创建applicationContext.xml文件

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

    </beans>

    注意:要使用注解开发需要引入context约束,具体的内容引用自spring-framework-4.2.4.RELEASE-dist\spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\xsd-configuration下。

  3. 编写接口UserDAO和实现类UserDAOImpl

    1
    2
    3
    4
    5
    package com.shoto.spring.demo;

    public interface UserDAO {
    public void save();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * UserDAO的实现类
    * @author 郑松涛
    *
    */
    @Component("userDAO")//相当于<bean id="userDAO" class="com.shoto.spring.demo.UserDAOImpl">
    public class UserDAOImpl implements UserDAO {

    @Override
    public void save() {
    System.out.println("UserDAOImpl中实现用户保存的方法执行了。。。");
    }
    }

    注意:使用注解Componet在类添加上注解。

  4. 开启Spring的组件扫描

    1
    2
    <!-- 使用IoC的注解开发,配置组件扫描,即扫描哪些包下的哪些类在**类上**使用了注解 -->
    <context:component-scan base-package="com.shoto.spring.demo" />
  5. 运行测试

    1
    2
    3
    4
    5
    6
    @Test
    public void test1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDAO userDAO = (UserDAO) applicationContext.getBean("userDAO");
    userDAO.save();//输出UserDAOImpl中实现用户保存的方法执行了。。。
    }

3.2 注解方式依赖注入

注解方式:使用注解方式,Bean所对应的类可以没有set方法。

  • 如果属性有set方法,则需要将属性注入的注解添加到set方法上;
  • 如果属性没有set方法,则需要将属性注入的注解添加在属性上。
    在这里插入图片描述
    注意:注解@Value代表的是值的注入。如果同时使用注解@Value在属性和对应的setter方法,则实际使用的setter方法的注解注入的值。

    3.3 注解详解

    3.3.1 @Component注解

    该注解用于修饰一个类,将这个类交给Spring管理,这个注解有三个衍生注解,功能类似,只是分别应用于不同的MVC层,具体如下:
  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

3.3.2 属性注入的注解

1. 普通属性:

@Value:用于设置好普通属性的值。

2. 对象类型的属性:
  • @Autowired:用于设置对象类型的属性的值,但是需要按照类型完成属性的注入,所谓按类型完成属性注入就是将与该属性的类型相同的bean注入进来,显然这是不太合理的,因为该属性如果是接口且有多个实现类,就会发生歧义而无法完成属性注入。因此可以使用@Autowired注解和@Qualifier注解的配合使用来完成按照名称属性注入。 具体用法如下(实现将UserDAOImpl2注入到UserDAOImpl中):

    新建一个UserDAO的新的实现类UserDAOImpl2,代码如下:

    1
    2
    3
    4
    5
    6
    7
    @Component("userDAOImpl2")//先使用注解声明创建Bean,才能被引用
    public class UserDAOImpl2 implements UserDAO {
    @Override
    public void save() {
    System.out.println("UserDAOImpl2的save方法执行了");
    }
    }

    修改UserDAOImpl类如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Component("userDAOImpl")//相当于<bean id="userDAOImpl" class="com.shoto.spring.demo.UserDAOImpl">
    public class UserDAOImpl implements UserDAO {
    @Value("张三")
    private String username;

    /*
    * 注入UserDAOImpl2
    * 相当于<property name="userDAOImpl2" ref="userDAOImpl2"/>
    */
    @Autowired
    @Qualifier("userDAOImpl2")//与UserDAOImpl中的@Component中的值相同
    private UserDAO userDAO;

    @Override
    public void save() {
    System.out.println("UserDAOImpl中实现用户保存的方法执行了。。。" + username);
    userDAO.save();
    }
    }

    运行测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Test
    public void test1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDAO userDAO = (UserDAO) applicationContext.getBean("userDAOImpl");
    /*
    * 运行输出:
    * UserDAOImpl中实现用户保存的方法执行了。。。张三
    * UserDAOImpl2的save方法执行了
    */
    userDAO.save();
    }
  • @Resource:相当于@AutoWired与@Qualifier的结合,完成对象类型的属性的注入,按照名称完成属性注入,常用于实际开发中。

    修改上面的UserDAOImpl为如下:
    在这里插入图片描述
    注意:@Resource注解使用name属性来引用

3.3.3 Bean的其他注解

1. 生命周期相关注解
  • @PostConstruct:初始化方法
  • @PreDestroy:销毁方法

下面演示一些这两个注解的简单使用:

首先编写一个Customer类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component(value="customer") //相当于<bean id="customer" class="com.shoto.spring.demo2.Customer"/>
public class Customer {

public void purchase() {
System.out.println("Customer中的purchase方法执行了。。。");
}

@PostConstruct //相当于bean中的init-method属性
public void init() {
System.out.println("Customer的init方法执行了。。。");
}

@PreDestroy //相当于bean中的destroy-method属性
public void destroy() {
System.out.println("Customer的destroy方法执行了。。。");
}
}

在applicatinContext.xml开启注解扫描:

1
2
<!-- 使用IoC的注解开发,配置组件扫描,即扫描哪些包下的类使用的注解 -->
<context:component-scan base-package="com.shoto.spring.demo2" />

测试代码:

1
2
3
4
5
6
7
8
9
@Test
public void testInitAndDestroy() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Customer customer = (Customer) applicationContext.getBean("customer");
customer.purchase();

//关闭工厂,才会调用destroy
applicationContext.close();
}

运行结果:
在这里插入图片描述

2. Bean作用范围相关注解

使用@Scope注解来设置作用范围,有以下4中作用域:

  • singleton(单例):默认选项,Spring会采用单例模式创建这个对象,即Bean实例;
  • prototype(多例):多例模式,Struts2和Spring整合一定会用到,不过现在Struts2很少用了。比如我们希望Struts2中的Action(Struts2的控制层类)有时候需要多个实例,这时就需要将Spring设置为多例模式了;
  • request(请求):应用在web项目中,就是在一次请求中Spring会创建一个实例,但是不同点的请求会创建不同的实例;
  • session (会话):应用在web项目中,就是在会话过程中Spring只创建一个实例

下面简单演示以下@Scope的使用:

在上面定义的Customer添加@Scope(“prototype”),如下所示:
在这里插入图片描述
运行测试:

1
2
3
4
5
6
7
8
9
@Test
public void testRange() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Customer customer1 = (Customer) applicationContext.getBean("customer");
Customer customer2 = (Customer) applicationContext.getBean("customer");
//两次的输出结果是不同的,说明使用@Scope("prototype")可以产生不同的Bean实例
System.out.println(customer1); //com.shoto.spring.demo2.Customer@7b02881e
System.out.println(customer2); //com.shoto.spring.demo2.Customer@1ebd319f
}

4. IoC的XML和注解开发比较

4.1 适用场景

  • XML:可以适用任何场景,结构清晰并维护方便。
  • 注解:注解在有些地方是无法使用的,比如我们在引用第三包或者其他外部的接口时,这是可以使用XML的方式。而在对于自己的工程中所开发的类使用注解则会更为方便。

4.2 混合使用

  • 推荐使用XML来管理Bean,而注解则完成属性注入。 也就是不再使用@Component等注解来声明创建Bean,而是以如下的形式直接写在XML文件中。

------ 本文结束------