【Java EE (Struts2 + Spring + Hibernate)开发】系列之 Spring(一)基本用法

13 篇文章 0 订阅

【Java EE (Struts2 + Spring + Hibernate)开发】系列之 Spring(一)基本用法
本文地址:http://blog.csdn.net/shanglianlm/article/details/49975521

1 Spring 简介

2 Spring 的核心机制:依赖注入

依赖注入主要有两种:
- 设值注入: IoC 使用属性的 setting 方法来注入被依赖的实例。
- 构造注入: IoC 使用构造器来注入被依赖的实例

2-1 设值注入

举例:

public interface Person
{
    //定义一个使用斧子的方法
    public void useAxe();
}
public interface Axe
{
    //Axe接口里有个砍的方法
    public String chop();
}

Person 的实现 调用axe的chop()方法

public class Chinese implements Person
{
    private Axe axe;
    public Chinese()
    {
    }
    //设值注入所需的setter方法
    public void setAxe(Axe axe)
    {
        this.axe = axe;
    }
    //实现Person接口的useAxe方法
    public void useAxe()
    {
        //调用axe的chop()方法,
        //表明Person对象依赖于axe对象
        System.out.println(axe.chop());
    }
}

Axe 的实现

public class StoneAxe implements Axe
{
    public StoneAxe()
    {
    }
    public String chop()
    {
        return "石斧砍柴好慢";
    }
}

配置文件:

<!-- Spring配置文件的根元素 -->
<beans>
    <!-- 配置chinese实例,其实现类是lee.Chinese -->
    <bean id="chinese" class="lee.Chinese">
        <!-- 将stoneAxe注入给axe属性 -->
        <property name="axe" ref="stoneAxe"/>
    </bean>
    <!-- 配置stoneAxe实例,其实现类是lee.StoneAxe -->
    <bean id="stoneAxe" class="lee.StoneAxe"/>
</beans>

测试

public class BeanTest
{
    public static void main(String[] args)throws Exception
    {
        //创建Spring容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        //获取chinese 实例
        Person p = (Person)ctx.getBean("chinese");
        //调用useAxe()方法
        p.useAxe();
    }
}

2-2 构造注入

public class Chinese implements Person
{
    private Axe axe;
    //默认的构造器
    public Chinese()
    {
    }
    //构造注入所需的带参数的构造器
    public Chinese(Axe axe)
    {
        this.axe = axe;
    }
    //实现Person接口的useAxe方法
    public void useAxe()
    {
        //调用axe的chop()方法,
        //表明Person对象依赖于axe对象
        System.out.println(axe.chop());
    }
}

配置文件

<!-- Spring配置文件的根元素 -->
<beans>
    <!-- 配置chinese实例,其实现类是lee.Chinese -->
    <bean id="chinese" class="lee.Chinese">
        <!-- 使用构造注入,为chinese实例注入stoneAxe实例 -->
        <constructor-arg ref="stoneAxe"/>
    </bean>
    <!-- 配置stoneAxe实例,其实现类是lee.StoneAxe -->
    <bean id="stoneAxe" class="lee.StoneAxe"/>
</beans>

2-3 两种注入方式的对比

这里写图片描述

建议选择设值注入为主,构造注入为主的策略。对依赖关系无需变化选择构造注入,其他选择设值注入。

3 使用 Sping 容器

3-1 Sping 容器

  • Sping 容器最基本的接口就是 BeanFactory。 BeanFactory 负责配置、创建、管理 Bean。
  • BeanFactory 有一个子接口:ApplicationContext 。ApplicationContext 增强了 BeanFactory 的功能。
  • Spring 容器还负责管理 Bean 和 Bean 之间的依赖关系。

BeanFactory 接口有如下几个基本方法:

这里写图片描述

这里写图片描述

3-2 使用 ApplicationContext

这里写图片描述

3-3 ApplicationContext 的国际化支持

这里写图片描述

例如:
message_en_US.properties

hello=welcome,{0}
now=now is :{1}

message.properties

hello=欢迎你,{0}
now=现在时间是:{1}

先使用native2ascii 工具将其国际化为 message_zh.properties 文件。

配置文件

<!-- Spring配置文件的根元素 -->
<beans>
    <bean id="messageSource"
    class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>message</value>
                <!-- 如果有多个资源文件,全部列在此处 -->
            </list>
        </property>
    </bean>
</beans> 

测试

public class SpringTest 
{
    public static void main(String[] args)throws Exception
    {
        //实例化ApplicationContext
        ApplicationContext ctx = new
            ClassPathXmlApplicationContext("bean.xml");
        //创建参数数组
        String[] a = {"读者"};
        //使用getMessage方法获取本地化消息。Locale的getDefault方法
        //返回计算机环境的默认Locale
        String hello = ctx.getMessage("hello",a,Locale.getDefault());
        Object[] b = {new Date()};
        String now = ctx.getMessage("hello",b,Locale.getDefault());
        //打印出两条本地化消息
        System.out.println(hello);
        System.out.println(now);
    }
}

3-4 ApplicationContext 事件机制

这里写图片描述
这里写图片描述

EmailEvent.java (继承 ApplicationEvent 类)

public class EmailEvent extends ApplicationEvent
{
    //为容器事件类定义无参数的构造器
    String address;
    String text;

    public EmailEvent(Object source)
    {
        super(source);
    }
    //定义一个有参数的构造器。
    public EmailEvent(Object source ,
        String address , String text)
    {
        super(source);
        this.address = address;
        this.text = text;
    }
}

EmailNotifier.java (实现 ApplicationListener 接口,且必须实现 onApplicationEvent(ApplicationEvent event) 方法)

public class EmailNotifier implements ApplicationListener
{
    //该方法会在容器发生事件时自动触发
    public void onApplicationEvent(ApplicationEvent evt)
    {
        if (evt instanceof EmailEvent)
        {
            //发送email通知...
            EmailEvent emailEvent = (EmailEvent)evt;
            System.out.println("需要发送邮件的接收地址  "
                + emailEvent.address);
            System.out.println("需要发送邮件的邮件正文  "
                + emailEvent.text);
        }
        else
        {
            //容器内置事件不作任何处理
            System.out.println("容器本身的事件:   " + evt);
        }
    }
}

配置文件 bean.xml

<!-- Spring配置文件的根元素 -->
<beans>
    <!-- 配置监听器 -->
    <bean id="emailListListener" class="lee.EmailNotifier"/>
</beans>

测试

public class SpringTest
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = new
            ClassPathXmlApplicationContext("bean.xml");
        //创建一个ApplicationEvent对象
        EmailEvent ele = new EmailEvent
            ("hello","spring_text@163.com","this is a test");
        //主动触发容器事件
        ctx.publishEvent(ele);
    }
}

3-5 让 Bean 获取 Spring 容器 (不推荐)

4 Sping 容器中的 Bean

4-1 Bean 的基本定义

这里写图片描述

< beans … />元素上面所指定的属性都可以在每个 < bean … />子元素上指定,只需要去掉 default 即可。

定义 Bean 时,需要指定两个属性:
- id :指定该 Bean 的唯一标识符。
- class:指定该 Bean 的具体实现类,不能是接口。

指定别名
- name:
- alias:指定一个别名。

<alias name="person" alias="jack" />
<alias name="jack" alias="jackee" />

默认情况下。当 Spring 创建 ApplicationContext 容器时,会自动初始化容器中的所有 singleton 实例,如果我们不喜欢 Spring 容器初始化某个 singleton Bean,则可以为该增加 lazy-init 属性。

<bean id="bean2" class="lee.Test2" lazy-init="true" />

4-2 容器中 Bean 的作用域

Bean 作用域支持如下5种:
- singleton:单例模式,在整个 Spring IoC 容器中,使用 singleton 定义的 Bean 将只有一个实例。
- prototype:原型模式,每次通过容器的 getBean 方法获取 prototype 定义的 Bean 时,都将产生一个新的 Bean 实例。
- request:(Web中有效)对于每次HTTP 请求,都将产生一个新的实例。
- session:(Web中有效)对于每次HTTP Session,都将产生一个新的实例。
- global session:(Web中有效)每个全局的 HTTP Session 对应一个 Bean 实例。

对于 request,session 和 global session,只有在Web 应用中才有效。
当使用 Servlet 2.4 以前规范的 Web 容器,只能在 web.xml 中使用 Filter 配置方式:

<web-app>
  <filter>
     <filter-name>requestContextFilter</filter-name>
     <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>requestContextFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

对于 Servlet 2.4 以后的规范,可以在 web.xml 中使用 Listener 配置:
对 request 作用域生效,

<listener>
    <listener-class> 
         org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>

4-3 配置依赖

通常情况下,Spring 在实例化容器时,会校验 BeanFactory 中每一个 Bean 的配置,这些校验包括:
- Bean 引用的依赖 Bean 是否指向一个合法的 Bean 。
- Bean 的普遍属性值是否获得了一个有效值。

4-4 设置普通属性值

Bean 实例的4种属性值:

  • value:主要用于传入字符串、基本类型的属性值。
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
    <property name="doubleProperty" value="2.3" />
</bean>
  • ref:属性值是容器中的另一个 Bean 实例(同一份XML配置文件用 local,不同文件用 bean)。
<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe" />
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
    <property name="axe" ref="steelAxe" />
</bean>

也可以使用 < beans … /> 元素的 default-autowire 属性自动装配注入合作者 Bean (不推荐)。
autowise 有 no, byName, byType, constructor 和 autodetect 等值。

  • bean:
  • list, set, map 及 props:

4-6 注入嵌套

<bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
    <property name="axe">
        <bean class="org.crazyit.app.service.impl.SteelAxe" />
    </property>
</bean>

嵌套 Bean 不能被 Spring 容器访问。

4-7 注入集合值

如果 Bean 的属性是个集合,则可以使用集合元素,< list … />, < set … / >, < map … /> 和 < prps … / > 分别来设置类型为 List, Set, Map 和 Properties 的集合属性值。

Chinese.java

public class Chinese implements Person
{
    //下面是系列集合属性,分别表示此人的学校,成绩,健康和斧子
    private List schools;
    private Map scores;
    private Properties health;
    private Set axes;
    private String[] books;

    public Chinese()
    {
        System.out.println("Spring实例化主调bean:Chinese实例...");
    }

    //schools属性依赖注入必须的setter方法
    public void setSchools(List schools)
    {
        this.schools = schools;
    }
    //scores属性依赖注入必须的setter方法
    public void setScores(Map scores)
    {
        this.scores = scores;
    }
    //health属性依赖注入必须的setter方法
    public void setHealth(Properties health)
    {
        this.health = health;
    }
    //axes属性依赖注入必须的setter方法
    public void setAxes(Set axes)
    {
        this.axes = axes;
    }
    //books属性依赖注入必须的setter方法
    public void setBooks(String[] books)
    {
        this.books = books;
    }

    //访问上面全部的集合属性
    public void test()
    {
        System.out.println(schools);
        System.out.println(scores);
        System.out.println(health);
        System.out.println(axes);
        System.out.println(java.util.Arrays.toString(books));
    }
}

bean.xml

<!-- Spring配置文件的根元素 -->
<beans>
    <!-- 定义一个普通Axe Bean -->
    <bean id="stoneAxe" class="lee.StoneAxe"/>
    <!-- 定义chinese Bean -->
    <bean id="chinese" class="lee.Chinese">
        <property name="schools">
            <!-- 为List属性配置属性值 -->
            <list>
                <!-- 每个value、ref、bean都配置一个List元素 -->
                <value>小学</value>
                <value>中学</value>
                <value>大学</value>
            </list>
        </property>
        <property name="scores">
            <!-- 为Map属性配置属性值 -->
            <map>
                <!-- 每个entry配置一个key-value对 -->
                <entry>
                    <!-- key元素配置Map key-->
                    <key>
                        <!-- 每个value、ref、bean都配置一个key值 -->
                        <value>数学</value>
                    </key>
                    <!-- 每个value、ref、bean都配置一个value元素 -->
                    <value>87</value>
                </entry>
                <entry>
                    <key>
                        <value>英语</value>
                    </key>
                    <value>89</value>
                </entry>
                <entry>
                    <key>
                        <value>语文</value>
                    </key>
                    <value>82</value>
                </entry>
            </map>
        </property>
        <property name="health">
            <!-- 为Properties属性配置属性值 -->
            <props>
                <!-- 每个prop元素配置一个属性项,
                    其中key指定属性名-->
                <prop key="血压">正常</prop>
                <prop key="身高">175</prop>
            </props>
        </property>
        <property name="axes">
            <!-- 为Set属性配置属性值 -->
            <set>
                <!-- 每个value、ref、bean都配置一个Set元素 -->
                <value>字符串斧子</value>
                <bean class="lee.SteelAxe"/>
                <ref local="stoneAxe"/>
            </set>
        </property>
        <property name="books">
            <!-- 为数组属性配置属性值 -->
            <list>
                <!-- 每个value、ref、bean都配置一个数组元素 -->
                <value>Struts2权威指南</value>
                <value>轻量级J2EE企业应用实战</value>
                <value>疯狂Java讲义</value>
            </list>
        </property>
    </bean>
</beans>

当使用 < list … />, < set … / >, < map … /> 和 < prps … / >等元素配置集合属性时,还需要手动配置集合元素。它们可以接手如下子元素:

  • value:主要用于传入字符串、基本类型的属性值。
  • ref:属性值是容器中的另一个 Bean 实例。
  • bean:
  • list, set, map 及 props:

当使用 < map … /> 元素配置 Map 属性时,配置文件每个 < entry … /> 配置一个 Map 元素。
< entry … />支持如下四个属性:

-key:Map key 是基本类型或者字符串
-key-ref:Map key是容器中另一个 Bean 实例,指定 Id。
-value:Map value 是基本类型或者字符串
-value-ref:Map value 是容器中另一个 Bean 实例,指定 Id。

集合合并
子集合和父集合合并,并覆盖父集合中重叠元素。

<beans>
  <bean id="parent" abstract="true" class="example.ComplexObject">
      <property name="adminEmails">
          <props>
            <prop key="administrator">administrator@crazyit.org</prop>
            <prop key="support">support@crazyit.org</prop>
          </props>
      </property>
  </bean>
  <bean id="child" parent="parent">
      <property name="adminEmails">
          <props merge="true">
            <prop key="sales">sales@crazyit.org</prop>
            <prop key="support">support@crazyit.org</prop>
          </props>
      </property>
  </bean>
</beans>

4-8 组合属性名称

使用组合属性名指定值。如: person.name (除最后一个属性外,其他属性都不能为 null)
ExampleBean.java

public class ExampleBean
{
    //定义一个Person型的属性
    private Person person
        = new Person(); 

    //person属性的getter方法
    public Person getPerson()
    {
        return this.person;
    }
}

bean.xml

<!-- Spring配置文件的根元素 -->
<beans>
    <bean id="exampleBean" class="lee.ExampleBean">
        <!-- 为复合属性指定值 -->
        <property name="person.name" value="孙悟空"/>
    </bean>
</beans>

4-9 Spring 的 Bean 和 JavaBean

Spring 的 Bean 和 JavaBean 的关系:
- 用处不同:传统 JavaBean 更多作为值对象传递参数;Spring 的 Bean 用处无所不包,任何应用组件都被成为 Bean 。
- 写法不同:传统 JavaBean 作为值对象,要求每个属性都提供 getter 和 setter 方法;但Spring 的 Bean 只需要为接受设置注入的属性提供 setter 方法。
- 生命周期不同:传统 JavaBean 作为值对象传递,不接受任何容器管理其生命周期;Spring 的 Bean 由Spring 管理其生命周期行为。

5 Sping 3.0 提供的 Java 配置管理

Sping 3.0 为不喜欢 XML 的人提供了一种选择,即可以使用 Java 类配置管理。

public class Chinese
    implements Person
{
    private Axe axe;
    private String name;
    //设值注入所需的setter方法
    public void setAxe(Axe axe)
    {
        this.axe = axe;
    }
    //设值注入所需的setter方法
    public void setName(String name)
    {
        this.name = name;
    }
    //实现Person接口的useAxe方法
    public void useAxe()
    {
        //调用axe的chop()方法,
        //表明Person对象依赖于axe对象
        System.out.println("我是:"    + name
            + axe.chop());
    }
}
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 配置chinese实例,其实现类是Chinese -->
    <bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
        <!-- 将stoneAxe注入给axe属性 -->
        <property name="axe" ref="stoneAxe"/>
        <property name="name" value="孙悟空"/>
    </bean>
    <!-- 配置stoneAxe实例,其实现类是StoneAxe -->
    <bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
    <!-- 配置steelAxe实例,其实现类是SteelAxe -->
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
</beans>
@Configuration
public class AppConfig 
{
    //定义需要依赖注入的属性值
    @Value("孙悟空") String personName; 
    //配置一个Bean:chinese
    @Bean(name="chinese") 
    public Person person() 
    { 
        Chinese p = new Chinese();
        p.setAxe(stoneAxe());
        p.setName(personName);
        return p;
    }
    //配置Bean:stoneAxe
    @Bean(name="stoneAxe")
    public Axe stoneAxe()
    { 
        return new StoneAxe();
    }
    //配置Bean:steelAxe
    @Bean(name="steelAxe")
    public Axe steelAxe()
    { 
        return new SteelAxe();
    }
} 

@Configuration:用于修饰一个 Java 配置类。
@Bean:用于修饰一个方法,将该方法的返回值定义成容器中的一个 Bean 。
@Value:用于修饰一个 Field, 用于为配置一个值。
@Import:修饰一个 Java 配置类,用于向当前 Java 配置类中导入其他 Java 配置类。
@Scope:用于修饰一个方法,指定该方法对应的 Bean 的生命域。
@Lazy:用于修饰一个方法,指定该方法对应的 Bean 的是否需要延迟初始化。
@DependOn:用于修饰一个方法,指定在初始化该方法对应的 Bean 之前初始化指定 Bean 。

5-1 XML 加载 Java 配置类

在 XML 配置文件中添加

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <context:annotation-config>
    <!-- j加载 Java 配置类 -->
    <bean class="org.crazyit.app.config.AppConfig"/>
</beans>

Spring 会先加载这份 XML 配置文件,再根据这份 XML 配置文件去加载指定的 Java 配置类。

5-2 Java 配置类 加载 XML

在 java 配置类文件中添加:

@Configuration
//导入 XML 配置
@ImportResource("classpath:/bean.xml")
public class MyConfig
{
 ...
}

Spring 会先加载 Java 配置类,再根据指示去加载指定的 XML 配置文件。

6 Bean 实例的创建方式及依赖配置

6-1 使用构造器创建 Bean 实例

Person.java

public interface Person
{
    //Person接口里定义一个使用斧子的方法
    public void useAxe();
}

Chinese.java

public class Chinese implements Person
{
    private Axe axe;
    //默认的构造器
    public Chinese()
    {
        System.out.println("Spring实例化主调Bean:Chinese实例...");
    }
    //设值注入所需的setter方法
    public void setAxe(Axe axe)
    {
        System.out.println("Spring执行依赖关系注入...");
        this.axe = axe;
    }
    //实现Person接口的useAxe方法
    public void useAxe()
    {
        System.out.println(axe.chop());
    }
}

Axe.java

public interface Axe
{
    //Axe接口里有个砍的方法
    public String chop();
}

SteelAxe.java

public class SteelAxe implements Axe
{
    //默认构造器
    public SteelAxe()
    {
        System.out.println("Spring实例化依赖Bean:SteelAxe实例...");
    }
    //实现Axe接口的chop方法
    public String chop()
    {
        return "钢斧砍柴真快";
    }
}

bean.xml

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 定义第一个Bean,id是chinese, class指定实现类 -->
    <bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
        <!--  property元素用来指定需要容器注入的属性,axe属性需要容器注入
            此处是设值注入,因此Chinese类必须拥有setAxe方法 -->
        <property name="axe" ref="steelAxe"/>
    </bean>
    <!-- 定义steelAxe bean -->
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
</beans>

BeanTest.java

public class BeanTest
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = new
            ClassPathXmlApplicationContext("bean.xml");
        Person p = ctx.getBean("chinese" , Person.class);
        p.useAxe();
    }
}

执行过程
这里写图片描述

6-2 使用静态工厂方法创建Bean

使用静态工厂方法时,class 属性指定的不是 Bean 实例的实现类,而是静态工厂类。
Being.java

public interface Being
{
    //接口定义testBeing方法
    public void testBeing();
}

Dog.java

public class Dog implements Being
{
    private String msg;
    //依赖注入时候必须的setter方法
    public void setMsg(String msg)
    {
        this.msg = msg;
    }
    //实现接口必须实现的testBeing方法
    public void testBeing()
    {
        System.out.println(msg + 
            "   狗爱啃骨头");
    }
}

Cat.java

public class Cat implements Being
{
    private String msg;
    //依赖注入时候必须的setter方法
    public void setMsg(String msg)
    {
        this.msg = msg;
    }
    //实现接口必须实现的testBeing方法
    public void testBeing()
    {
        System.out.println(msg + 
            "   猫喜欢吃老鼠");
    }
}

BeingFactory.java

public class BeingFactory
{
    /**
     * 获取Being实例的静态工厂方法
     * @param arg 决定返回哪个Being实例的参数
     */
    public static Being getBeing(String arg) 
    {
        //调用此静态方法的参数为dog,则返回Dog实例
        if (arg.equalsIgnoreCase("dog"))
        {
            return new Dog();
        }
        //否则返回Cat实例
        else
        {
            return new Cat();
        }
    }
}

bean.xml

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans ... >
    <!-- 配置BeingFactory的getBeing方法产生dog Bean -->
    <bean id="dog" class="org.crazyit.app.factory.BeingFactory"
        factory-method="getBeing">
        <!-- 配置静态工厂方法的参数 -->
        <constructor-arg value="dog"/>
        <!-- property配置普通依赖注入属性 -->
        <property name="msg" value="我是狗"/>
    </bean>
    <!-- 配置BeingFactory的getBeing方法产生dog Bean -->
    <bean id="cat" class="org.crazyit.app.factory.BeingFactory"
        factory-method="getBeing">
        <!-- 配置静态工厂方法的参数 -->
        <constructor-arg value="cat"/>
        <!-- property配置普通依赖注入属性 -->
        <property name="msg" value="我是猫"/>
    </bean>
</beans>

SpringTest.java

public class SpringTest 
{
    public static void main(String[] args)
    {
        //以类加载路径下的配置文件创建ClassPathResource实例
        ApplicationContext ctx = new 
            ClassPathXmlApplicationContext("bean.xml");
        Being b1 = ctx.getBean("dog" , Being.class);
        b1.testBeing();
        Being b2 = ctx.getBean("cat" , Being.class);
        b2.testBeing();
    }
}

这里写图片描述

6-3 调用实例工厂方法创建Bean

Person.java

public interface Person
{
    //定义一个打招呼的方法
    public String sayHello(String name);
    //定义一个告别的方法
    public String sayGoodBye(String name);
}

Chinese.java

public class Chinese implements Person
{
    //实现Person接口必须实现如下两个方法
    public String sayHello(String name)
    {
        return name + ",您好";
    }
    public String sayGoodBye(String name)
    {
        return name + ",下次再见";
    }
}

American.java

public class American implements Person
{
    //实现Person接口必须实现如下两个方法  
    public String sayHello(String name)
    {
        return name + ",Hello!";
    }
    public String sayGoodBye(String name)
    {
        return name + ",Good Bye!";
    }
}

PersonFactory.java

public class PersonFactory
{
    /**
     * 获得Person实例的实例工厂方法
     * @param ethnic 决定返回哪个Person实例的参数
     * @return 返回Person实例
     */
    public Person getPerson(String ethnic)
    {
        if (ethnic.equalsIgnoreCase("chin"))
        {
            return new Chinese();
        }
        else
        {
            return new American();
        }
    }
}

bean.xml

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 配置工厂Bean,该Bean负责产生其他Bean实例 -->
    <bean id="personFactory"
        class="org.crazyit.app.factory.PersonFactory"/>
    <!-- 采用实例工厂创建Bean实例,factory-bean指定工厂Bean的id属性
        factory-method属性指定工厂Bean的实例工厂方法 -->
    <bean id="chinese" factory-bean="personFactory" 
        factory-method="getPerson">
        <!-- 调用工厂方法时,传入的参数通过constructor-arg元素指定 -->
        <constructor-arg value="chin"/>
    </bean>
    <bean id="american" factory-bean="personFactory"
        factory-method="getPerson">
        <constructor-arg value="ame"/>
    </bean>
</beans>

这里写图片描述
这里写图片描述

7 深入理解容器中的 Bean

7-1 使用抽象 Bean

抽象 Bean,就是指定 abstract 属性为 true 。抽象 Bean 不能被实例化, Spring 容器不会创建抽象 Bean 的实例。抽象 Bean 的价值在于被继承。

<!-- 通过abstract属性定义该Bean 是抽象Bean -->
<bean id="chineseTemplate" class="org.crazyit.app.service.impl.Chinese"
        abstract="true">
    <property name="axe" ref="steelAxe"/>
</bean>

7-2 使用子 Bean

子 Bean 继承父 Bean (一般作为模板),但无法继承 depends-on, autowire, singleton, scope, lazy-init 等属性。

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 定义两个Axe实例 -->
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
    <bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
    <!-- 通过abstract属性定义该Bean 是抽象Bean -->
    <bean id="chineseTemplate" class="org.crazyit.app.service.impl.Chinese" abstract="true">
        <property name="axe" ref="steelAxe"/>
    </bean>
    <!-- 通过parent属性定义子bean -->
    <bean id="chinese" parent="chineseTemplate">
        <!-- 覆盖父Bean中依赖关系的配置 -->
        <property name="axe" ref="stoneAxe"/>
    </bean>
</beans>

7-3 Bean 继承与 Java 继承的区别

这里写图片描述

7-4 容器中的工厂 Bean

7-5 获得 Bean 本身的 id

可以通过借助 Spring 的 BeanNameAware 接口,通过该接口允许 Bean 类 获取部署该 Bean 时指定的 id 属性。该接口提供一个方法: setBeanName(String name), name 参数 就是 Bean 的 id。

public class Chinese 
    implements BeanNameAware
{
    //保存部署该Bean时指定的id属性
    private String beanName;
    public void setBeanName(String name) 
    {
        this.beanName = name;
    }
    public void info()
    {
        System.out.println("Chinese实现类"
            + ", 部署该Bean时指定的id为" + beanName);
    }
}

测试

public class SpringTest 
{
    public static void main(String[] args)
    {
        //创建Spring容器,容器会自动预初始化所有Bean实例
        ApplicationContext ctx = 
            new ClassPathXmlApplicationContext("bean.xml");
        Chinese chin = ctx.getBean("chinese" , Chinese.class);
        chin.info();
    }
}

7-6 强制初始化 Bean

Spring 默认先初始化主调 Bean ,然后再初始化依赖 Bean 。如果要指定 Bean 在目标 Bean 之前初始化,可以使用 depends-on 属性。

<bean id="beanOne" class="ExampleOne" depdends-on="manager">
      <property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />

8 容器中 Bean 的生命周期

这里写图片描述

8-1 依赖关系注入之后的行为

Spring 提供两种方式在 Bean 全部属性设置成功后执行特定行为:

  • 使用 init-method 属性。
  • 实现 InitializingBean 接口及其 afterPropertiesSet [void afterPropertiesSet() throws Exception ] 方法(不推荐)。
public class Chinese implements Person, InitializingBean
{
    private Axe axe;
    public Chinese()
    {
        System.out.println("Spring实例化主调bean:Chinese实例...");
    }
    //依赖注入必须的setter方法
    public void setAxe(Axe axe)
    {
        System.out.println("Spring执行依赖关系注入...");
        this.axe = axe;
    }

    public void useAxe()
    {
        System.out.println(axe.chop());
    }
    //测试用初始化方法
    public void init()
    {
        System.out.println("正在执行初始化方法   init...");
    }
    //实现InitializingBean接口必须实现的方法
    public void afterPropertiesSet() throws Exception
    {
        System.out.println("正在执行初始化方法  afterPropertiesSet...");
    }
}
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
    <!--  配置chinese Bean,使用init-method="init"
        指定该Bean所有属性设置完成后,自动执行init方法 -->
    <bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
        init-method="init">
        <property name="axe" ref="steelAxe"/>
    </bean>
</beans>

8-2 Bean 销毁之前的行为

Spring 提供两种方式定制 Bean 实例销毁之前的特定行为:

  • 使用 destroy-method 属性。
  • 实现 DisposableBean 接口及其方法 destroy [void destroy() throws Exception] 。
public class Chinese implements Person, DisposableBean
{
    private Axe axe;
    public Chinese()
    {
        System.out.println("Spring实例化主调bean:Chinese实例...");
    }
    public void setAxe(Axe axe)
    {
        System.out.println("Spring执行依赖关系注入...");
        this.axe = axe;
    }   
    public void useAxe()
    {
        System.out.println(axe.chop());
    }
    public void close()
    {
        System.out.println("正在执行销毁之前的方法   close...");
    }
    public void destroy() throws Exception
    {
        System.out.println("正在执行销毁之前的方法  destroy...");
    }
}
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
    <!--  配置chinese Bean,使用destroy-method="close"
        指定该Bean实例被销毁,Spring自动执行close方法 -->
    <bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
        destroy-method="close">
        <property name="axe" ref="steelAxe"/>
    </bean>
</beans>

8-3 协调作用域不同步的 Bean

当 singleton 作用域 Bean (只有一次初始化) 依赖 prototype 作用域 Bean (每次得到一个全新的 Bean 实例) 时, 将导致 singleton 作用域的 Bean 的依赖得不到即时更新。可以使用方法注入解决这个问题。

利用 lookup 方法注入可以让 Spring 容器重写容器中 Bean 的抽象或具体方法,返回查找容器中其他 Bean 的结果。

这里写图片描述

SteelAxe.java

public class SteelAxe implements Axe
{
    public SteelAxe()
    {
        System.out.println("Spring实例化依赖Bean:SteelAxe实例...");
    }
    //测试用方法
    public String chop()
    {
        return "钢斧砍柴真快 !";
    }
}

Chinese.java

public abstract class Chinese implements Person
{
    public Chinese()
    {
        System.out.println("Spring实例化主调bean:Chinese实例...");
    }

    //定义一个抽象方法,该方法将由Spring负责实现
    public abstract Axe getAxe();
    public void useAxe()
    {
        System.out.println("正在使用 " + getAxe()
            + "砍柴!");
        System.out.println(getAxe().chop());
    }
}

bean.xml

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 定义一个steelAxe实例,指定prototype的作用域 --> 
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe" 
        scope="prototype"/>
    <bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
        <!-- 指定getAxe方法返回steelAxe
            每次调用getAxe方法将获取新的steelAxe对象 -->
        <lookup-method name="getAxe" bean="steelAxe"/>
    </bean>
</beans>

9 深入理解依赖关系配置

这里写图片描述

9-1 注入其他 Bean 的属性值

PropertyPathFactoryBean 用来获取目标 Bean 的属性值(即 getter 方法的返回值),获得的值可注入给其他 Bean ,也可以直接定义成新的 Bean 。

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!--以下定义了将要被引用的目标bean-->
    <bean id="person" class="org.crazyit.app.service.Person">
        <!-- 为age属性指定值 -->
        <property name="age" value="30"/>
        <property name="son">
            <!-- 使用嵌套Bean定义属性值 -->
            <bean class="org.crazyit.app.service.Son">
                <property name="age" value="11" />
            </bean>
        </property>
    </bean>

    <!-- 将指定Bean实例的属性值定义成指定Bean实例 -->
    <bean id="son1"
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
        <!-- 确定目标Bean,表明son1 Bean来自哪个Bean的属性 -->
        <property name="targetBeanName" value="person"/>
        <!-- 确定属性表达式,表明son1 Bean来自目标bean的哪个属性 -->
        <property name="propertyPath" value="son"/>
    </bean>

    <!-- 如下定义son2的 Bean,该Bean的age属性不是直接注入
        ,而是依赖于其他Bean的属性值 -->
    <bean id="son2" class="org.crazyit.app.service.Son">
        <property name="age">
            <!-- 以下是访问Bean属性的简单方式,这样可以将person Bean的son属性的、
                age属性赋值给son2这个bean的age属性-->
            <bean id="person.son.age" class=
                "org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
        </property>
    </bean>

    <!-- 将基本数据类型的属性值定义成Bean实例 -->
    <bean id="theAge" class=
        "org.springframework.beans.factory.config.PropertyPathFactoryBean">
        <!-- 确定目标Bean,表明theAge Bean来自哪个Bean的属性 -->
        <property name="targetBeanName" value="person"/>
        <!-- 确定属性名,表明theAge Bean来自目标Bean的哪个属性,
            此处的属性采用复合属性的形式 -->
        <property name="propertyPath" value="son.age"/>
    </bean>

    <!-- 将基本数据类型的属性值定义成Bean实例 -->
    <bean id="theAge2" class=
        "org.springframework.beans.factory.config.PropertyPathFactoryBean">
        <!-- 确定目标Bean,表明theAge2 Bean来自哪个Bean的属性。
            此处采用嵌套Bean定义目标Bean -->
        <property name="targetObject">
            <!-- 目标Bean不是容器中已经存在的Bean, 而是如下的嵌套Bean-->
            <bean class="org.crazyit.app.service.Person">
                <property name="age" value="30"/>
            </bean>
        </property>
        <!-- 确定属性表达式,表明theAge2 Bean来自目标bean的哪个属性 -->
        <property name="propertyPath" value="age"/>
    </bean>
</beans>

SpringTest.java

public class SpringTest
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        System.out.println("系统获取theAge的值:" + ctx.getBean("theAge"));
        System.out.println("系统获取theAge2的值:" + ctx.getBean("theAge2"));
        System.out.println("系统获取的son1:" + ctx.getBean("son1"));
        System.out.println("系统获取son2:" + ctx.getBean("son2"));
    }
}

9-2 注入其他 Bean 的 Field 值

FieldRetrievingFactoryBean 获取目标 Bean 的 Field 值之后,得到的值可注入给其他 Bean, 也可以直接定义成新的 Bean 。

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 将java.sql.Connection的TRANSACTION_SERIALIZABLE
        的值赋给son的age属性-->
    <bean id="son" class="org.crazyit.app.service.Son">
        <property name="age">
            <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE" 
                class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
        </property>
    </bean>
    <!-- 将Field 值定义成Bean 实例-->
    <bean id="theAge1" class=
        "org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
        <!-- targetClass指定Field所在的目标类 -->
        <property name="targetClass" value="java.sql.Connection"/>
        <!-- targetField指定Field名 -->
        <property name="targetField" value="TRANSACTION_SERIALIZABLE"/>
    </bean>

<!-- 将Field 值定义成Bean实例 -->
<bean id="theAge2" class=
    "org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
    <!-- value指定采用哪个类的哪个静态域值 -->
    <property name="staticField" 
        value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</bean>
</beans>
public class SpringTest
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        //获取son Bean实例
        Son son = ctx.getBean("son" , Son.class);
        //输出son的age值
        System.out.println("系统获取son的age属性值:" + son.getAge());
        System.out.println("系统获取theAge1的值:" + ctx.getBean("theAge1"));
        System.out.println("系统获取theAge2的值:" + ctx.getBean("theAge2"));
    }
}

9-3 注入其他 Bean 的方法返回值

MethodInvokingFactoryBean 可以将指定方法的返回值注入成目标 Bean 的属性值。该方法既可以是静态方法,也可以是实例方法。

<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 定义目标Bean,后面将会获取该Bean的方法返回值 -->
    <bean id="valueGnerator" class="org.crazyit.app.util.ValueGenerator"/>
    <!-- 定义名为son1的Bean -->
    <bean id="son1" class="org.crazyit.app.service.Son">
        <property name="age">
            <!-- 获取方法返回值 -->
            <bean class=
                "org.springframework.beans.factory.config.MethodInvokingFactoryBean">
                <!-- targetObject确定目标Bean,指定调用哪个Bean -->
                <property name="targetObject" ref="valueGnerator"/>
                <!-- targetMethod确定目标方法,指定调用目标Bean的哪个方法 -->
                <property name="targetMethod" value="getValue"/>
            </bean>
        </property>
    </bean>
    <!-- 定义名为son2的Bean -->
    <bean id="son2" class="org.crazyit.app.service.Son">
        <property name="age">
            <bean class=
                "org.springframework.beans.factory.config.MethodInvokingFactoryBean">
                <!-- targetClass确定目标类,指定调用哪个类 -->
                <property name="targetClass" value="org.crazyit.app.util.ValueGenerator"/>
                <!-- targetMethod确定目标方法,指定调用目标class的哪个方法。 
                    该方法必须是静态方法-->
                <property name="targetMethod" value="getStaticValue"/>
            </bean>
        </property>
    </bean>
    <!--  将静态方法返回值直接定义成Bean -->
    <bean id="sysProps" class=
        "org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <!-- targetClass确定目标类,确定调用哪个类 -->
        <property name="targetClass" value="java.lang.System"/>
        <!-- targetMethod确定目标方法,确定调用目标class的哪个方法 
            该方法必须是静态方法-->
        <property name="targetMethod" value="getProperties"/>
    </bean>
<!-- 将实例方法返回值直接定义成Bean -->
<bean id="javaVersion" class=
    "org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <!-- targetObject确定目标Bean,确定调用哪个Bean -->
    <property name="targetObject" ref="sysProps"/>
    <!-- targetMethod确定目标方法,确定调用目标Bean的哪个方法 -->
    <property name="targetMethod" value="getProperty"/>
    <!-- 确定调用目标方法的参数 -->
    <property name="arguments">
        <!-- list元素列出调用方法多个参数值 -->
        <list>
            <value>java.version</value>
        </list>
    </property>
</bean>
</beans>
public class SpringTest
{
    public static void main(String[] args)
    {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        //获取son1 Bean实例
        Son son1 = ctx.getBean("son1" , Son.class);
        //输出son1的age值
        System.out.println("系统获取son1的age属性值:" + son1.getAge());
        //获取son2 Bean实例
        Son son2 = ctx.getBean("son2" , Son.class);
        //输出son2的age值
        System.out.println("系统获取son2的age属性值:" + son2.getAge());
        System.out.println("系统获取Java版本:" + ctx.getBean("javaVersion"));
    }
}

10 基于 XML Schema 的简化配置方式

从 Spring 2.0 开始, Spring 允许使用基于 XML Schema 的配置方式来简化 Spring 配置文件。

10-1 使用 p 名称空间配置属性

p 名称直接存在于 Spring 内核中。
Chinese.java

public class Chinese implements Person
{
    private Axe axe;
    private int age;
    public Chinese()
    {
    }
    //设值注入axe属性所需的setter方法
    public void setAxe(Axe axe)
    {
        this.axe = axe;
    }
    //设值注入age属性所需的setter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    //实现Person接口的useAxe方法
    public void useAxe()
    {
        //调用axe的chop()方法,
        //表明Person对象依赖于axe对象
        System.out.println(axe.chop());
        System.out.println("age属性值:" + age);
    }
}

bean.xml

<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的根元素和Schema
    并导入p命名空间的元素 -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 配置chinese实例,其实现类是Chinese -->
    <bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
        p:age="29" p:axe-ref="stoneAxe"/>
    <!-- 配置steelAxe实例,其实现类是SteelAxe -->
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
    <!-- 配置stoneAxee实例,其实现类是StoneAxe -->
    <bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
</beans>

10-2 使用 util Schema

为了使用 util Schema,必须先在 Spring 配置文件中导入 spring-util-3.0.xsd,

<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的根元素和Schema
    导入p命名空间和util命名空间的元素 -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">
    ...
</beans>

实例
Chinese.java

public class Chinese implements Person
{
    private Axe axe;
    private int age;
    private List schools;
    private Map scores;
    private Set axes;

    ... ...
}

bean.xml

<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的根元素和Schema
    导入p命名空间和util命名空间的元素 -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">
    <!-- 配置chinese实例,其实现类是Chinese -->
    <bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
        p:age-ref="chin.age" p:axe-ref="stoneAxe"
        p:schools-ref="chin.schools"
        p:axes-ref="chin.axes"
        p:scores-ref="chin.scores"/>

    <!-- 使用util:constant将指定类的静态Field暴露出来 -->
    <util:constant id="chin.age" static-field=
        "java.sql.Connection.TRANSACTION_SERIALIZABLE"/>

    <!-- 使用util:property-path将指定Bean的指定属性暴露出来 -->   
    <util:property-path id="test" path="chinese.age"/>

    <!-- 使用util.properties加载指定资源文件 -->
    <util:properties id="confTest"
        location="classpath:test_zh_CN.properties"/>

    <!-- 使用util:list定义一个List对象 -->
    <util:list id="chin.schools" list-class="java.util.LinkedList">
        <!-- 每个value、ref、bean都配置一个List元素 -->
        <value>小学</value>
        <value>中学</value>
        <value>大学</value>
    </util:list>
    <!-- 使用util:set定义一个Set对象 -->
    <util:set id="chin.axes" set-class="java.util.HashSet">
        <!-- 每个value、ref、bean都配置一个Set元素 -->
        <value>字符串斧子</value>
        <bean class="org.crazyit.app.service.impl.SteelAxe"/>
        <ref local="stoneAxe"/>
    </util:set>
    <!-- 使用util:map定义一个Map对象 -->
    <util:map id="chin.scores" map-class="java.util.TreeMap">
        <entry key="数学" value="87"/>
        <entry key="英语" value="89"/>
        <entry key="语文" value="82"/>
    </util:map>

    <!-- 配置steelAxe实例,其实现类是SteelAxe -->
    <bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
    <!-- 配置stoneAxee实例,其实现类是StoneAxe -->
    <bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
</beans>

11 Spring 3.0 提供的表达式语言(SpEL)

SpEL 可以独立于 Spring 容器使用, 只是被当成简单的表达式语言来使用;也可以在 Annotation 或 XML 配置中使用 SpEL ,这样可以充分利用 SpEL 简化 Spring 的 Bean 配置。

11-1 使用 Expression 接口进行表达式求值

SpEL 主要提供如下两个接口:

  • ExpressionParser : 该接口的实例负责解析一个 SpEL 表达式,返回一个 Expression 对象。
  • Expression:该接口的一个实例代表一个表达式。

这里写图片描述

public class SpELTest
{
    public static void main(String[] args)
    {
        //创建一个ExpressionParser对象,用于解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //最简单的字符串表达式
        Expression exp = parser.parseExpression("'HelloWorld'");
        System.out.println("'HelloWorld'的结果: " + exp.getValue());
        //调用方法的表达式
        exp = parser.parseExpression("'HelloWorld'.concat('!')");
        System.out.println("'HelloWorld'.concat('!')的结果: "
            + exp.getValue());
        //调用对象的getter方法
        exp = parser.parseExpression("'HelloWorld'.bytes");
        System.out.println("'HelloWorld'.bytes的结果: "
            + exp.getValue());
        //访问对象的属性(d相当于HelloWorld.getBytes().length)
        exp = parser.parseExpression("'HelloWorld'.bytes.length");
        System.out.println("'HelloWorld'.bytes.length的结果:"
            + exp.getValue());
        //使用构造器来创建对象
        exp = parser.parseExpression("new String('helloworld')"
            + ".toUpperCase()");
        System.out.println("new String('helloworld')"
            + ".toUpperCase()的结果是: "
            + exp.getValue(String.class));

        Person person = new Person(1 , "孙悟空", new Date());
        exp = parser.parseExpression("name");
        //以指定对象作为root来计算表达式的值
        //相当于调用person.name表达式的值
        System.out.println("以persn为root,name表达式的值是: "
            + exp.getValue(person , String.class));     
        exp = parser.parseExpression("name=='孙悟空'");
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        ctx.setRootObject(person);
        //以指定Context来计算表达式的值
        System.out.println(exp.getValue(ctx , Boolean.class));

        List<Boolean> list = new ArrayList<Boolean>();
        list.add(true);
        EvaluationContext ctx2 = new StandardEvaluationContext();
        //将list设置成EvaluationContext的一个变量
        ctx2.setVariable("list" , list);
        //修饰list变量的第一个元素的值
        parser.parseExpression("#list[0]").setValue(ctx2 , "false");
        //list集合的第一个元素被改变
        System.out.println("list集合的第一个元素为: "
            + list.get(0));
    }
}

这里写图片描述

11-2 Bean 定义中的表达式语言支持

SpEL 的一个重要作用就是扩展 Spring 的功能,允许在 Bean 定义中使用 SpEL 。XML 配置文件和 Annotation 中都可以使用 SpEL ,但需要在表达式外面增加 #{} 包围。

public class Author
    implements Person
{
    private Integer id;
    private String name;
    private List<String> books;
    private Axe axe;

    public void setAxe(Axe axe)
    {
        this.axe = axe;
    }

    //id属性的setter和getter方法
    public void setId(Integer id)
    {
        this.id = id;
    }
    public Integer getId()
    {
        return this.id;
    }

    //name属性的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    //books属性的setter和getter方法
    public void setBooks(List<String> books)
    {
        this.books = books;
    }
    public List<String> getBooks()
    {
        return this.books;
    }

    public void useAxe()
    {
        System.out.println("我是"
            + name 
            + ",正在砍柴\n"
            + axe.chop());
    }
}
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的根元素和Schema
    导入p命名空间和util命名空间的元素 -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">
    <!-- 使用util.properties加载指定资源文件 -->
    <util:properties id="confTest"
        location="classpath:test_zh_CN.properties"/>
    <!--
    配置name属性值时,在表达式中调用方法
    配置axe属性值时,在表达式中创建对象 -->
    <bean id="author" class="org.crazyit.app.service.impl.Author"
        p:name="#{T(java.lang.Math).random()}"
        p:axe="#{new org.crazyit.app.service.impl.SteelAxe()}">
        <property name="books">
            <list>
                <!-- 在表达式中访问其他Bean的属性 -->
                <value>#{confTest.a}</value>
                <value>#{confTest.b}</value>
            </list>
        </property>
    </bean>
</beans>

11-3 SpEL 语法详述

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值