9、Bean
1、bean的作用域
<!--
scope默认是单例,可以选择prototype多例
scope="singleton|prototype"
singleton:单例,表示获取该bean所对应的对象都是同一个
prototype:多例,表示获取该bean所对应的对象都不是同一个
-->
<bean id="student" class="com.zylai.spring.pojo.Student" scope="prototype">
<property name="sid" value="1001"/>
<property name="sname" value="张三"/>
</bean>
2、bean的生命周期
- 实例化,调用无参构造
- 实例注入,调用set方法
- 初始化之前的操作(由后置处理器负责)
- 初始化,需要通过bean的init-method属性指定初始化方法
- 初始化之后的操作(由后置处理器负责)
- IOC容器关闭时销毁
3、bean的后置处理器
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口, 且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容 器中所有bean都会执行
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之前执行
System.out.println("MyBeanPostProcessor-->后置处理postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之后执行
System.out.println("MyBeanPostProcessor-->后置处理postProcessAfterInitialization");
return bean;
}
}
在IOC容器中配置后置处理器:
<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.atguigu.spring.process.MyBeanProcessor"/>
过程:
1、实例化,调用无参构造
* 2、实例注入,调用set
* 3、后置处理的postProcessBeforeInitialization方法
* 4、初始化,需要通过bean的init-method属性指定初始化方法
* 使用bean
* 5、后置处理的postProcessAfterInitialization方法
* 6、IOC容器关闭时销毁,需要使用bean的destroy-method属性指定销毁方法
*
* bean的作用域是单例时,在创建IOC容器时,bean实例就会被创建
* 作用域是多例时,只有在获取bean实例时才会被创建
*
* bean的后置处理器会在生命周期的初始化前后添加额外的操作,
* 需要实现BeanPostProcessor接口,
* 且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,
* 而是针对IOC容器中所有bean都会执行
4、FactoryBean
概念
FactoryBean是Spring提供的一种整合第三方框架的常用机制。
和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。
通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
一句话:IOC容器会创建工厂bean getObject方法返回的实例类型,不会去创建工厂bean的实例。这样我们直接从ioc容器中获取工厂创建的实例对象
实现FactoryBean接口
接口中的三个方法:
getObject():返回一个对象给IOC容器
getObjectType():设置所提供对象的类型
isSingleton():所提供的对象是否为单例
当把FactoryBean的实现类配置为bean时,会将当前类中的getObject方法返回的对象交给IOC容器管理(类似于setter注入,bean给value赋值之后,自动调用set方法给属性赋值)
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
public class FactoryTest {
@Test
public void test(){
// 在配置文件中只需要配置FactoryBean即可
// 当把FactoryBean的实现类配置为bean时,
// 真正交给IOC容器管理的对象,是FactoryBean工厂中getObject方法返回的对象
// 也就是说,省略了传统的工厂模式从工厂实例中获取产品的步骤,
// 而是直接把工厂的产品交给了ioc容器管理
// 另外,还可以设置是否为单例
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
}
}
5、自动装配⭐️
概念:
根据指定的策略,在IOC容器中匹配某个bean,自动为为bean中的类类型的属性或者接口类型的属性赋值
实现:
可以通过bean标签的autowire属性设置自动装配的策略
自动装配的策略:
no,default:表示不装配,即bean中的属性不会自动匹配某个bean为某个属性赋值
byType:根据赋值的属性的类型,在IOC容器中匹配某个bean为属性赋值
异常情况:
若IOC中一个类型都匹配不上:属性就不会装配,使用默认值
若通过类型找到了多个类型匹配的bean,此时会抛出异常:NoUniqueBeanDefinitionException
总结:当使用ByType实现自动装配时,IOC容器中有且仅有一个类型匹配的bean能够为属性赋值
byName:将要赋值的属性的属性名作为bean的id在IOC容器中匹配某个bean,为属性赋值
总结:一般使用byType。特殊情况下:当类型匹配的bean有多个时,此时可以使用byName实现自动装配
<?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 class="com.zylai.spring.controller.UserController"
autowire="byType">
<!--<property name="userService" ref="userService"/>-->
</bean>
<bean id="userService"
class="com.zylai.spring.service.impl.UserServiceImpl"
autowire="byType">
<!--<property name="userDao" ref="userDao"/>-->
</bean>
<bean id="userDao" class="com.zylai.spring.dao.impl.UserDaoImpl"></bean>
</beans>
6、基于注解管理bean
1、注解的理解
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测 到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。
2、扫描
Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注 解进行后续操作。
3、导入依赖
<dependencies>
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies
4、标记组件的常用注解
@Component:将类标识为普通组件
@Controller:将类标识为控制层组件
@Service:将类标 识为业务层组件
@Repository:将类标识为持久层组件
这四个注解本质和功能上完全一样,后面三个相当于Component改了个名字,但是对于开发人员便于理解
注意:
- 在service层和dao层,注解应该标识在接口的实现类上(因为注解把spring容器中的bean进行实例化,等同于new操作,来操作方法,属性,而接口不能)
- 加了注解的类在IOC容器中的默认id为类名的小驼峰
<!--
context:exclude-filter:排除扫描
type:设置排除扫描的方式
type="annotation|assignable"
annotation:根据注解的类型进行排除,expression需要设置排除的注解的全类名
assignable:根据类的类型进行排除,expression需要设置排除的类的全类名
context:include-filter:包含扫描
注意:需要在context:component-scan标签中设置use-default-filters="false"
use-default-filters="true"(默认),所设置的包下所有的类都需要扫描,此时可以使用排除扫描
use-default-filters="false",所设置的包下所有的类都不需要扫描,此时可以使用包含扫描
-->
<!--扫描组件-->
<context:component-scan base-package="com.atguigu.spring"></context:component-scan>
<!--<context:include-filter type="assignable"
expression="com.atguigu.controller.UserController"/>-->
5、组件所对应的bean的id
使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用 注解后,每个组件仍然应该有一个唯一标识。
默认情况
类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。
自定义bean的id
可通过标识组件的注解的value属性设置自定义的bean的id @Service(“userService”)
7、基于注解的自动装配
场景: 基于xml的自动装配 在UserController中声明UserService对象 在UserServiceImpl中声明UserDao对象
1、@Autowired注解
① 在成员变量上直接标记@Autowired注解即可完成自动装配,不需要提供setXxx()方法。以后我们在项 目中的正式用法就是这样。
@Controller
public class UserController {
@Autowired
private UserService userService;
public void saveUser(){
userService.saveUser();
}
}
②@Autowired注解可以标记在构造器和set方法上
@Autowired
public UserController(UserService userService){
this.userService = userService;
}
③注入的方式
- 首先根据所需要的组件类型到IOC容器中查找
- 能够找到唯一的bean:直接执行装配 (byType)
- 如果完全找不到匹配这个类型的bean:装配失败
- 和所需类型匹配的bean不止一个
- 没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id(byName)进行 匹配
- 能够找到:执行装配
- 找不到:装配失败
- 使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配
- 能够找到:执行装配
- 找不到:装配失败
- 没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id(byName)进行 匹配
④@Autowired工作流程
注意:若IOC容器中没有任何一个类型匹配bean,此时抛出异常:NoSuchBeanDefinitionException
在@Autowired注解中有个required属性,默认值为true,要求必须完成自动装配
可以将required设置为false,此时能装配则装配,无法装配则使用属性的默认值