xml的自动装配

82 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情

FactroyBean

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

<T> getObject():通过一个对象交给ioc容器来管理
​
Class<?> getObjectType():设置所提供对象的类型
​
boolean isSingleton():所提供的对象是否为单例

Factory

import com.sentiment.pojo.User;
import org.springframework.beans.factory.FactoryBean;
​
public class UserFactoryBean implements FactoryBean<User> {
​
    @Override
    public User getObject() throws Exception {
        return new User();
    }
​
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

配置文件

这里并不是返回UserFactoryBean的类对象,而是该类中getObject方法中返回的User()对象,较以往的工厂来说bean工厂省去了找工厂的过程直接找我们需要的对象。

<bean class="com.sentiment.factory.UserFactoryBean"></bean>

测试

@Test
public void factoryTest(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");
    User bean = ioc.getBean(User.class);
    System.out.println(bean);
}

输出结果

可以看到这里实例化了User对象,并输出了对应内容

基于xml的自动装配

根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值

三层架构

自动装配用到了三层架构,先了解了一下

controller

controller又叫web层、控制层、控制器,主要用于用户交互,接收和响应来自用户端的http请求,并且包含一些web功能

常见技术:cookie seesion jsp servlet listener filter

service

service层又叫业务层,主要用来处理逻辑,用于事务处理、日志管理、监控等

dao

dao层又叫持久层、mapper层、respotiry层,主要用于操纵数据库返回用户数组,前篇提到的mybatis就属于该层

技术:jdbc druid mybatis

三层架构的调用关系

controller -> service -> dao -> 操作数据库

数据返回关系

dao操作数据库数据 ->service -> controller

场景模拟

回到xml自动装配,感觉跟动态代理好像。。。

先写一个controller

public class UserController {
    private UserService userService;
​
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
​
    public void saveUser() {
        userService.saveUser();
    }
}

controller中定义了UserService并调用了他的saveUser()方法,所以在创建个UserService接口和实现类

UserService接口

public interface UserService {
    void saveUser();
}

实现类

public class UserServiceImpl implements UserService {
    private UserDao userDao;
​
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
​
    @Override
    public void saveUser() {
        userDao.saveUser();
    }
}

接着又调用了UserDao的saveUser所以继续创建接口和实现类

UserDao接口

public interface UserDao {
​
    void saveUser();
}

实现类

public class UserDaoImpl implements UserDao {
​
    @Override
    public void saveUser() {
        System.out.println("保存成功");
    }
}

最后通过配置文件自动装配,通过其中的set方法进行赋值

<bean id="userController" class="com.sentiment.controller.UserController">
    <property name="userService" ref="userService"></property>
</bean>
​
<bean id="userService" class="com.sentiment.service.iml.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>
​
<bean id="userDao" class="com.sentiment.dao.iml.UserDaoImpl"></bean>

测试

public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire.xml");
    UserController bean = ioc.getBean(UserController.class);
    bean.saveUser();
}

byType

在场景模拟中,用到的并不是自动装配,而是通过property标签手动设置了对应的属性值,所以这里就通过autowire标签的byType属性实现自动装配,若设置no或default属性则表示不会自动装配即使用默认值

    <bean id="userController" class="com.sentiment.controller.UserController" autowire="byType">
<!--        <property name="userService" ref="userService"></property>-->
    </bean>
​
    <bean id="userService" class="com.sentiment.service.iml.UserServiceImpl" autowire="byType">
<!--        <property name="userDao" ref="userDao"></property>-->
    </bean>
​
    <bean id="userDao" class="com.sentiment.dao.iml.UserDaoImpl"></bean>

此时在运行程序后,会通过byType自动匹配对应的类型属性赋值

但需要注意两个问题:

  1. 若删除userService的bean,则会爆空指针错误,即:当匹配不到对应的bean后,则会不自动装配使用默认值
  2. 由于是根据类型进行匹配所以当设置多个userService类型的bean,则会报错:NoUniqueBeanDefinitionException,也就是这样会匹配到了多个bean,无法执行

byName

根据bean的id名来进行bean的匹配。

上边提到当设置多个userService类型的bean后,则会报错,这时就可以使用byName因为他是根据bean的id进行匹配的,所以不管设置几个同类型bean,只要id唯一就能匹配到(而id是唯一标识,所以常规状态下不会出现id相同情况)