前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring学习(2):Spring Bean管理(上)

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

原创
作者头像
啃饼思录
修改2020-07-20 09:59:21
8910
修改2020-07-20 09:59:21
举报

#Spring工厂类介绍

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

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

代码语言:txt
复制
   /**
     * 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文件:

代码语言:txt
复制
public class Bean1 {
    /**
     * 使用无参数的类构造器实例化Bean
     */
    public Bean1(){
        System.out.println("使用无参数的类构造器实例化Bean");
    }
}

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

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

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

代码语言:txt
复制
public class SpringBeanTest {
    /**
     *
     * Bean实例化的三种方式
     **/
    @Test
    public void testOne(){
        /**
         * 使用无参数的构造函数实例化Bean
         * **/

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

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

代码语言:txt
复制
使用无参数的类构造器实例化Bean

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

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

代码语言:txt
复制
public class Bean2{
   
}

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

代码语言:txt
复制
public class Bean2Factory {
    public static Bean2 createBean2(){
        System.out.println("使用静态工厂实例化Bean");
        return new Bean2();
    }
}

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

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

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

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

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

代码语言:txt
复制
使用无参数的类构造器实例化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文件:

代码语言:txt
复制
public class Bean3{
   
}

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

代码语言:txt
复制
public class Bean3Factory {
    /**
     * Bean3的实例工厂
     **/
    public Bean3 createBean3(){
        System.out.println("使用实例工厂实例化Bean");
        return new Bean3();
    }
}

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

代码语言:txt
复制
    <!-- 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方法:

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

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

代码语言:txt
复制
使用实例工厂实例化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文件:

代码语言:txt
复制
public class Bean4 {
}

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

代码语言:txt
复制
    <!-- 测试Bean的作用域 -->
    <bean id="bean4" class="com.envy.demo.Bean4"></bean>

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

代码语言:txt
复制
  @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方法,发现控制台输出:

代码语言:txt
复制
com.envy.demo.Bean4@51c8530f
com.envy.demo.Bean4@51c8530f

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

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

代码语言:txt
复制
com.envy.demo.Bean4@51c8530f
com.envy.demo.Bean4@7403c468

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

Bean的生命周期配置

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

代码语言:txt
复制
<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文件:

代码语言:txt
复制
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:

代码语言:txt
复制
    <!-- 测试Bean的生命周期 -->
    <bean id="bean5" class="com.envy.demo.Bean5" init-method="initBean5" destroy-method="destroyBean5"></bean>

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

代码语言:txt
复制
   @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方法,发现控制台输出:

代码语言:txt
复制
Bean5被实例化了
Bean5被初始化了
com.envy.demo.Bean5@51c8530f
Bean5被销毁了

Bean的生命周期完整过程

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

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

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

代码语言:txt
复制
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:

代码语言:txt
复制
 <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接口,并新增以下代码:

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

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

代码语言:txt
复制
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第四步:了解工厂信息");
    }

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

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

代码语言:txt
复制
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:

代码语言:txt
复制
<bean class="com.envy.demo.MyBean5PostProcessor"></bean>

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

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

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

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

代码语言:txt
复制
    public void initBean5(){
        System.out.println("第七步:Bean5被初始化了");
    }

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

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

代码语言:txt
复制
  public void run(){
        System.out.println("第九步:执行自身业务逻辑方法");
    }

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

代码语言:txt
复制
 public void destroy() throws Exception {
        System.out.println("第十步:执行Spring中的destroy方法");
    }

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

代码语言:txt
复制
    public void destroyBean5(){
        System.out.println("第十一步:Bean5被销毁了");
    }

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

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

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

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

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

BeanPostProcessor的作用

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

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

代码语言:txt
复制
public interface UserDao {
    public void findAll();

    public void save();

    public void update();

    public void delete();
}

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

代码语言:txt
复制
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:

代码语言:txt
复制
<bean id="userDao" class="com.envy.demo.UserDaoImpl"></bean>

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

代码语言:txt
复制
<bean class="com.envy.demo.MyBean5PostProcessor"></bean>
<!-- 测试BeanPostProcessor的作用 -->
<bean id="userDao" class="com.envy.demo.UserDaoImpl"></bean>

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

代码语言:txt
复制
    @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方法,发现控制台输出:

代码语言:txt
复制
第五步:这是初始化前的方法...
第八步:这是初始化后的方法...
查询用户
保存用户
修改用户
删除用户

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

代码语言:txt
复制
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方法,发现控制台输出:

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

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

DI依赖注入

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

构造函数注入

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

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

代码语言:txt
复制
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的信息:

代码语言:txt
复制
<!-- 使用构造器函数给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:

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

运行结果:

代码语言:txt
复制
Bean6{name='张三', age=22}

属性setter方法注入

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

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

代码语言:txt
复制
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的信息:

代码语言:txt
复制
    <!-- 属性setter方法注入 -->
    <bean id="bean7" class="com.envy.demo.Bean7">
        <property name="id" value="001"></property>
    </bean>

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

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

运行结果:

代码语言:txt
复制
Bean7{id='001'}

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

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

代码语言:txt
复制
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信息:

代码语言:txt
复制
    <!-- 属性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:

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

运行结果:

代码语言:txt
复制
Bean67{name='张三', age=22, bean7=Bean7{id='001'}}

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

p名称空间属性注入

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

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

代码语言:txt
复制
<!-- 导入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文件,里面的代码如下:

代码语言:txt
复制
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文件,里面的代码如下:

代码语言:txt
复制
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文件中新增以下代码:

代码语言:txt
复制
   <!-- 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:

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

运行结果:

代码语言:txt
复制
Product{name='童装', price=99.0, category=Category{name='001'}}

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

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

代码语言:txt
复制
public class ProductPrice {
    public double CalcPrice(){
        return Math.random()*99;
    }
}

接着修改applicationContext.xml文件为:

代码语言:txt
复制
  <!-- 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,结果为:

代码语言:txt
复制
Product{name='童装', price=21.540606263709023, category=Category{name='001'}}

复杂类型的属性注入

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

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

代码语言:txt
复制
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文件中的其他代码,添加以下代码:

代码语言:txt
复制
    <!-- 复杂(集合)类型的属性注入 -->
    <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方法,里面的代码如下:

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

运行结果为:

代码语言:txt
复制
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。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用XML来管理Bean
    • 使用XML来管理Bean----使用类构造器实例化(默认无参数)
      • 使用XML来管理Bean----使用静态工厂方法实例化(简单工厂模式)
        • 使用XML来管理Bean----使用实例工厂方法实例化(工厂方法模式)
          • Bean的配置
            • Bean的作用域
              • Bean的生命周期配置
                • Bean的生命周期完整过程
                  • BeanPostProcessor的作用
                  • DI依赖注入
                    • 构造函数注入
                      • 属性setter方法注入
                        • p名称空间属性注入
                          • SpEL注入
                            • 复杂类型的属性注入
                            相关产品与服务
                            容器服务
                            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档