专栏首页零基础使用Django2.0.1打造在线教育网站Spring学习(2):Spring Bean管理(上)
原创

Spring学习(2):Spring Bean管理(上)

#Spring工厂类介绍

在前面使用了Spring的ApplicationContext,并通过它的getBean方法获取Spring的配置文件applicationContext.xml,然后得到Bean的class对象。

从图中可以知道我们使用的是ApplicationContext接口,而使用的具体实现类是ClassPathXmlApplicationContext用来加载类路径下面的配置文件;注意到左边还有一个FileSystemXmlApplicationContext实现类,用于加载文件系统中的文件,言外之意就是当你的配置文件不在当前的项目工程内,此时就可以使用FileSystemXmlApplicationContext实现类去获取配置文件,接着去获取Bean对象。在老旧的Spring中使用的是BeanFactory接口,正如你所看到的ApplicationContext接口是它的子类自然功能就相对来说较为丰富。再有一点就是两者在生成Bean实例的时间是不同的。对于BeanFactory接口来说是工厂实例化结束后,在调用getBean方法的时才会去创建该类的实例;而ApplicationContext接口则是一开始加载配置文件的时候,就会将配置文件中所有通过单例模式创建的对象都给实例化,一般都会使用后者:

   /**
     * Spring开发模式:读取磁盘系统中的配置文件
     **/
    @Test
    public void testThree(){
        ApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\Maven\\applicationContext.xml");
        UserServiceImpl userServiceimpl = (UserServiceImpl) applicationContext.getBean("userService");
        //调用对象的方法
        System.out.println(userServiceimpl.getName());
        userServiceimpl.sayHello();
    }

使用XML来管理Bean

在Spring中管理Bean有两种方式:XML方式和注解方式,先介绍XML方式。在Spring中实例化Bean的方式有三种:1、使用类构造器实例化(默认无参数);2、使用静态工厂方法实例化(简单工厂模式);3、使用实例工厂方法实例化(工厂方法模式)

使用XML来管理Bean----使用类构造器实例化(默认无参数)

在java包中新建一个com.envy.demo的文件夹,接着新建一个Bean1.java文件:

public class Bean1 {
    /**
     * 使用无参数的类构造器实例化Bean
     */
    public Bean1(){
        System.out.println("使用无参数的类构造器实例化Bean");
    }
}

接着在applicationContext.xml文件中新增一个Bean:

    <!-- Bean实例化的三种方式 -->
    <!-- 第一种,使用无参数的类构造器实例化Bean -->
    <bean id="bean1" class="com.envy.demo.Bean1"></bean>

最后新建一个SpringBeanTest.java的测试文件:

public class SpringBeanTest {
    /**
     *
     * Bean实例化的三种方式
     **/
    @Test
    public void testOne(){
        /**
         * 使用无参数的构造函数实例化Bean
         * **/

        //获得Spring工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean1 bean1 = (Bean1)applicationContext.getBean("bean1");
    }

运行testOne方法,发现控制台输出:

使用无参数的类构造器实例化Bean

使用XML来管理Bean----使用静态工厂方法实例化(简单工厂模式)

在java包中新建一个com.envy.demo的文件夹,接着新建一个Bean2.java文件:

public class Bean2{
   
}

接着在该包中新建Bean2Factory.java文件,用于作为静态工厂实例化Bean2:

public class Bean2Factory {
    public static Bean2 createBean2(){
        System.out.println("使用静态工厂实例化Bean");
        return new Bean2();
    }
}

然后在applicationContext.xml文件中新增一个Bean:

    <!-- Bean实例化的三种方式 -->
    <!-- 第二种,使用静态工厂实例化Bean -->
    <bean id="bean2" class="com.envy.demo.Bean2Factory" factory-method="createBean2"></bean>

最后在SpringBeanTest.java测试文件中添加testTwo方法:

@Test
    public void testTwo(){
        /**
         * 使用静态工厂实例化Bean
         * **/
        //获得Spring工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean2 bean2 = (Bean2)applicationContext.getBean("bean2");
    }

运行testTwo方法,发现控制台输出:

使用无参数的类构造器实例化Bean
使用静态工厂实例化Bean

你会发现之前的testOne方法也被执行了,这个是正常现象,因为该工厂ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");只要加载配置文件就会将该配置文件中所有的类都给实例化。

如果你不想看到testOne方法被执行输出,可以将applicationContext.xml文件中的<bean id="bean1" class="com.envy.demo.Bean1"></bean>给注释掉即可。

使用XML来管理Bean----使用实例工厂方法实例化(工厂方法模式)

在java包中新建一个com.envy.demo的文件夹,接着新建一个Bean3.java文件:

public class Bean3{
   
}

接着在该包中新建Bean3Factory.java文件,用于作为实例工厂实例化Bean3:

public class Bean3Factory {
    /**
     * Bean3的实例工厂
     **/
    public Bean3 createBean3(){
        System.out.println("使用实例工厂实例化Bean");
        return new Bean3();
    }
}

然后在applicationContext.xml文件中新增一个Bean:

    <!-- Bean实例化的三种方式 -->
   <!-- 第三种,使用实例工厂实例化Bean -->
    <bean id="bean3Factory" class="com.envy.demo.Bean3Factory"></bean>
    <bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"></bean>

实例方法和静态方法的区别是前者的调用必须依赖于对象,而后者可以通过类名来调用。因此此处的bean3必须要依靠bean3Factory对象,所以先获取bean3Factory对象,接着把bean3Factory对象作为factory-bean来生成bean3对象。

最后在SpringBeanTest.java测试文件中添加testThree方法:

    @Test
    public void testThree(){
        /**
         * 使用实例工厂实例化Bean
         * **/
        //获得Spring工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean3 bean3 = (Bean3) applicationContext.getBean("bean3");
    }

运行testThree方法,发现控制台输出:

使用实例工厂实例化Bean

通常情况下我们会使用第一种,也就是默认的无参数的构造函数方式来实例化Bean,除非你的类的构造非常复杂才会使用后面三种。

Bean的配置

一般而言在Bean在使用最多的就是id和name,通常装配一个Bean时指定一个id属性作为Bean的名称,注意id属性在IOC容器中必须是唯一的;name和id属性的区别不大,当id中出现一些特殊字符时,则必须使用name属性,Id属性中不能包含特殊字符。

class属性用于设置一个类的完全路径名称,主要作用是IOC容器生成类的实例。

Bean的作用域

Bean的作用域是通过scope属性来设置的,其中scope的值有以下几种:

前面两个较为常用,后面两个不常用,一般用于web项目。scope属性默认值是singleton,也就是单例模式。你可以测试一下,先创建一个Bean4.java文件:

public class Bean4 {
}

然后在applicationContext.xml文件中新增一个Bean:

    <!-- 测试Bean的作用域 -->
    <bean id="bean4" class="com.envy.demo.Bean4"></bean>

最后在SpringBeanTest.java测试文件中添加testFour方法:

  @Test
    public void testFour(){
        /**
         * 测试Bean的作用域
         * **/
        //获得Spring工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean4 bean41 = (Bean4) applicationContext.getBean("bean4");
        Bean4 bean42 = (Bean4) applicationContext.getBean("bean4");

        System.out.println(bean41);
        System.out.println(bean42);
    }

运行testFour方法,发现控制台输出:

com.envy.demo.Bean4@51c8530f
com.envy.demo.Bean4@51c8530f

说明这两个对象是同一个对象,也就是scope属性默认值为singleton。

接着添加scope="prototype"配置,然后再次运行testFour方法,发现控制台输出:

com.envy.demo.Bean4@51c8530f
com.envy.demo.Bean4@7403c468

说明这是两个不同的对象,每次调用getBean方法都会生成一个新的实例。

Bean的生命周期配置

Spring初始化bean或销毁bean时,有时需要进行一些处理工作,因此spring可以在创建和销毁bean的时候调用Bean的两个生命周期方法:init-methoddestory-method

<bean id="test" class="com.envy.demo.Test" init-method="init" destroy-method="destory"></bean>

当bean被加载到容器的时候调用init方法;当bean从容器中删除的时候调用destory方法(注意destory方法必须是当scope=singleton时才有效,因为当你不是单例的时候,destory方法不知道你具体需要删除哪个实例)。前面说过scope默认是singleton,因此可以不写scope即可。

你可以测试一下,先创建一个Bean5.java文件:

public class Bean5 {
    public Bean5(){
        System.out.println("Bean5被实例化了");
    }

    public void initBean5(){
        System.out.println("Bean5被初始化了");
    }

    public void destroyBean5(){
        System.out.println("Bean5被销毁了");
    }
}

然后在applicationContext.xml文件中新增一个Bean:

    <!-- 测试Bean的生命周期 -->
    <bean id="bean5" class="com.envy.demo.Bean5" init-method="initBean5" destroy-method="destroyBean5"></bean>

最后在SpringBeanTest.java测试文件中添加testFive方法:

   @Test
    public void testFive(){
        /**
         * 测试Bean的生命周期
         * **/
        //获得Spring工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean5 bean5 = (Bean5) applicationContext.getBean("bean5");
        System.out.println(bean5);
        ((ClassPathXmlApplicationContext) applicationContext).close();
    }

运行testFive方法,发现控制台输出:

Bean5被实例化了
Bean5被初始化了
com.envy.demo.Bean5@51c8530f
Bean5被销毁了

Bean的生命周期完整过程

接下来通过之前的Bean5来介绍Bean的整个完整的生命周期过程。

第一步:Bean对象实例化。前面代码已经完成。

第二步:封装Bean对象属性。打开Bean5.java文件,修改代码为:

public class Bean5 {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("第二步:封装name属性");
        this.name = name;
    }

    public Bean5(){
        System.out.println("第一步:Bean5被实例化了");
    }

    public void initBean5(){
        System.out.println("Bean5被初始化了");
    }

    public void destroyBean5(){
        System.out.println("Bean5被销毁了");
    }
}

既然封装了某个属性,那么需要去applicationContext.xml文件中新增property:

 <bean id="bean5" class="com.envy.demo.Bean5" init-method="initBean5" destroy-method="destroyBean5">
        <property name="name" value="这是测试完整的Bean生命周期"></property>
    </bean>

第三步:如果Bean实现BeanNameAware,那么必须重写setBeanName方法

第四步:如果Bean实现BeanFactoryAware,则需重写setBeanFactory方法(或者实现ApplicationContextAware,并重写上下文对象setApplicationContext方法,两者二选一)

可以看出其实第三步和第四步是让我们这个Bean来了解自己在Spring容器中的信息,如名字、工厂等信息。

其实第三步就是获取Bean在Spring容器中配置信息,即name或者id值。在Bean5.java文件中让Bean5实现BeanNameAware接口,并新增以下代码:

   public void setBeanName(String name) {
        //这里的name其实就是applicationContext.xml中Bean的id的值
        System.out.println("第三步:设置Bean的名称"+name);
    }

第四步是让Bean去了解工厂的信息。在Bean5.java文件中让Bean5实现ApplicationContextAware接口,并新增以下代码:

  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第四步:了解工厂信息");
    }

这其中的第五步和第八步是较为核心的步骤。第五步,如果存在类实现BeanPostProcessor(后处理Bean),则会执行其中的postProcessBeforeInitialization方法。

先创建一个MyBean5PostProcessor.java文件,去实现BeanPostProcessor接口,并重写其中的两个方法(一般该两个方法均有返回值):

public class MyBean5PostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException {
        System.out.println("第五步:这是初始化前的方法...");
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String s) throws BeansException {
        System.out.println("第八步:这是初始化后的方法...");
        return bean;
    }
}

既然新建了一个类,就得去applicationContext.xml文件中新增一个Bean:

<bean class="com.envy.demo.MyBean5PostProcessor"></bean>

请注意该Bean没有id属性,因为通常设置id属性是可以让你在工厂或者其他地方可以使用到该类,而这个MyBean5PostProcessor类Spring则会在生成类的实例过程中会自动去调用,并不需要其他地方去引用,因此无需配置id。

第六步,如果Bean实现InitializingBean,那么必须重写afterPropertiesSet方法。让Bean5类去实现InitializingBean接口,并重写afterPropertiesSet方法:

  public void afterPropertiesSet() throws Exception {
        System.out.println("第六步:属性设置完后就会调用");
    }

第七步,执行applicationContext.xml文件中<bean id="bean5" init-method="initBean5">中的指定的init-method方法initBean5。修改Bean5.java文件中的initBean5方法代码:

    public void initBean5(){
        System.out.println("第七步:Bean5被初始化了");
    }

第八步,如果存在类实现BeanPostProcessor(后处理Bean),则会执行其中的postProcessAfterInitialization方法。

第九步,执行Bean5.java文件中自身的逻辑方法,如定义一个run方法:

  public void run(){
        System.out.println("第九步:执行自身业务逻辑方法");
    }

第十步,如果Bean实现DisposableBean接口,那么必须重写destroy方法。让Bean5类去实现DisposableBean接口,并重写destroy方法:

 public void destroy() throws Exception {
        System.out.println("第十步:执行Spring中的destroy方法");
    }

第十一步,执行applicationContext.xml文件中<bean id="bean5" destroy-method="destroyBean5>中的指定的destroy-method方法destroyBean5。修改Bean5.java文件中的destroyBean5方法代码:

    public void destroyBean5(){
        System.out.println("第十一步:Bean5被销毁了");
    }

最后在SpringBeanTest.java测试文件中添加testSix方法:

@Test
    public void testSix(){
        /**
         * 测试Bean的完整生命周期
         * **/
        //获得Spring工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean5 bean5 = (Bean5) applicationContext.getBean("bean5");
        bean5.run();
        ((ClassPathXmlApplicationContext) applicationContext).close();
    }

运行testSix方法,发现控制台输出:

第一步:Bean5被实例化了
第二步:封装name属性
第三步:设置Bean的名称bean5
第四步:了解工厂信息
第五步:这是初始化前的方法...
第六步:属性设置完后就会调用
第七步:Bean5被初始化了
第八步:这是初始化后的方法...
第九步:执行自身业务逻辑方法
第十步:执行Spring中的destroy方法
第十一步:Bean5被销毁了

前面也说过这里面最重要的是第五步和第八步(后处理Bean),它是在我们整个类的生成过程中需要执行的一个步骤,它可以在不修改Bean中的代码实现对类功能的增强。

BeanPostProcessor的作用

前面说过BeanPostProcessor(后处理类)可以在生成类的过程中对类进行代理,且可以对其中的方法进行增强。接下来通过面向接口的方式来演示如何增强一个类中的方法。

新建一个UserDao.java的接口文件:

public interface UserDao {
    public void findAll();

    public void save();

    public void update();

    public void delete();
}

接着新建一个UserDaoImpl.java文件,去实现它并重写其中的方法:

public class UserDaoImpl implements UserDao {
    public void findAll() {
        System.out.println("查询用户");
    }

    public void save() {
        System.out.println("保存用户");
    }

    public void update() {
        System.out.println("修改用户");
    }

    public void delete() {
        System.out.println("删除用户");
    }
}

既然新建了一个类,就得去applicationContext.xml文件中新增一个Bean:

<bean id="userDao" class="com.envy.demo.UserDaoImpl"></bean>

接着注释掉其中一些没有的代码,只保留以下代码:

<bean class="com.envy.demo.MyBean5PostProcessor"></bean>
<!-- 测试BeanPostProcessor的作用 -->
<bean id="userDao" class="com.envy.demo.UserDaoImpl"></bean>

最后在SpringBeanTest.java测试文件中添加testSeven方法:

    @Test
    public void testSeven(){
        /**
         * 测试BeanPostProcessor的作用
         * **/
        //获得Spring工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        userDao.findAll();
        userDao.save();
        userDao.update();
        userDao.delete();
    }

运行testSeven方法,发现控制台输出:

第五步:这是初始化前的方法...
第八步:这是初始化后的方法...
查询用户
保存用户
修改用户
删除用户

确实调用了MyBean5PostProcessor文件中的方法并进行输出。MyBean5PostProcessor中的两个方法目前只是直接返回了Bean实例,其实是可以返回一个代理对象的,因此可以先对Bean进行处理,最后返回一个代理对象即可。或者说需要对UserDaoImpl类中的save方法进行功能上增强,如增加权限控制。实现这个过程只需要在MyBean5PostProcessor类中修改postProcessAfterInitialization方法即可:

public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        System.out.println("第八步:这是初始化后的方法...");
        if ("userDao".equals(beanName)) {
            Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if ("save".equals(method.getName())) {
                        System.out.println("执行对应的权限操作验证");
                        return method.invoke(bean, args);
                    }
                    return method.invoke(bean, args);
                }
            });
            return proxy;
        }
        return bean;
    }
}

最后在SpringBeanTest.java测试文件中运行testSeven方法,发现控制台输出:

第五步:这是初始化前的方法...
第八步:这是初始化后的方法...
查询用户
执行对应的权限操作验证
保存用户
修改用户
删除用户

看到没有我们并没有在实现类中进行任何操作,仅仅在postProcessAfterInitialization方法中进行了处理就实现了相应的功能。

DI依赖注入

spring在创建类的过程中会将类依赖的属性注入进来,接下是spring类的属性注入方式。对于类成员变量而言,注入方式有三种:1、构造函数注入;2、属性setter方法注入;3、接口注入(不常用)。但是Spring只支持前两种也就是构造函数注入和属性setter方法注入。

构造函数注入

通过构造方法注入Bean的属性值或依赖的对象,它保证了Bean实例在实例化后就可以使用。构造器注入在<constructor-arg>元素里声明的属性。

第一步:新建Bean6.java文件,里面的代码如下:

public class Bean6 {
    private String name;
    private Integer age;

    public Bean6(String name,Integer age){
        this.name =name;
        this.age=age;
    }

    @Override
    public String toString() {
        return "Bean6{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

第二步:往applicationContext.xml文件中新增Bean6的信息:

<!-- 使用构造器函数给Bean注入属性 -->
    <bean id="bean6" class="com.envy.demo.Bean6">
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg name="age" value="22"></constructor-arg>
    </bean>

第三步:新建一个测试方法testBeanOne:

   @Test
    /**
     * 使用构造器函数给Bean注入属性
     * **/
    public void testBeanOne(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean6 bean6 =(Bean6)applicationContext.getBean("bean6");
        System.out.println(bean6);
    }

运行结果:

Bean6{name='张三', age=22}

属性setter方法注入

使用属性的setter方法,并通过Spring配置文件中的<property>元素来声明属性的名称及值。

第一步:新建Bean7.java文件,里面的代码如下:

public class Bean7 {
    private String id;
    
    public String getId(){
        return this.id;
    }
    public void setId(String id){
        this.id=id;
    }
   @Override
    public String toString() {
        return "Bean7{" +
                "id='" + id + '\'' +
                '}';
    }
}

第二步:往applicationContext.xml文件中新增Bean7的信息:

    <!-- 属性setter方法注入 -->
    <bean id="bean7" class="com.envy.demo.Bean7">
        <property name="id" value="001"></property>
    </bean>

第三步:新建一个测试方法testBeanTwo:

    @Test
    /**
     * 属性setter方法注入
     * **/
    public void testBeanTwo(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean7 bean7 =(Bean7)applicationContext.getBean("bean7");
        System.out.println(bean7);
    }

运行结果:

Bean7{id='001'}

其实在实际中setter属性注入是用的较多的方法,而更多的被用在属性是引用对象的情况,如现在的Bean6有一个属性是Bean7,则应该怎么修改代码呢?按照下面的步骤进行:

第一步:新建Bean67.java文件,里面的代码如下:

public class Bean67 {
    private String name;
    private Integer age;
    private Bean7 bean7;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Bean7 getBean7() {
        return bean7;
    }

    public void setBean7(Bean7 bean7) {
        this.bean7 = bean7;
    }

    @Override
    public String toString() {
        return "Bean67{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", bean7=" + bean7 +
                '}';
    }
}

第二步:往applicationContext.xml文件中新增Bean67信息:

    <!-- 属性setter方法注入 -->
    <bean id="bean7" class="com.envy.demo.Bean7">
        <property name="id" value="001"></property>
    </bean>

    <bean id="bean67" class="com.envy.demo.Bean67">
        <property name="name" value="张三"></property>
        <property name="age" value="22"></property>
        <property name="bean7" ref="bean7"></property>  //ref中为引用对象的id或者name
    </bean>

第三步:新增测试方法testBeanThree:

    @Test
    /**
     * 属性setter方法注入(参数中包含引用对象)
     * **/
    public void testBeanThree(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean67 bean67 =(Bean67)applicationContext.getBean("bean67");
        System.out.println(bean67);
    }

运行结果:

Bean67{name='张三', age=22, bean7=Bean7{id='001'}}

也就是说普通属性的注入使用name,而引用属性使用ref,这种方式在构造方法中注入也是适用的。

p名称空间属性注入

当然还可以使用p名称空间的方法来实现属性的注入,它是为了简化xml文件配置,从Spring2.5开始引入的。使用p:<属性名>="xxx"引入常量值;使用p:<属性名>-ref="xxx"引用其他Bean对象。

接下来使用前面介绍的Bean67进行p名称空间属性注入的演示。你只需要注释前面的配置,并使下面的配置生效即可:

<!-- 导入p名称空间 -->
xmlns:p="http://www.springframework.org/schema/p"

<!-- 属性p名称空间注入  -->
<bean id="bean7" class="com.envy.demo.Bean7" p:id="001"></bean>
<bean id="bean67" class="com.envy.demo.Bean67" p:name="李四" p:age="18" p:bean7-ref="bean7"></bean>

SpEL注入

SpEL(Spring Expression Language),使用Spring表达式语言对依赖注入进行简化。语法是#{表达式},通常是<bean id="xxx" value="#{表达式}"> 。下面介绍一些常用的SpEL的用法:1、传递变量使用#{变量名称};2、传递字符串使用#{'字符串名称'};3、使用另一个Bean对象采用#{helloId2};4、使用指定名属性且使用方法#{world.print.toUpperCase()};5、使用静态字段或者方法#{T(java.lang.Math).PI}等等。

下面通过product和Category这两个类来演示SpEL的属性注入。

第一步,新建Product.java文件,里面的代码如下:

public class Product {
    private String name;
    private double price;
    private Category category;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", price=" + price +
                ", category=" + category +
                '}';
    }
}

以及Category.java文件,里面的代码如下:

public class Category {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Category{" +
                "name='" + name + '\'' +
                '}';
    }
}

第二步,往applicationContext.xml文件中新增以下代码:

   <!-- SpEL属性注入  -->
    <bean id="product" class="com.envy.demo.Product">
        <property name="name" value="#{'童装'}"></property>
        <property name="price" value="#{99.00}"></property>
        <property name="category" value="#{category}"></property>  <!-- 注意value的值必须是引用对象Bean的id或者name属性 -->
    </bean>
    <bean id="category" class="com.envy.demo.Category">
        <property name="name" value="#{'001'}"></property>
    </bean>

第三步,新增测试方法testBeanFour:

    @Test
    /**
     * 属性SpELl方法注入(参数中包含引用对象)
     * **/
    public void testBeanFour(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Product product =(Product) applicationContext.getBean("product");
        System.out.println(product);
    }

运行结果:

Product{name='童装', price=99.0, category=Category{name='001'}}

现在只是单纯的使用到了引用对象的属性,接下来演示引用对象的方法注入。

可以新建一个ProductPrice.java文件:

public class ProductPrice {
    public double CalcPrice(){
        return Math.random()*99;
    }
}

接着修改applicationContext.xml文件为:

  <!-- SpEL属性注入  -->
    <bean id="product" class="com.envy.demo.Product">
        <property name="name" value="#{'童装'}"></property>
        <property name="price" value="#{productPrice.CalcPrice()}"></property>
        <property name="category" value="#{category}"></property>  <!-- 注意value的值必须是引用对象Bean的id或者name属性 -->
    </bean>
    <bean id="productPrice" class="com.envy.demo.ProductPrice"></bean>

    <bean id="category" class="com.envy.demo.Category">
        <property name="name" value="#{'001'}"></property>
    </bean>

最后运行一下测试方法testBeanFour,结果为:

Product{name='童装', price=21.540606263709023, category=Category{name='001'}}

复杂类型的属性注入

复杂类型是指属性为数组、List、Map、Set、Properties类型。

新建一个CollectionBean.java文件,里面的代码如下:

public class CollectionBean {
    private String[] stringArray;  //数组类型
    private List<String> stringList;  //列表类型
    private Set<String> stringSet;  //集合类型
    private Map<String,Integer> stringIntegerMap;  //map类型
    private Properties properties;  //属性类型

    public String[] getStringArray() {
        return stringArray;
    }

    public void setStringArray(String[] stringArray) {
        this.stringArray = stringArray;
    }

    public List<String> getStringList() {
        return stringList;
    }

    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }

    public Set<String> getStringSet() {
        return stringSet;
    }

    public void setStringSet(Set<String> stringSet) {
        this.stringSet = stringSet;
    }

    public Map<String, Integer> getStringIntegerMap() {
        return stringIntegerMap;
    }

    public void setStringIntegerMap(Map<String, Integer> stringIntegerMap) {
        this.stringIntegerMap = stringIntegerMap;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "CollectionBean{" +
                "stringArray=" + Arrays.toString(stringArray) +
                ", stringList=" + stringList +
                ", stringSet=" + stringSet +
                ", stringIntegerMap=" + stringIntegerMap +
                ", properties=" + properties +
                '}';
    }
}

接着注释掉applicationContext.xml文件中的其他代码,添加以下代码:

    <!-- 复杂(集合)类型的属性注入 -->
    <bean id="collectionBean" class="com.envy.demo.CollectionBean">
        <!-- 数组类型属性注入 -->
        <property name="stringArray">
            <list>
                <value>数组1</value>
                <value>数组2</value>
                <value>数组3</value>
            </list>
        </property>

        <!-- List类型属性注入 -->
        <property name="stringList">
            <list>
                <!-- 普通属性用value -->
                <value>List1</value>
                <value>List2</value>
                <!-- 对象属性使用ref -->
                <!--<ref>List3</ref>-->
                <!--<ref>List4</ref>-->
            </list>
        </property>

        <!-- Set类型属性注入 -->
        <property name="stringSet">
            <set>
                <value>Set1</value>
                <value>Set2</value>
                <value>Set3</value>
            </set>
        </property>

        <!-- Map类型属性注入 -->
        <property name="stringIntegerMap">
            <map>
                <entry key="map1" value="01"></entry>
                <entry key="map2" value="02"></entry>
                <entry key="map3" value="03"></entry>
            </map>
        </property>

        <!-- Properties类型属性注入 -->
        <property name="properties">
            <props>
                <prop key="username">hello</prop>
                <prop key="password">world</prop>
                <prop key="update">good</prop>
            </props>
        </property>
    </bean>

最后新建一个测试testBeanFive方法,里面的代码如下:

    @Test
    /**
     * 复杂类型的属性注入
     * **/
    public void testBeanFive(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        CollectionBean collectionBean =(CollectionBean) applicationContext.getBean("collectionBean");
        System.out.println(collectionBean);
    }

运行结果为:

CollectionBean{stringArray=[数组1, 数组2, 数组3], stringList=[List1, List2], stringSet=[Set1, Set2, Set3], stringIntegerMap={map1=1, map2=2, map3=3}, properties={password=world, username=hello, update=good}}

复杂属性注入一般在进行框架整合的时候才会使用,而用的较多的属性则是properties和map。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

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

    实际开发中还有一种XML和注解整合开发:Bean有XML配置.但是使用的属性使用注解注入.

    挽风
  • Spring官网阅读(七)容器的扩展点(二)FactoryBean

    上面这些概念可能刚刚说的时候大家不是很明白,下面我们通过FactoryBean的一些应用来进一步体会这个接口的作用。

    程序员DMZ
  • Spring官网阅读系列(七):容器的扩展点(FactoryBean)

    上面这些概念可能刚刚说的时候大家不是很明白,下面我们通过FactoryBean的一些应用来进一步体会这个接口的作用。

    秃顶的Java程序员
  • 你竟敢说你懂Spring框架?有可能你是没看到这些...(上)

    所以,特地去搜刮了一些关于spring的面试题,希望能帮助各位同学在升职加薪的路上,一去不复返。

    Java学习
  • 《Spring 手撸专栏》第 2 章:小试牛刀,实现一个简单的Bean容器!

    你总会在小傅哥的文章前言里,发现一些关于成长、学习、感悟以及对当篇内容的一个介绍,其实之所以写这样的铺垫性内容,主要是为了让大家对接下来的内容学习有一个较轻松的...

    小傅哥
  • 《Spring 手撸专栏》第 2 章:小试牛刀(让新手能懂),实现一个简单的Bean容器

    你总会在小傅哥的文章前言里,发现一些关于成长、学习、感悟以及对当篇内容的一个介绍,其实之所以写这样的铺垫性内容,主要是为了让大家对接下来的内容学习有一个较轻松的...

    小傅哥
  • 关于Spring面试题讲解1

    Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标...

    Java学习
  • 向Spring大佬低头——大量源码流出解析

    优秀的源码中有着多年沉积下来的精华,这些精华是非常值得我们学习的。放弃阅读源码,你将失去一个和大师学习的机会。

    Java团长
  • 金三银四Java面试必备132道Spring系列问题解析,吃透让你涨薪10K

    春节过后就是招聘旺季,因为疫情的影响,今年的金三银四有些特别,危机面前,持续的提高自己才是王道,提前准备下面试,有备无患。在这免费分享Spring,Spring...

    用户5546570
  • 原向Spring大佬低头--大量源码流出解析

           用Spring框架做了几年的开发,只停留在会用的阶段上,然而Spring的设计思想和原理确实一个巨大的宝库。大部分人仅仅知道怎么去配,或着加上什么...

    我叫刘半仙
  • [Spring框架]Spring开发实例: XML+注解.

    一枝花算不算浪漫
  • 关于Spring面试题讲解3

    47.如何通过HibernateDaoSupport将Spring和Hibernate结合起来?

    Java学习
  • Spring和SpringMVC父子容器关系初窥

    最近由于项目的包扫描出现了问题,在解决问题的过程中,偶然发现了Spring和SpringMVC是有父子容器关系的,而且正是因为这个才往往会出现包扫描的问题,我们...

    JAVA高级架构开发
  • Java开发Spring第一天

    今日内容 Spring框架的概述 Spring的快速入门 IoC容器装配Bean(xml配置方式) Ioc容器装配Bean(注解方式) 在web项目中集成Sp...

    Java帮帮
  • 三条路线告诉你如何掌握Spring IoC容器的核心原理

    前三篇已经从历史的角度和大家一起探讨了为什么会有Spring,Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国。本节的主...

    Java后端技术
  • 史上最全 69 道 Spring 面试题和答案

    如果你是初学者,或者是自学者!你可以加小编微信(xxf960513)!小编可以给你学习上,工作上的一些建议以及可以给你(免费)提供学习资料!最重要我们还可以交个...

    Java学习
  • spring @EnableAutoConfiguration实现原理

    在refresh中调用了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors。Po...

    平凡的学生族
  • 走进JavaWeb技术世界10:从JavaBean讲到Spring

    本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看

    Java技术江湖
  • 从Spring 应用上下文获取 Bean 的常用姿势

    通常,在Spring应用程序中,当我们使用 @Bean,@Service,@Controller,@Configuration 或者其它特定的注解将 Bean ...

    码农小胖哥

扫码关注云+社区

领取腾讯云代金券