- IOC即控制反转,一种思想
- 将创建对象和对象之间的调用过程交给Spring管理
- 使用IOC的目的是为了降低耦合
项目中交给Spring的管理的类称之为Bean
Bean管理指的两个操作
- spring创建对象
- spring注入属性
依赖注入,即bean管理中的注入属性
DI是IOC中的注入属性的步骤,DI需要在创建对象的基础之上完成
-
使用xml创建bean
- 在spring的xml配置文件中使用bean标签创建bean
- bean标签的必要属性
- id/name:bean的唯一标识,通过这个标识找到bean
- class:类的全路径(包名+类名)
- 创建bean依赖对应类的无参构造方法完成创建,若没有无参构造方法将抛出
java.lang.NoSuchMethodException异常 - 示例项目相关代码:
<bean id="hello" class="top.ersut.spring.Hello"></bean>,- 示例中bean的唯一标识为'hello'对应的类为'top.ersut.spring5.Hello'
-
使用xml注入属性-字面量
-
通过setxxx方法注入
- 在spring的xml配置文件中使用bean标签的子标签property进行注入
- property标签的属性
- name:注入属性的名称
- ref:引用现有的bean进行注入
- value:直接赋值
- 注入属性依赖属性的set方法,如果没有方法将抛出
org.springframework.beans.NotWritablePropertyException异常 - 示例项目相关代码:
<bean id="student" class="top.ersut.spring.ioc.Student"> <!--注入属性--> <property name="name" value="wang"></property> </bean> -
通过有参构造方法注入
- 在spring的xml配置文件中使用bean标签的子标签constructor-arg进行注入
- constructor-arg常用属性:
- name:构造方法形参的名称
- ref:引用现有的bean进行注入
- value:直接赋值
- 示例项目相关代码:
<bean id="student" class="top.ersut.spring.ioc.Student"> <!--有参构造注入属性--> <constructor-arg name="name" value="ersut"></constructor-arg> </bean>
-
-
使用xml注入属性-bean
- 示例项目相关代码:
<bean id="student" class="top.ersut.spring.ioc.Student"> <!--注入属性--> <property name="teacher" ref="tea"></property> </bean> <bean id="tea" class="top.ersut.spring.ioc.Teacher"></bean>
- 示例项目相关代码:
-
使用xml注入属性-集合
- 集合对应的标签
- 在property标签的子标签内进行
- array标签对应数组
- 常用子标签:
- value:注入字面量值
- ref:注入bean
- 常用属性:
- bean:bean的唯一标识
- 常用属性:
- 代码示例:
<property name="array"> <array> <value>a</value> <value>b</value> <value>c</value> <value>d</value> <value>e</value> </array> </property> - 常用子标签:
- list标签对应List集合(数组使用list标签也可以)
- 常用子标签:与array标签一致
- 代码示例:
<property name="list"> <list> <value>a</value> <value>b</value> <value>a</value> <value>d</value> <value>e</value> </list> </property> - set标签对应Set集合
- 常用子标签:与array标签一致
- 代码示例:
<property name="set"> <list> <value>a</value> <value>b</value> <value>c</value> <value>e</value> <value>e</value> </list> </property> - map标签对应Map集合
- 常用子标签:
- entry:给map注入值
- 常用属性
- key:字面量形式的key值
- value:字面量形式的value值
- ref-key:bean形式的key值
- ref-value:bean形式的value值
- 常用属性
- entry:给map注入值
- 代码示例:
<property name="map"> <map> <entry key="97" value="a"></entry> <entry key="98" value="b"></entry> <entry key="99" value="c"></entry> <entry key="100" value="d"></entry> <entry key="101" value="e"></entry> </map> </property> - 常用子标签:
- 字面量注入:以上示例均为字面量注入示例项目
- bean注入:
- 数组注入
- 代码示例
<property name="array"> <array> <ref bean="user1"></ref> <ref bean="user2"></ref> <ref bean="user1"></ref> </array> </property> - List注入
- 代码示例
<property name="list"> <list> <ref bean="user2"></ref> <ref bean="user1"></ref> <ref bean="user1"></ref> </list> </property> - Set注入
- 代码示例
<property name="set"> <list> <ref bean="user2"></ref> <ref bean="user1"></ref> <ref bean="user2"></ref> </list> </property> - Map注入
- 代码示例
<property name="map"> <map> <entry key-ref="user1" value-ref="user2"></entry> <entry key-ref="user2" value-ref="user1"></entry> </map> </property> - 示例项目
- 数组注入
- 集合对应的标签
-
引入外部配置
- 添加名称空间
<beans xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">- 加载配置文件
<context:property-placeholder location="config.properties"/>- config.properties文件内容:
name=wang - 使用
<bean id="student" class="top.ersut.spring.ioc.Student"> <!--注入属性--> <property name="name" value="${name}"></property> </bean>
- bean有两种:一种是普通bean(bean类型与引用类一致),一种是工厂bean(引用类与bean类型不一致)
- 工厂bean可以定义一些复杂的bean,比如在不同的情况下返回不同的实现类
- FactoryBean有3个方法
- boolean isSingleton():是否为单利模式
- T getObject():返回创建的Bean实例,如果isSingleton方法返回ture那么该实例会Spring容器的单实例缓存池中。
- Class<?> getObjectType():返回该工厂创建bean的类型
- 示例项目,部分代码:
public class UserFactory implements FactoryBean<User> {
private String impl;
public void setImpl(String impl) {
this.impl = impl;
}
@Override
public User getObject() throws Exception {
User user = null;
if(Objects.equals(impl,"student")){
user = new Student();
} else if(Objects.equals(impl,"teacher")) {
user = new Teacher();
}
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
- Bean的作用域分为两种,一种是单实例,另一种是多实例
- 单实例是在加载配置文件时创建的bean,并放入bean单例缓存池
- 多实例是在获取bean时(getBean方法)创建的。
- 默认情况下是单实例的。
- 示例代码:
- 配置文件:
<bean id="beanDefaultScope" class="top.ersut.spring.ioc.BeanDefaultScope"></bean>- 运行代码:
@Test public void test(){ ApplicationContext applicationContext = new GenericXmlApplicationContext("bean.xml"); BeanDefaultScope beanDefaultScope1 = applicationContext.getBean("beanDefaultScope",BeanDefaultScope.class); BeanDefaultScope beanDefaultScope2 = applicationContext.getBean("beanDefaultScope",BeanDefaultScope.class); System.out.println("beanDefaultScope1:"+beanDefaultScope1); System.out.println("beanDefaultScope2:"+beanDefaultScope2); System.out.println("地址是否相等:"+(beanDefaultScope1==beanDefaultScope2)); }- 运行结果:
beanDefaultScope1:top.ersut.spring.ioc.BeanDefaultScope@294425a7 beanDefaultScope2:top.ersut.spring.ioc.BeanDefaultScope@294425a7 地址是否相等:true
- 示例代码:
- 设置为多实例作用域
- 通过bean标签的scope属性设置
- 可选项:
- prototype:多实例
- singleton:单实例(默认值)
- 可选项:
- 示例代码:
- 配置文件
<!--多实例--> <bean id="beanScopePrototype" class="top.ersut.spring.ioc.BeanScopePrototype" scope="prototype"></bean>- 运行代码
@Test public void test(){ ApplicationContext applicationContext = new GenericXmlApplicationContext("bean.xml"); BeanScopePrototype beanScopePrototype1 = applicationContext.getBean("beanScopePrototype",BeanScopePrototype.class); BeanScopePrototype beanScopePrototype2 = applicationContext.getBean("beanScopePrototype",BeanScopePrototype.class); System.out.println("beanScopePrototype1:"+beanScopePrototype1); System.out.println("beanScopePrototype2:"+beanScopePrototype2); System.out.println("地址是否相等:"+(beanScopePrototype1==beanScopePrototype2)); }- 运行结果
beanScopePrototype1:top.ersut.spring.ioc.BeanScopePrototype@9f116cc beanScopePrototype2:top.ersut.spring.ioc.BeanScopePrototype@12468a38 地址是否相等:false
- 通过bean标签的scope属性设置
- 示例项目
BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在每个Bean的创建过程中回调BeanPostProcessor中定义的两个方法。
源码如下:
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实例化(调用类的构造方法)
- 注入属性(DI)
- 调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization方法)
- 调用自定义初始化方法
- 调用BeanPostProcessor的初始化后方法(postProcessAfterInitialization方法)
- Bean可以使用了
- 上下文销毁时,调用自定义销毁方法
- 配置初始化方法
- 设置bean标签的属性init-method,其值为方法名
- 示例
<bean id="beanLifeCycle" class="top.ersut.spring.ioc.BeanLifeCycle" init-method="init"/>
- 配置销毁方法
- 设置bean标签的属性destroy-method,其值为方法名
- 示例
<bean id="beanLifeCycle" class="top.ersut.spring.ioc.BeanLifeCycle" destroy-method="destroy"/>
后置处理器:
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("3. 调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization方法)");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5. 调用BeanPostProcessor的初始化后方法(postProcessAfterInitialization方法)");
return bean;
}
}
Bean对应的类:
public class BeanLifeCycle {
public BeanLifeCycle() {
System.out.println("1. Bean实例化(调用类的构造方法)");
}
private String str;
public void setStr(String str) {
System.out.println("2. 注入属性(DI)");
this.str = str;
}
public void init(){
System.out.println("4. 调用自定义初始化方法");
}
public void destroy(){
System.out.println("7. 上下文销毁时,调用自定义销毁方法");
}
}
spring配置:
<bean id="beanLifeCycle" class="top.ersut.spring.ioc.BeanLifeCycle" init-method="init" destroy-method="destroy">
<property name="str" value="test"></property>
</bean>
<bean id="myBeanPostProcessor" class="top.ersut.spring.ioc.MyBeanPostProcessor"/>
测试方法:
@Test
void test() {
GenericXmlApplicationContext context = new GenericXmlApplicationContext("bean.xml");
BeanLifeCycle beanLifeCycle = context.getBean("beanLifeCycle",BeanLifeCycle.class);
//销毁\关闭上下文
context.close();
}
运行结果:
1. Bean实例化(调用类的构造方法)
2. 注入属性(DI)
3. 调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization方法)
4. 调用自定义初始化方法
5. 调用BeanPostProcessor的初始化后方法(postProcessAfterInitialization方法)
7. 上下文销毁时,调用自定义销毁方法
- 注解用来标记代码,这个标记是可编译器、运行期被读取的。
- 注解是JDK5引入的新特新
- 格式:@注解名称[(key=value,...)]
- 注解可以用在types(类、接口、枚举、注解类)、方法上、属性上,具体可以用在这三个那几个上是可控的
- 示例:
- 示例1:单元测试
@Test void world() { }- 示例2:子类重写父类的方法
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { }
- 简化xml配置
- @Component:适用于组件
- @Repository:适用于持久层
- @Service:适用于服务层
- @Controller:适用于控制层
- @Bean:一般在配置类中使用,用于方法上
以上4个注解功能一样,只是使用的地方不一样
代码示例1:
//可以设置bean的名称为userServer2
@Service("userServer2")
public class UserServer {
}代码示例2:
//如果不设置bean的名称,那么取类名将首字母小写作为Bean的名称,也就是userDao
@Repository
public class UserDao {
}使用@Configuration标记类使其成为配置类,这种方式是在代替xml配置文件
代码示例:
@Configuration
public class ProjectConfig {
}
使用@ComponentScan注解进行配置使注解生效,具体那些包下注解生效需要用到 属性 basePackages,他接收一个String数组,也就是说他可以设置多个包,代码示例:
@Configuration
@ComponentScan(basePackages = {"top.ersut.spring.ioc.dao","top.ersut.spring.ioc.server"})
public class ProjectConfig {
}-
@Autowired:根据属性的类型自动注入
- 可使用在属性上或者方法上
- 示例:根据类型注入
/**可以放在属性上*/ @Autowired private UserDao userDao; private UserDao userDao2; @Autowired public void setUserDao2(UserDao userDao2) { this.userDao2 = userDao2; }
- @Qualifier:这俩个配合使用可以根据Bean的名称进行自动注入
- 示例:根据Bean的名称注入
@Autowired @Qualifier("userDao") private UserDao userDao3;
-
@Resource:可以根据类型注入,也可以根据Bean的名称注入
- 示例1:根据类型注入
@Resource private UserDao userDao4;
- 示例2:根据Bean的名称注入
@Resource(name = "userDao") private UserDao userDao5;
-
@Value:普通属性注入值
- 示例:添加在属性上(也可以添加在set方法上此处不再做演示)
@Value("wang") private String name;
示例项目,部分代码:
-
UserDao
@Repository public class UserDao { @Value("wang") private String name; public String getName() { System.out.println("UserDao.getName()"); return name; } } -
UserServer
@Service("userServer2") public class UserServer { ... public void selectUserName(){ userDao.getName(); userDao2.getName(); userDao3.getName(); userDao4.getName(); userDao5.getName(); System.out.println("UserServer.selectUserName"); } } -
测试方法
@Test void selectUserName() { //实现类使用 AnnotationConfigApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(ProjectConfig.class); UserServer userServer = context.getBean("userServer2", UserServer.class); userServer.selectUserName(); } -
输出内容
UserDao.getName() UserDao.getName() UserDao.getName() UserDao.getName() UserDao.getName() UserServer.selectUserName
在类上添加@Scope注解,其value属性设置为prototype
示例项目(Bean的作用域注解版),关键代码:
@Component
@Scope("prototype")
public class BeanScopePrototype {
}
- 初始化方法
- 在方法上添加@PostConstruct注解那么这个方法将变成初始化方法
- 代码示例:
@PostConstruct public void init(){ System.out.println("4. 调用自定义初始化方法"); }
- 摧毁方法
- 在方法上添加@PreDestroy注解那么这个方法将变成摧毁方法
- 代码示例:
@PreDestroy public void destroy(){ System.out.println("7. 上下文销毁时,调用自定义销毁方法"); }
- 示例项目(Bean的生命周期注解版),部分代码:
- 配置类
@Configuration @ComponentScan(basePackages = "top.ersut.spring") public class ProjectConf { }
- MyBeanPostProcessor
- BeanPostProcessor接口对于每个bean都生效
@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("3. 调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization方法)"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("5. 调用BeanPostProcessor的初始化后方法(postProcessAfterInitialization方法)"); return bean; } }
- 测试用例
@Test void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProjectConf.class); BeanLifeCycle beanLifeCycle = context.getBean(BeanLifeCycle.class); //销毁\关闭上下文 context.close(); }- 输出内容
3. 调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization方法) 5. 调用BeanPostProcessor的初始化后方法(postProcessAfterInitialization方法) 1. Bean实例化(调用类的构造方法) 2. 注入属性(DI) 3. 调用BeanPostProcessor的预初始化方法(postProcessBeforeInitialization方法) 4. 调用自定义初始化方法 5. 调用BeanPostProcessor的初始化后方法(postProcessAfterInitialization方法) 7. 上下文销毁时,调用自定义销毁方法- 疑问:与Bean的生命周期注解版的输出结果相比,注解版的输出结果多了两行这是为什么
- 因为配置类也是一个bean所以在加载配置类的时候执行了一次后置处理器(MyBeanPostProcessor)