Spring-FactoryBean

1.FactoryBean

Spring 有两种类型 Bean,一种是普通 Bean,另外一种是工厂 Bean(FactoryBean)

  • 普通 Bean:在配置文件中定义的 Bean 类型就是返回类型

  • 工厂 Bean:在配置文件中定义的 Bean 类型可以和返回类型不一致

上述的例子都是普通 Bean 的类型,那么工厂 Bean 该怎么实现呢

1)创建类,实现 FactoryBean 接口,使其作为一个工厂 Bean

2)实现接口中的方法,在实现方法中定义返回的 Bean 类型

 
 
  1. public class MyFactoryBean implements FactoryBean<Course> {

  2.    @Override

  3.    public Course getObject() throws Exception {

  4.        Course course = new Course();

  5.        course.setCname("CourseName");

  6.        return course;

  7.   }

  8.    @Override

  9.    public Class<?> getObjectType() {

  10.        return null;

  11.   }

  12. }

3)在 Spring 配置文件中进行配置

<bean id="myFactoryBean" class="com.zking.spring.factorybean.MyBean"></bean>

由于是 FactoryBean,所以再通过上下文获取时,需要使用实现 FactoryBean 时传入的泛型类型进行接收

 
  1. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean5.xml");

  2. Course course = applicationContext.getBean("myFactoryBean", Course.class);

如果仍然使用配置文件中定义的 Bean 类型,则会报错

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'myBean' is expected to be of type 'com.zking.spring.factorybean.MyBean' but was actually of type 'com.zking.spring.collectiontype.Course'

2.Bean 作用域和生命周期

2.1 Bean 作用域

在 Spring 里面,可以设置创建 Bean 的实例是单实例还是多实例,默认情况下是单实例

<bean id="book" class="com.zking.spring.collectiontype.Book"></bean>

测试

 
 
  1. ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");

  2. Book book1 = context.getBean("book", Book.class);

  3. Book book2 = context.getBean("book", Book.class);

  4. System.out.println(book1 == book2); // true 表示是同一个对象,证明默认情况下是单实例

如何设置单实例多实例?

在 Spring 配置文件中 bean 标签里scope属性用于设置单实例还是多实例

  • 1)singleton,单实例,默认情况下不写也是它

  • 2)prototype,多实例

  • <bean id="book2" class="com.zking.spring.collectiontype.Book" scope="prototype"></bean>

测试

 
 
  1. Book book3 = context.getBean("book2", Book.class);

  2. Book book4 = context.getBean("book2", Book.class);

  3. System.out.println(book3 == book4); // false 表示不是同一个对象,证明scope为prototype时是多实例

singletonprototype的区别

singletonprototype除了单实例和多实例的差别之外,还有以下区别

  • 设置scope值是singleton时,加载 Spring 配置文件时就会创建单实例对象

  • 设置scope值是prototype时,加载 Spring 配置文件时不会创建对象,而是在调用getBean方法时创建多实例对象

 scope的其他值

scope的属性值除了singletonprototype之外,其实还有一些属性值,如

  • request,每个request创建一个新的 bean

  • session,同一session中的 bean 是一样的

不过这两个属性值使用非常少,了解即可

2.2 Bean 生命周期

生命周期:从对象创建到对象销毁的过程

Bean 生命周期

  1. 通过构造器创建 Bean 实例(无参构造)

  2. 为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)

  3. 调用 Bean 的初始化方法(需要进行配置初始化方法)

  4. Bean 就可以使用了(对象获取到了)

  5. 当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)

代码演示

 
 
  1. public class Orders {

  2.    //无参数构造

  3.    public Orders() {

  4.        System.out.println("第一步 执行无参数构造创建bean实例");

  5.   }

  6.    private String oname;

  7.    public void setOname(String oname) {

  8.        this.oname = oname;

  9.        System.out.println("第二步 调用set方法设置属性值");

  10.   }

  11.    //创建执行的初始化的方法

  12.    public void initMethod() {

  13.        System.out.println("第三步 执行初始化的方法");

  14.   }

  15.    //创建执行的销毁的方法

  16.    public void destroyMethod() {

  17.        System.out.println("第五步 执行销毁的方法");

  18.   }

  19. }

Spring 配置文件中的配置、

 
 
  1. <bean id="orders" class="com.zking.spring.bean.Orders" init-method="initMethod"

  2.      destroy-method="destroyMethod">

  3.    <property name="oname" value="手机"></property>

  4. </bean>

测试

 
 
  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");

  2. Orders orders = context.getBean("orders", Orders.class);

  3. System.out.println("Step4.获取创建Bean实例对象.");

  4. System.out.println(orders);

  5. // 手动销毁Bean实例

  6. context.close();

执行结果

 
 
  1. 第一步 执行无参数构造创建bean实例

  2. 第二步 调用set方法设置属性值

  3. 第三步 执行初始化的方法

  4. 第四步 获取创建bean实例对象

  5. com.zking.spring.bean.Orders@c81cdd1

  6. 第五步 执行销毁的方法

Spring 中 Bean 更加完整的生命周期其实不止上述 5 步,另外还有 2 步操作叫做 Bean 的后置处理器

2.3 Bean 后置处理器

加上 Bean 后置处理器,Bean 生命周期如下

  1. 通过构造器创建 Bean 实例(无参构造)

  2. 为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)

  3. 把 Bean 的实例传递给 Bean 后置处理器的postProcessBeforeInitialization方法

  4. 调用 Bean 的初始化方法(需要进行配置初始化方法)

  5. 把 Bean 的实例传递给 Bean 后置处理器的postProcessAfterInitialization方法

  6. Bean 就可以使用了(对象获取到了)

  7. 当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)

代码演示

1)创建类,实现接口BeanPostProcessor,创建后置处理器

 
 
  1. public class MyBeanPost implements BeanPostProcessor {

  2.    @Override

  3.    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

  4.        System.out.println("在初始化之前执行的方法");

  5.        return bean;

  6.   }

  7.    @Override

  8.    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

  9.        System.out.println("在初始化之后执行的方法");

  10.        return bean;

  11.   }

  12. }

2)Spring 配置文件中配置后置处理器

 
 
  1. <!--配置后置处理器,会为当前配置文件中所有bean添加后置处理器-->

  2. <bean id="myBeanPost" class="com.zking.spring.bean.MyBeanPost"></bean>

执行结果

 
 
  1. 第一步 执行无参数构造创建bean实例

  2. 第二步 调用set方法设置属性值

  3. 在初始化之前执行的方法

  4. 第三步 执行初始化的方法

  5. 在初始化之后执行的方法

  6. 第四步 获取创建bean实例对象

  7. com.zking.spring.bean.Orders@289d1c02

  8. 第五步 执行销毁的方法

3.注解方式

3.1 什么是注解

  • 注解是一种代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)

  • 注解作用:在类上面,方法上面,属性上面

  • 注解目的:简化 XML 配置

3.2 创建对象

  • @Component

  • @Service

  • @Controller

  • @Repository

上面四个注解功能是一样的,都可以用来创建 Bean 实例

1)引入依赖

spring-aop.xxx.jar

2)开启组件扫描

 
 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <!--引入context名称空间-->

  3. <beans xmlns="http://www.springframework.org/schema/beans"

  4.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  5.       xmlns:context="http://www.springframework.org/schema/context"

  6.       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

  7.                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  8.    <!--开启组件扫描:多个包用逗号隔开-->

  9.    <context:component-scan base-package="com.zking.spring.dao,com.zking.spring.service"></context:component-scan>

  10. </beans>

3)创建类,在类上添加创建对象注解

 
 
  1. /**

  2. * value可省略,默认值为类名首字母小写

  3. */

  4. @Component(value = "userService")

  5. public class UserService {

  6.    public void add(){

  7.        System.out.println("UserService add...");

  8.   }

  9. }

3.3 组件扫描配置

设置扫描
  • use-default-filters表示现在不使用默认filter,自己配置filter

  • include-filter设置扫描哪些内容

 
 
  1. <context:component-scan

  2.            base-package="com.zking.spring" use-default-filters="false">

  3.        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>

  4.    </context:component-scan>

设置不扫描
  • 配置扫描包下所有内容

  • exclude-filter设置不扫描哪些内容

  •  
       
    1. <context:component-scan

    2.            base-package="com.zking.spring">

    3.        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>

    4.    </context:component-scan>

 

4.属性注入

  • @Autowired根据属性类型进行自动装配

  • @Qualifier根据属性名称进行注入,需要和@Autowired一起使用

  • @Resource可以根据类型和名称注入

  • @Value根据普通类型注入

4.1 @Autowired

1)创建 Service 和 Dao 对象,在 Service 和 Dao 类上添加创建对象注解

 
 
  1. public interface UserDao {

  2.    void add();

  3. }

  4. @Repository

  5. public class UserDaoImpl implements UserDao{

  6.    @Override

  7.    public void add() {

  8.        System.out.println("UserDaoImpl add...");

  9.   }

  10. }

  11. @Service

  12. public class UserService {

  13.    public void add() {

  14.        System.out.println("UserService add...");

  15.   }

  16. }

2)在 Service 类中添加 Dao 类型属性,在属性上面使用注解注入 Dao 对象

 
 
  1. @Service

  2. public class UserService {

  3.    @Autowired

  4.    private UserDao userDao;

  5.    public void add() {

  6.        System.out.println("UserService add...");

  7.        userDao.add();

  8.   }

  9. }

因为@Autowired是根据属性类型进行注入的,如果 UserDao 的实现类不止一个,比如新增一个 UserDaoImpl2 类

 
 
  1. @Repository

  2. public class UserDaoImpl2 implements UserDao {

  3.    @Override

  4.    public void add() {

  5.        System.out.println("UserDaoImpl2 add...");

  6.   }

  7. }

那么此时测试程序就会报错

 
 
  1. Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2

  2. ...

  3. Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2

  4. ...

大概意思就是说,主程序抛出了一个UnsatisfiedDependencyException不满足依赖异常,嵌套异常是NoUniqueBeanDefinitionExceptionBean定义不唯一异常,预期匹配单个 Bean 但是找到了两个 Bean

此时想要指定装配某一个实现类,就需要用到@Qualifier注解

4.2 @Qualifier

书接上回,如果我们想要从多个实现类中装配具体某一个实现类,可以这么写

 
 
  1. @Autowired

  2. @Qualifier(value = "userDaoImpl")

  3. private UserDao userDao;

其中value值为具体的实现类上配置的注解中value

 
 
  1. @Repository

  2. public class UserDaoImpl implements UserDao{

  3.    @Override

  4.    public void add() {

  5.        System.out.println("UserDaoImpl add...");

  6.   }

  7. }

  8. @Repository

  9. public class UserDaoImpl2 implements UserDao {

  10.    @Override

  11.    public void add() {

  12.        System.out.println("UserDaoImpl2 add...");

  13.   }

  14. }

由于上述例子中,我们没有对@Repository配置相应的value,所以默认为首字母小写的类名

如果想使用 UserDaoImpl2 类,则

 
 
  1. @Autowired

  2. @Qualifier(value = "userDaoImpl2")

  3. private UserDao userDao;

如果指定名称有误,即不存在名称为value对应的类,则会报NoSuchBeanDefinitionException异常,即找不到对应类

 
 
  1. Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDaoImpl1)}

  2. ...

  3. Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDaoImpl1)}

4.3 @Resource

根据类型注入

 
 
  1. @Resource

  2. private UserDao userDao;

根据名称注入

 
 
  1. @Resource(name = "userDaoImpl")

  2. private UserDao userDao;

需要注意的是@Resource注解所在包为javax.annotation即 Java 扩展包,所以 Spring 官方不建议使用该注解而推崇@Autowired@Qualifier注解

4.4 @Value

上述注解都是对对象类型的属性进行注入,如果想要装配普通类型属性,如基本数据类型及其包装类等,则可以需要使用@Value注解;

 
 
  1. @Value(value = "${user.name}")

  2. private String name;

  3. @Value(value = "100")

  4. private Integer age;

  5. @Value(value = "200.0d")

  6. private Double length;

  7. @Value(value = "true")

  8. private boolean isOk;

  9. @Value(value = "0,a,3,6,test")

  10. private String[] arrs;

@Value(value = "${user.name}"):需要加载外部属性资源文件

4.5 完全注解开发

创建配置类,替代 XML 配置文件

 
 
  1. //标注当前类为配置类

  2. @Configuration

  3. //开启组件扫描

  4. @ComponentScan({"com.zking.spring"})

  5. //引入外部属性文件

  6. @PropertySource({"classpath:config.properties"})

  7. public class SpringConfig {

  8. }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值