共计 10577 个字符,预计需要花费 27 分钟才能阅读完成。
一、简介
spring 可以通过读取 xml 配置文件的方式,生成 bean 实例对象。
现有如下 beans.xml 配置文件和实体类与接口。
<?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">
<bean id="userService" class="com.amjun.spring.service.impl.UserServiceImpl">
</bean>
</beans>
UserService.java
public interface UserService {
void save();
}
UserServiceImpl.java
package com.amjun.spring.service.impl;
import com.amjun.spring.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void save() {
}
}
可以通过 BeanFactory 创建容器。
public class BeanFactoryTest {
public static void main(String[] args) {
// 创建工程对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建xml文件读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 读取配置文件给工厂
reader.loadBeanDefinitions("beans.xml");
// 去工厂获取实例
UserService userService = (UserService)beanFactory.getBean("userService");
System.out.println(userService);
}
}
也可以通过 ApplicationContext 创建容器,但实际上 ApplicationContext 还是通过 BeanFactory 创建,并且他们创建实例的时机不通。
ApplicationContext 在加载配置文件时创建 bean 对象放入容器,BeanFactory 在获取 bean 对象时才创建,并放入容器。
public class BeanFactoryTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService)applicationContext.getBean("userService");
System.out.println(userService);
}
}
二、xml配置文件详解
Spring 开发中主要是对 Bean 的配置,Bean 的常用配置如下:
/>1. 基础配置
这里的基础配置比较简单,这里只给出最终的 xml 文件和相关文件。
<?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">
<!-- 1. 可以配置 id 或者 别名,如果 id 和别名都不配置则可以通过全限定名进行获取对象
2. 默认对象是单例模式 singleton,可设置为 prototype 多例模式(springmvc 情况下可以设置 request、session 域)
3. lazy-init 配置懒加载,对于 beanfactory 无效,因为 beanfactory 是获取时才创建
4. init-method 指定初始化方法,destroy-method 指定销毁方法 - 当容器显示关闭时执行
除了指定 init-method,还可以通过实现 InitializingBean 接口的 afterPropertiesSet 方法实现初始化, 并在 init-method 之前执行
-->
<bean id="userService" name="alaisName,alaisName2" scope="singleton" lazy-init="false" init-method="init" destroy-method="destory"
class="com.amjun.spring.service.impl.UserServiceImpl">
<!--<constructor-arg name="name" value="abc"></constructor-arg>-->
<property name="userMapper" ref="userMapper"></property>
</bean>
<bean id="userMapper" class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>
</beans>
UserService.java
public interface UserService {
void save();
}
UserServiceImpl.java
public class UserServiceImpl implements UserService, InitializingBean {
private String name;
private UserMapper userMapper;
public UserServiceImpl() {
System.out.println("UserService实例化-无参构造");
}
public UserServiceImpl(String name) {
System.out.println("UserService实例化-有参构造");
}
public void setUserMapper(UserMapper userMapper) {
System.out.println("依赖注入userMapper");
this.userMapper = userMapper;
}
public void init(){
System.out.println("UserService初始化方法");
}
public void destory(){
System.out.println("UserService销毁方法");
}
@Override
public void save() {
System.out.println("UserService 调用 userMapper 的 insert 方法");
userMapper.insert();
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行afterPropertiesSet方法....");
}
}
UserMapper.java
public interface UserMapper {
void insert();
}
UserMapperImpl.java
public class UserMapperImpl implements UserMapper {
public UserMapperImpl() {
System.out.println("UserMapper实例化");
}
@Override
public void insert() {
System.out.println("用户信息入库");
}
}
测试方法:
public class BeanFactoryTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService)applicationContext.getBean("userService");
System.out.println(userService);
userService.save();
// 设置为多例模式每个对象都是新创建的
UserService userService2 = (UserService)applicationContext.getBean("userService");
System.out.println(userService2);
applicationContext.close();
}
}
2. 实例化bean的方式
2.1 构造方法实例化
默认就是使用无参构造方法进行实例化,如果需要有参构造方法实例化需要配置 constructor-arg 标签,且可以配置多个标签。
<bean id="userService" class="com.amjun.spring.service.impl.UserServiceImpl">
<constructor-arg name="name" value="abc"></constructor-arg>
</bean>
2.2 工厂方式实例化
底层通过调用自定义的工厂方法对Bean进行实例化。
- 静态工厂实例化
public class MyBeanFactory1 {
// 如果 userMapper 方法需要参数,同时配置 constructor-arg 即可
public static UserMapper userMapper(){
return new UserMapperImpl();
}
}
xml 文件。
<bean id="userMapper1" class="com.amjun.spring.factory.MyBeanFactory1" factory-method="userMapper"></bean>
测试方法:
public class BeanFactoryTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserMapper userMapper1 = (UserMapper)applicationContext.getBean("userMapper1");
System.out.println(userMapper1);
}
}
静态工厂可以不创建工厂实例,通过静态方法创建对象。
/>原理是 spring 解析配置文件时发现,需要生成的不是 MyBeanFactory1 对象,而是 userMapper 方法返回的对象。
好处:
- bean 创建前后可以进行一些其他操作
- 可以通过这种方式创建非自定义的 bean 交给 spring 进行管理(通过静态方法创建的bean)
- 实例工厂实例化
public class MyBeanFactory2 {
// 如果 userMapper 方法需要参数,同时配置 constructor-arg 即可
public UserMapper userMapper(){
return new UserMapperImpl();
}
}
xml 文件。
<bean id="myBeanFactory2" class="com.amjun.spring.factory.MyBeanFactory2"></bean>
<bean id="userMapper2" factory-bean="myBeanFactory2" factory-method="userMapper"></bean>
测试方法:
public class BeanFactoryTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserMapper userMapper2 = (UserMapper)applicationContext.getBean("userMapper2");
System.out.println(userMapper2);
}
}
可以看见先创建了工厂实例,再通过工厂方法创建实例。
/>好处:
- bean 创建前后可以进行一些其他操作
- 可以通过这种方式创建非自定义的 bean 交给 spring 进行管理(通过实例对象创建的bean)
- 实现FactoryBean规范延迟实例化Bean
FactoryBean 是工厂 Bean, 注意和 BeanFactory 区分开。
FactoryBean 是一个接口。
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
创建一个 MyBeanFactory3 实现 FactoryBean。
public class MyBeanFactory3 implements FactoryBean<UserMapper> {
@Override
public UserMapper getObject() throws Exception {
return new UserMapperImpl();
}
@Override
public Class<?> getObjectType() {
return UserMapper.class;
}
}
xml 文件。
<bean id="userMapper3" class="com.amjun.spring.factory.MyBeanFactory3"></bean>
测试方法:
public class BeanFactoryTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserMapper userMapper3 = (UserMapper)applicationContext.getBean("userMapper3");
System.out.println(userMapper3);
}
}
为什么叫延迟实例化bean?
当加载配置文件时,可以看见并没有创建 UserMapper 实例,而是创建了工厂 Bean,并且此时 factoryBeanObjectCache 里面没有任何东西。
/>当去容器里面获取实例时,FactoryBean 才调用 getObject 方法进行实例化 bean,并将实例放入 factoryBeanObjectCache 中。
/>3. bean的依赖注入
3.1 手动装配
bean 的依赖注入有两种方式。
/>依赖注入的数据类型有如下三种
- 普通数据类型,例如:String、int、boolean 等,通过 value 属性指定
- 引用数据类型,例如:UserDaolmpl、DataSource 等,通过 ref 属性指定
- 集合数据类型,例如:List、Map、Properties等
前面两种数据类型比较常见,这里说下 list 数据类型的注入,配置 set 将 list 标签替换为 set 即可。
现 UserServiceImpl.java 如下:
public class UserServiceImpl implements UserService {
private List<String> stringList;
private List<UserMapper> userMapperList;
private Map<String, UserMapper> map;
private Properties properties;
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public void setUserMapperList(List<UserMapper> userMapperList) {
this.userMapperList = userMapperList;
}
public void setMap(Map<String, UserMapper> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
则 xml 配置如下:
<bean id="userService" class="com.amjun.spring.service.impl.UserServiceImpl">
<property name="stringList">
<list>
<value>aaa</value>
<value>bbb</value>
</list>
</property>
<property name="userMapperList">
<list>
<!-- 方式一 -->
<!-- <bean class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>
<bean class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>
<bean class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>-->
<!-- 方式二 -->
<ref bean="userMapper1"></ref>
<ref bean="userMapper2"></ref>
<ref bean="userMapper3"></ref>
</list>
</property>
<property name="map">
<map>
<entry key="d1" value-ref="userMapper1"></entry>
<entry key="d2" value-ref="userMapper2"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="p1">ppp1</prop>
<prop key="p2">ppp2</prop>
</props>
</property>
</bean>
<bean id="userMapper1" class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>
<bean id="userMapper2" class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>
<bean id="userMapper3" class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>
3.2 自动装配
如果被注入的属性类型是Bean引用的话,那么可以在<bean>
标签中使用autowire属性去配置自动注入方式,属性值有两个:
- byName:通过属性名自动装配,即去匹配 setXxx 与 id=“xxx”(name=“xxx")是否一致
- byType:通过 Bean 的类型从容器中匹配,匹配出多个相同 Bean 类型时,报错
<!-- 需要 UserServiceImpl 有 setUserMapper 方法, 有此方法时,自动去容器里面寻找 id 为 userMapper 的对象进行自动装配-->
<bean id="userService" class="com.amjun.spring.service.impl.UserServiceImpl" autowire="byName">
</bean>
<!-- 不管 UserMapperImpl 的 id 为任何值都可以自动装配,但是存在多个时报错-->
<bean id="userService2" class="com.amjun.spring.service.impl.UserServiceImpl" autowire="byType">
</bean>
<bean id="userMapper" class="com.amjun.spring.mapper.impl.UserMapperImpl"></bean>
4. spring的其他配置标签
Spring的xml标签大体上分为两类,一种是默认标签,一种是自定义标签
-
默认标签:就是不用额外导入其他命名空间约束的标签,例如
<bean>
标签 -
自定义标签:就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如
<context:property-placeholder/>
标签
例如引入 context 命名空间,使用它里面的标签需要添加前缀。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder></context:property-placeholder>
</beans>
默认标签如下:
/>需要注意的是,<beans>
标签可以嵌套。
<?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">
<!-- 默认环境 -->
<bean id="userService" class="com.amjun.spring.service.impl.UserServiceImpl"></bean>
<!-- 开发环境 -->
<beans profile="dev">
<bean id="userService1" class="com.amjun.spring.service.impl.UserServiceImpl"></bean>
</beans>
<!-- 测试环境 -->
<beans profile="test">
<bean id="userService2" class="com.amjun.spring.service.impl.UserServiceImpl"></bean>
</beans>
</beans>
可以使用以下两种方式指定被激活的环境:
- 使用命令行动态参数,虚拟机参数位置加载 -Dspring.profiles.active=test
- 使用代码的方式设置环境变量 System.setProperty("spring.profiles.active","test")·
当激活 test 环境时,可以在 environment 对象中看见。
/>即使指定激活了 test 环境,还是可以获取到 id 为 userService 的实例,因为它是公共的。
当配置文件很多时,可以通过主配置文件引入其他配置文件。
<!-- 引入数据库配置文件-->
<importresource="classpath:dataSource.xml"></import>
<!-- 引入springmvc配置文件-->
<importresource="classpath:springMvc.xml"></import>