Spring框架学了不会应用?一篇文章让你深度了解SpringBean的配置

基于XML的Spring的应用

SpringBean的配置

Spring开发中主要是对Bean的配置,Bean的常用配置一览如下:

<bean id="" class="">

Bean的id和全限定名配置

<bean name="">

通过name设置Bean的别名,通过别名也能直接获取到Bean实例

<bean scope="">

Bean的作用范围,BeanFactory作为容器时取值singleton和prototype

<bean lazy-init="">

Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效

<bean init-method="">

Bean实例化后自动执行的初始化方法,method指定方法名

<bean destroy-method="">

Bean实例销毁前的方法,method指定方法名

<bean autowire="byType">

设置自动注入模式,常用的有按照类型byType,按照名字byName

<bean factory-bean="" factory-method=""/>

指定哪个工厂Bean的哪个方法完成Bean的创建

  1. Bean的基础配置

id是确定对象的唯一标识符,getbean方法获取的是id将其实例化,即获取beanName

<bean id="userDao" class="com.itlzxr.service.userServiceImpl"/>
applicationContext.getBean("userService")

不设置id,则获取的Bean实例对象的全限定名,将其存入spring容器(singltonObject单例池)中

key为id名,即userDao,value为userDao的实例对象userDaoImpl

applicationContext.getBean("com.itlzxr.service.userServiceImpl")
  1. Bean别名配置

Bean可设置多个别名,根据别名也可获取Bean对象

<bean id="userService" name="aaa,bbb,ccc" class="com.itlzxr.service.userServiceImpl"/>

此时,Bean具有多个别名,别名均可获取实例对象

applicationContext.getBean("userService")
applicationContext.getBean("aaa")
applicationContext.getBean("bbb")
applicationContext.getBean("ccc")

不配置id,配置了name,则默认情况下别名第一个为beanName

<bean name="aaa,bbb,ccc" class="com.itlzxr.service.userServiceImpl"/>
applicationContext.getBean("aaa")

一般情况下不配置别名,大部分情况配置id即可

  1. Bean的作用范围

默认情况下,简单的spring环境下,Bean的作用范围有两个,singleton和prototype

注:如果在MVC环境下,作用范围则会变为singleton、prototype、session、request
  • singleton:单例,Bean默认为单例作用范围,spring容器创建时获取实例化对象,每次获取的都是同一个实例对象,并将其存入singletoObje池中,getBean时则从单例池中获取

  • prototype:原型,只有当执行getBean方法时才会获取实例化对象,getBean创建的是新的Bean实例对象,每次获取的对象都不是同一个对象,且不会被存入singletonObject池中,userDao的信息被存储到beanDefinitionMap中

<bean name="aaa,bbb,ccc" class="com.itlzxr.service.userServiceImpl" scope="singleton"/>
  1. Bean的延迟加载

一般情况下,单例作用范围,spring容器加载完成后就会立刻获取实例化对象,如果配置lazy-init="true",则不会立刻创建bean实例对象,等到执行getBean方法时创建实例化对象,然后将其存入单例池,后续获取该对象时从单例池中获取,本质上Bean仍为单例

<bean id="userService" name="aaa,bbb,ccc" class="com.itlzxr.service.userServiceImpl" lazy-init="true"/>
  1. Bean初始化方法和销毁方法的配置
<bean id="userService" class="com.itlzxr.service.userServiceImpl" init-method="init" destory-medthod="destory"/>

Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁

方法完成一些操作,初始化方法名称和销毁方法名称通过

public class UserServiceImpl implements UserService {
	public UserDaoImpl() { 
        System.out.println("UserServiceImpl创建了..."); 
    }
	public void init(){ 
        System.out.println("初始化方法..."); 
    }
	public void destroy(){ 
        System.out.println("销毁方法..."); 
    }
}

注:Bean的销毁和Bean的销毁方法的调用不是同一个概念,Bean的销毁是指spring容器已被销毁,其维护的Bean对象也被销毁,而有时spring容器已经被销毁但spring也未执行到调用销毁方法的步骤

扩展:除此之外,我们还可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作

public class UserDaoImpl implements UserDao, InitializingBean {
	public UserDaoImpl() {System.out.println("UserDaoImpl创建了...");}
	public void init(){System.out.println("初始化方法...");}
	public void destroy(){System.out.println("销毁方法...");}
	//执行时机早于init-method配置的方法
	public void afterPropertiesSet() throws Exception {
		System.out.println("InitializingBean..."); 
	}
}
  1. Bean的实例化配置

Spring的实例化方式主要如下两种:

  • 构造方式实例化:底层通过构造方法对Bean进行实例化

  • 工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化

构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的 几乎都是无参构造该方式

//有参构造方法
public UserDaoImpl(String name){
}

有参构造在实例化Bean时,需要参数的注入,通过 标签,嵌入在 标签内部提供构造参数

<bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl">
	<constructor-arg name="name" value="lzxr"/>
</bean>

注: 标签不单单只是在构造方法时使用,只要创建Bean对象时就会使用

<bean id="userDao" class="com.itlzxr.factory.UserDaoFactoryBean" factory-method="getUserDao">
	<constructor-arg name="name" value="lzxr"/>
</bean>

工厂方式实例化Bean又分为如下三种

  • 静态工厂方法实例化Bean

  • 实例工厂方法实例化Bean

  • 实现FactoryBean规范延迟实例化Bean

静态工厂方法实例化Bean,其实就是定义一个工厂类,提供一个静态方法用于生产Bean实例,在将该工厂类及其静态方法配置给Spring即可

该方法不是构造方法,而是构造Bean的方法,只要是创建Bean的方法,内部参数在spring的配置当中都可以使用< 标签进行传递

public class UserDaoFactoryBean {
    public static UserService userService() {
        // Bean创建之前可进行其他业务操作
        return new UserServiceImpl();
    }
}

将其打印查看其具体信息,运行ApplicationTest,进行测试

ApplicationContext applicationContext = new ClassPathxmlApplicationContext("applicationContext.xml");
Object userDao = applicationContext.getBean("userDao");
System.out.println(userDao);

断点调试,发现其被存入到了单例池中

有些Bean不是通过构造方法来获取,而是通过某些对象的方法获取的,如果要将这些Bean交给spring去管理,可以使用实例工厂方式实例化

实例工厂方法实例化Bean,也就是非静态工厂方法产生Bean实例,与静态工厂方式比较,该方式需要先有工厂对象,再用工厂

对象去调用非静态方法,所以在进行配置时,要先配置工厂Bean,在配置目标Bean

<!-- 配置实例工厂Bean -->
<bean id="userDaoFactoryBean2" class="com.itlzxr.factory.UserDaoFactoryBean2"/>
<!-- 配置实例工厂Bean的哪个方法作为工厂方法 -->
<bean id="userDao" factory-bean="userDaoFactoryBean2" factory-method="getUserDao">
<constructor-arg name="name" value="lzxr"/>
</bean>

通过断点观察单例池singletonObjects,发现单例池中既有工厂Bean实例,也有目标Bean实例,且都是在Spring容器创建时,就完成了Bean的实例化

不管是静态工厂方式还是非静态工厂方式,都是自定义的工厂方法

Spring提供了FactoryBean的接口规范,FactoryBean接口定义如下:

public interface FactoryBean<T> {
	String OBJECT_TYPE_ATTRIBUTE = “factoryBeanObjectType”;
	T getObject() throws Exception; //获得实例对象方法
	Class<?> getObjectType(); //获得实例对象类型方法
	default boolean isSingleton() {
		return true;
	}
}

定义工厂实现FactoryBean

public class UserDaoFactoryBean3 implements FactoryBean<UserDao> {
	public UserDao getObject() throws Exception {
		return new UserDaoImpl();
	}
	public Class<?> getObjectType() {
		return UserDao.class;
	}
}

配置FactoryBean交由Spring管理即可

    <bean id="userDao3" class="com.itlzxr.factory.MyFactory3"></bean>

通过Spring容器根据beanName可以正常获得UserDaoImpl

ApplicationContext applicationContext = new ClassPathxmlApplicationContext("applicationContext.xml");
Object userDao = applicationContext.getBean("userDao");
System.out.println(userDao);

通过断点观察发现Spring容器创建时,FactoryBean被实例化了,并存储到了单例池singletonObjects中

但是getObject() 方法尚未被执行,UserDaoImpl也没被实例化,当首次用到UserDaoImpl时,才调用getObject()

此工厂方式产生的Bean实例不会存储到单例池singletonObjects中,会存储到 factoryBeanObjectCache 缓存池中,并且后期每次使用到userDao都从该缓存池中返回的是同一个userDao实例。

  1. Bean的依赖注入方式

Bean的依赖注入有两种方式:

  • 通过Bean的set方法注入

<property name="userDao" ref="userDao"/>
<property name="userDao" value="haohao"/>
  • 通过构造Bean的方法进行注入

<constructor-arg name="name" ref="userDao"/>
<constructor-arg name="name" value="haohao"/>

其中,ref 是 reference 的缩写形式,翻译为:涉及,参考的意思,用于引用其他Bean的id。value 用于注入普通属性值。

依赖注入的数据类型有如下三种:

  • 普通数据类型,例如:String、int、boolean等,通过value属性指定。

  • 引用数据类型,例如:UserDaoImpl、DataSource等,通过ref属性指定。

  • 集合数据类型,例如:List、Set、Map、Properties等。

注入 List 集合 – 普通数据

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    //注入List 通过set方法注入
    private List<String> stringList;

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

stringList既不是普通数据类型,也不是引用数据类型,因此,需要利用 子标签来对其进行指定,而list内部存的只是普通字符串,直接使用 标签进行指定

<bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl">
        <property name="stringList">
            <list>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>
    </bean>

注入 List 集合 – 引用数据

    private List<UserDao> userDaoList;

    public void setUserDaoList(List<UserDao> userDaoList) {
        this.userDaoList = userDaoList;
    }

很明显,此处为list集合,应使用 子标签,而此集合内部存的是Bean对象,因此,不可使用 标签进行指定

解决此问题有两种方式:

内部进行 配置,可以配置多个对象,每一个bean配置都代表一个bean对象

<bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl">
        <property name="userDaoList">
            <list>
                <bean class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
                <bean class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
                <bean class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
            </list>
        </property>
    </bean>

利用ref引用进行配置,外部进行bean配置,内部对其引用

<bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl">
        <property name="userDaoList">
            <list>
                <ref bean="userDao1"></ref>
                <ref bean="userDao2"></ref>
                <ref bean="userDao3"></ref>
            </list>
        </property>
    </bean>

    <bean id="userDao1" class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
    <bean id="userDao2" class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
    <bean id="userDao3" class="com.itlzxr.dao.impl.UserDaoImpl"></bean>

注入 Set 集合(同list相似,区别在于list有序,set无序

    <bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl">
        <property name="stringSet">
            <set>
                <value>xxxx</value>
                <value>yyyy</value>
            </set>
        </property>
        <property name="userDaoSet">
            <set>
                <ref bean="userDao1"></ref>
                <ref bean="userDao2"></ref>
                <ref bean="userDao3"></ref>
            </set>
        </property>
    </bean>

    <bean id="userDao1" class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
    <bean id="userDao2" class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
    <bean id="userDao3" class="com.itlzxr.dao.impl.UserDaoImpl"></bean>
	private Set<String> stringSet;
    private Set<UserDao> userDaoSet;

	// 注入Set
	// 注入泛型为字符串的Set集合
    public void setStringSet(Set<String> stringSet) {
        this.stringSet = stringSet;
    }

	// 注入泛型为对象的Set集合
    public void setUserDaoSet(Set<UserDao> userDaoSet) {
        this.userDaoSet = userDaoSet;
    }

注入 Map 集合

    <bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl">
        <property name="map">
            <map>
                <!--每个键值对组成一个entry-->
                <entry key="d1" value-ref="userDao1"></entry>
                <entry key="d2" value-ref="userDao2"></entry>
                <entry key="d3" value-ref="userDao3"></entry>
            </map>
        </property>
    </bean>
 private Map<String, UserDao> map;

    // //注入值为对象的Map集合
    public void setMap(Map<String, UserDao> map) {
        this.map = map;
    }

注入 Properties 键值对

    private Properties properties;

	// 注入Properties
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
<bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl">
        <property name="properties">
            <props>
                <prop key="p1">111</prop>
                <prop key="p2">222</prop>
            </props>
        </property>
    </bean>

扩展:自动装配方式

如果被注入的属性类型是Bean引用的话,那么可以在 标签中使用 autowire 属性去配置自动注入方式,属性值有两个:

  • byName:通过属性名自动装配,即去匹配 setXxx 与 id="xxx"(name="xxx")是否一致

  • byType:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错

<bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl" autowire="byType">
  1. spring的其他配置标签

Spring 的 xml 标签大体上分为两类,一种是默认标签,一种是自定义标签

  • 默认标签:就是不用额外导入其他命名空间约束的标签,例如 标签

  • 自定义标签:就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如< context:property-placeholder/ >标签

Spring的默认标签用到的是Spring的默认命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

自定义标签

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-beans.xsd">
    <context:property-placeholder></context:property-placeholder>
</beans>

该命名空间约束下的默认标签如下:

标签

作用

<beans>

一般作为 xml 配置根标签,其他标签都是该标签的子标签

<bean>

Bean的配置标签,上面已经详解了,此处不再阐述

<import>

外部资源导入标签

<alias>

指定Bean的别名标签,使用较少

<bean> 标签,除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境

<!-- 配置测试环境下,需要加载的Bean实例 -->
<beans profile="test">
</beans>
<!-- 配置开发环境下,需要加载的Bean实例 -->
<beans profile="dev">
</beans>
<!--默认环境-->
<!--配置UserServiceImpl-->
<bean id="userService" class="com.itlzxr.service.impl.UserServiceImpl"></bean>
<!--配置UserDaoImpl-->
<bean id="userDao" class="com.itlzxr.dao.impl.UserDaoImpl"></bean>

可以使用以下两种方式指定被激活的环境:

  • 使用命令行动态参数,虚拟机参数位置加载 -Dspring.profiles.active=test

  • 使用代码的方式设置环境变量 System.setProperty("spring.profiles.active","test")

在没有指定环境的情况下,只有默认环境下的标签生效,若指定了测试环境,则测试环境和默认环境的标签都生效

标签,用于导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务某块进行拆分,拆分后,最终通过 标签导入到一个主配置文件中,项目加载主配置文件就连同 导入的文件一并加载了

    <!--导入用户模块配置文件-->
    <import resource="applicationContext-user.xml"></import>
    <!--导入商品模块配置文件-->
    <import resource="applicationContext-order.xml"></import>

标签是为某个Bean添加别名,与在 标签上使用name属性添加别名的方式一样

我们为UserServiceImpl指定四个别名:aaa、bbb、xxx、yyy

<!--配置UserService-->
<bean id="userService" name="aaa,bbb" class="com.itlzxr.service.impl.UserServiceImpl">
	<property name="userDao" ref="userDao"/>
</bean>
<!--指定别名-->
<alias name="userService" alias="xxx"/>
<alias name="userService" alias="yyy"/>

断点调试,在beanFactory中维护着一个名为aliasMap的Map<String,String>集合,存储别名和beanName之间的映射关系

Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用 <前缀:标签> 形式的标签,称之为自定义标签,自定义标签的解析流程也是 Spring xml扩展点方式之一

<!--默认标签-->
<bean id="userDao" class="com.itlzxr.dao.impl.UserDaoImpl"/>
<!--自定义标签-->
<context:property-placeholder/>
<mvc:annotation-driven/>
<dubbo:application name="application"/>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值