9、IOC之bean

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的生命周期

  1. 实例化,调用无参构造
  2. 实例注入,调用set方法
  3. 初始化之前的操作(由后置处理器负责)
  4. 初始化,需要通过bean的init-method属性指定初始化方法
  5. 初始化之后的操作(由后置处理器负责)
  6. 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进行匹配
        • 能够找到:执行装配
        • 找不到:装配失败

④@Autowired工作流程

在这里插入图片描述

注意:若IOC容器中没有任何一个类型匹配bean,此时抛出异常:NoSuchBeanDefinitionException

在@Autowired注解中有个required属性,默认值为true,要求必须完成自动装配

可以将required设置为false,此时能装配则装配,无法装配则使用属性的默认值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值