三种使用模式
纯xml模式
web.xml中的配置:
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><!--配置Spring IoC容器的配置文件--><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><!--使用监听器启动Spring IoC容器--><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener></web-app>
Spring IoC实例化Bean的三种方式
使用无参构造器
applicationContext.xml:
<bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils" />
获取:
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
ConnectionUtils connectionUtils = (ConnectionUtils) applicationContext.getBean("connectionUtils");
静态方法
<bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" factory-method="getInstanceStatic" />
CreateBeanFactory.java
package com.lagou.edu.factory;
import com.lagou.edu.utils.ConnectionUtils;
public class CreateBeanFactory {
// 静态方法
public static ConnectionUtils getInstanceStatic() {
return new ConnectionUtils();
}
// 实例化方法
public ConnectionUtils getInstance() {
return new ConnectionUtils();
}
}
实例化方法
<bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory" />
<bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance" />
Bean标签属性
- id:唯一标识。
- class:创建bean对象的全限定类名。
- name:给bean提供一个或多个名称,空格分隔。
- factory-bean:指定创建当前bean对象的工厂bean的唯一标识,会使class属性失效。
- factory-method:指定创建当前bean对象的工厂方法。如果配合factory-bean属性使用,会使class属性失效;如果配合class属性使用,则该方法必须是static。
- scope:指定bean对象的作用范围,默认singleton。
- init-method:指定bean对象的初始化方法,此方法会在bean对象装配后调用,必须为无参方法。
- destroy-method:指定bean对象的销毁方法,此方法会在bean对象销毁前执行,只有scope为singleton的时候才起作用。
Bean的作用范围及生命周期

用scope定义bean的作用范围:
- singleton:单例,IoC容器中只有一个该类对象,默认。
- prototype:原型(多例),每次使用该类的对象(getBean),都返回给你一个新的对象。
其他几种很少使用。
DI依赖注入的xml配置
依赖注入分类
注入方式:
- 构造函数注入
- set方法注入
注入的数据类型:
- 基本类型和String
- 其他Bean类型
复杂类型(集合类型),如Array,List,Set,Map,Properties。
使用构造参数注入
涉及的标签是
constructor-arg,属性有:name:给构造函数中指定名称的参数赋值。
- index:给构造函数中指定索引位置的参数赋值。
- value:指定基本类型或者String类型的数据。
- ref:指定其他Bean类型的数据。
参考写法:
如JdbcAccountDaoImpl有构造器:
private ConnectionUtils connectionUtils;
private String name;
private int sex;
private float money;
public JdbcAccountDaoImpl(ConnectionUtils connectionUtils, String name, int sex, float money) {
this.connectionUtils = connectionUtils;
this.name = name;
this.sex = sex;
this.money = money;
}
xml配置可以为:
<bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" init-method="init" destroy-method="destroy">
<constructor-arg index="0" ref="connectionUtils"/>
<constructor-arg index="1" value="zhangsan"/>
<constructor-arg index="2" value="1"/>
<constructor-arg index="3" value="100.5"/>
</bean>
<bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" init-method="init" destroy-method="destroy">
<constructor-arg name="connectionUtils" ref="connectionUtils"/>
<constructor-arg name="name" value="zhangsan"/>
<constructor-arg name="sex" value="1"/>
<constructor-arg name="money" value="100.6"/>
</bean>
set方法注入
涉及的标签是property,属性有:
- name:指定注入时调用的set方法名称,不包含set这三个字母,Druid连接池指定属性名称。
- value:指定注入的数据,支持基本类型和String类型。
- ref:指定注入的数据,支持其他bean类型。
如JdbcAccountDaoImpl有一些setter:
private ConnectionUtils connectionUtils;
private String name;
private int sex;
private float money;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public void setName(String name) {
this.name = name;
}
public void setSex(int sex) {
this.sex = sex;
}
public void setMoney(float money) {
this.money = money;
}
xml配置可以为:
<bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" init-method="init" destroy-method="destroy">
<property name="ConnectionUtils" ref="connectionUtils"/>
<property name="name" value="zhangsan"/>
<property name="sex" value="1"/>
<property name="money" value="100.3"/>
</bean>
复杂数据类型的注入
集合分为两类:
- List结构(数组结构)。List结构的集合数据注入时,
array,list,set三个标签通用,value标签内部可以直接写值,也可以用bean标签配置一个对象,或者用ref标签引用一个已经配置的bean的唯一标识。 - Map结构(键值对)。Map结构的集合数据注入时,
map标签使用entry子标签实现数据注入,entry标签可以使用key和value属性指定存入map中的数据。使用value-ref属性指定已经配置好的bean的引用,同时entry标签中也可以使用ref标签,但是不能使用bean标签。而property标签中不能使用ref或者bean标签引用对象。
如JdbcAccountDaoImpl有一些setter:
private String[] myArray;
private Map<String,String> myMap;
private Set<String> mySet;
private Properties myProperties;
public void setMyArray(String[] myArray) {
this.myArray = myArray;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyProperties(Properties myProperties) {
this.myProperties = myProperties;
}
xml配置可以为:
<bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl" init-method="init" destroy-method="destroy">
<property name="myArray">
<array>
<value>array1</value>
<value>array2</value>
<value>array3</value>
</array>
</property>
<property name="myMap">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
</map>
</property>
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<property name="myProperties">
<props>
<prop key="prop1">value1</prop>
<prop key="prop2">value2</prop>
</props>
</property>
</bean>
xml与注解相结合模式
第三方jar中的bean定义在xml,如Druid数据库连接池。
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
创建jdbc.properties文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bank?&useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=zhaoyiqi
自己开发的bean定义使用注解。
xml中标签与注解的对应(IoC)
| xml形式 | 注解形式 |
|---|---|
| 标签 | @Component(“accountDao”),注解加在类上。 bean的id属性内容直接配置在注解后面。 如果不配置,默认定义个这个bean的id为类的类名首字母小写。 另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository,分别用于控制层类、服务层类、dao层类的bean定义。 这四个注解的用法完全一样,只是为了更清晰的区分而已。 |
| 标签的scope属性 | @Scope(“prototype”) |
| 标签的init-method属性 | @PostConstruct |
| 标签的destroy-method属性 | @PreDestroy |
DI依赖注入的注解实现方式
开启注解扫描,base-package指定扫描的包路径:
<context:component-scan base-package="com.lagou.edu"/>
@Autowired
需要导入org.springframework.beans.factory.annotation.Autowired。
自动装配,按类型注入。
如果按照类型无法唯一锁定对象,可以结合@Qualifier指定具体的id。
public class TransferServiceImpl {
@Autowired
@Qualifier(name="jdbcAccountDaoImpl")
private AccountDao accountDao;
}
@Resource
默认按照名称注入,也可以按照类型注入。
在jdk11中已经移除,如果还想使用,需要引入jar包javax.annotation-api。
纯注解模式
web.xml改为:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--告诉ContextloaderListener知道我们使用注解的方式启动ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!--配置启动类的全限定类名-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.lagou.edu.SpringConfig</param-value>
</context-param>
<!--使用监听器启动Spring的IOC容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
配置类为com.lagou.edu.SpringConfig。
对应注解:
- @Configuration:表明当前类是一个配置类。
- @ComponentScan:替代context:component-scan。
- @PropertySource:引入外部属性配置文件。
- @Import:引入其他配置类。
- @Value:对变量赋值,可以直接赋值,也可以使用${}读取资源配置文件中的信息。
- @Bean:将方法返回对象加入Spring IoC容器。
一个demo:
package com.lagou.edu;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
@Configuration
@ComponentScan({"com.lagou.edu"})
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
@Value("${jdbc.driver}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource createDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
高级特性
Lazy-init延迟加载
涉及bean标签的lazy-init属性,默认值为false,可以手动把默认值改为true:
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>
为false的时候,spring启动时,对应的bean立刻进行实例化。
为true的时候,bean不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean索取bean时,才进行实例化。
注意,如果一个bean的scope属性值为prototype,即使设置了lazy-init=”false”,容器启动时也不会实例化bean,在getBean的时候才实例化。
也可以使用注解@Lazy。
使用场景:
- 开启延迟加载,可以一定程度上提高容器启动和运转性能。
- 对于不常使用的bean设置延迟加载,不必一开始就占用资源。
FactoryBean
(BeanFactory是容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,一般使用它下面的子接口类型,如ApplicationContext。)
Spring中的Bean有两种:普通Bean和工厂Bean(FactoryBean).
FactoryBean可以生成某一个类型的Bean实例并返回,即我们可以借助FactoryBean自定义Bean的创建过程。
Bean创建的三种方式中,静态方法和实例化方法与FactoryBean的作用类似。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
public interface FactoryBean<T> {
@Nullable
// 返回FactoryBean创建的Bean实例
T getObject() throws Exception;
@Nullable
// 返回FactoryBean创建的Bean类型
Class<?> getObjectType();
// 如果isSingleton返回true,则getObject()返回的实例会放到Spring IoC容器的单例缓存池中
default boolean isSingleton() {
return true;
}
}
一个demo:
package com.lagou.edu.factory;
import com.lagou.edu.pojo.Company;
import org.springframework.beans.factory.FactoryBean;
/**
* @author 应癫
* */
public class CompanyFactoryBean implements FactoryBean<Company> {
private String companyInfo; // 公司名称,地址,规模
public void setCompanyInfo(String companyInfo) {
this.companyInfo = companyInfo;
}
@Override
public Company getObject() throws Exception {
Company company = new Company();
String[] strings = companyInfo.split(",");
company.setName(strings[0]);
company.setAddress(strings[1]);
company.setScale(Integer.parseInt(strings[2]));
return company;
}
@Override
public Class<?> getObjectType() {
return Company.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
后置处理器
Spring提供了两种后处理bean的扩展接口:
- BeanPostProcessor。在Bean对象实例化之后使用。
- BeanFactoryPostProcessor。在BeanFactory初始化之后使用。
对象不一定是spring bean,而spring bean一定是个对象。
SpringBean生命周期
- 根据配置情况调用Bean构造方法或工厂方法实例化Bean;
- 利用依赖注入完成Bean中所有属性值的配置注入;
- 如果Bean实现了BeanNameAware接口,则Spring调用Bean的setBeanName方法传入当前Bean的id值;
- 如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory方法传入当前工厂实例的引用;
- 如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext方法传入当前ApplicationContext实例的引用;
- 如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialization对Bean进行加工操作,Spring AOP就是用它实现的;
- 如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet方法;
- 如果在配置文件中通过init-method属性指定了初始化方法,则调用;
- 如果BeanPostFactory和Bean关联,则Spring将调用该接口的初始化方法postProcessAfterInitialization,此时,Bean已经可以被应用系统使用了;
- 如果在
bean标签中指定了该Bean的作用范围scope=”singleton”,则将该Bean放入Spring IoC容器的缓存池中,将触发Spring对该Bean的生命周期管理;如果scope=”prototype”,则将该Bean交给调用者; - 如果Bean实现了DisposableBean接口,则Spring会调用destroy方法将Spring中的Bean销毁;如果在配置文件中通过destroy-method属性指定了Bean的销毁方法,则Spring将调用该方法对Bean进行销毁。
BeanPostProcessor
可以针对某个具体的Bean。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
接口中的两个方法分别在Bean的初始化方法前和初始化方法后执行。这个初始化方法就是由init-method属性所指定的方法。
定义一个类实现BeanPostProcessor,默认会对整个Spring容器中所有的bean进行处理。
若要针对具体的某个bean,可以通过方法参数判断:
- Object bean是每个bean的实例。
- String beanName是每个bean的name或者id属性的值。
可以通过第二个参数来判断要处理的具体的bean。
一个demo:
package com.lagou.edu.pojo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* @author 应癫
*
* 拦截实例化之后的对象(实例化了并且属性注入了)
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if("lazyResult".equalsIgnoreCase(beanName)) {
System.out.println("MyBeanPostProcessor before方法拦截处理lazyResult");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("lazyResult".equalsIgnoreCase(beanName)) {
System.out.println("MyBeanPostProcessor after方法拦截处理lazyResult");
}
return bean;
}
}
BeanFactoryPostProcessor
可以针对整个Bean的工厂进行处理。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
接口中方法的参数中定义了一些方法:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.beans.factory.config;
import java.util.Iterator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.lang.Nullable;
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
void ignoreDependencyType(Class<?> var1);
void ignoreDependencyInterface(Class<?> var1);
void registerResolvableDependency(Class<?> var1, @Nullable Object var2);
boolean isAutowireCandidate(String var1, DependencyDescriptor var2) throws NoSuchBeanDefinitionException;
BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
Iterator<String> getBeanNamesIterator();
void clearMetadataCache();
void freezeConfiguration();
boolean isConfigurationFrozen();
void preInstantiateSingletons() throws BeansException;
}
我们可以根据getBeanDefinition方法找到我们定义的bean的BeanDefinition对象,然后就可以对定义的属性进行修改。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.beans.factory.config;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;
import org.springframework.lang.Nullable;
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
void setParentName(@Nullable String var1);
@Nullable
String getParentName();
void setBeanClassName(@Nullable String var1);
@Nullable
String getBeanClassName();
void setScope(@Nullable String var1);
@Nullable
String getScope();
void setLazyInit(boolean var1);
boolean isLazyInit();
void setDependsOn(@Nullable String... var1);
@Nullable
String[] getDependsOn();
void setAutowireCandidate(boolean var1);
boolean isAutowireCandidate();
void setPrimary(boolean var1);
boolean isPrimary();
void setFactoryBeanName(@Nullable String var1);
@Nullable
String getFactoryBeanName();
void setFactoryMethodName(@Nullable String var1);
@Nullable
String getFactoryMethodName();
ConstructorArgumentValues getConstructorArgumentValues();
default boolean hasConstructorArgumentValues() {
return !this.getConstructorArgumentValues().isEmpty();
}
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !this.getPropertyValues().isEmpty();
}
void setInitMethodName(@Nullable String var1);
@Nullable
String getInitMethodName();
void setDestroyMethodName(@Nullable String var1);
@Nullable
String getDestroyMethodName();
void setRole(int var1);
int getRole();
void setDescription(@Nullable String var1);
@Nullable
String getDescription();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
方法名字类似bean标签的属性。
如,setBeanClassName就对应bean标签中的class属性。
因此拿到BeanDefinition对象就可以手动修改bean标签中所定义的属性值。
(我们在XML中定义的bean标签,会被Spring解析为一个JavaBean,这个JavaBean就是BeanDefinition。)
注意,调用BeanFactoryPostProcessor的方法时,bean还没有实例化,此时bean刚被解析成BeanDefinition对象。
