一、根据类型获取 bean

1、情景一

  • bean 对应的类没有实现任何接口

  • 根据 bean 本身的类型获取 bean

    • 测试:IOC 容器中同类型的 bean 只有一个

      结果:可以正常获取到 IOC 容器中的那个 bean 对象

    • 测试:IOC 容器中同类型的 bean 有多个

      结果:会抛出NoUniqueBeanDefinitionException异常,表示 IOC 容器中这个类型的 bean 有多个

    准备:

    测试程序 SpringTest:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class SpringTest {

    private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");

    @Test
    public void testSituation1() {
    EmpDao empDao = iocContainer.getBean(EmpDao.class);
    System.out.println("empDao = " + empDao);
    }

    IOC 容器中同类型的 bean 只有一个:

    1
    2
    3
    4
    5
    6
    7
    8
    package get.bean.situation1;

    import org.springframework.stereotype.Repository;

    @Repository
    public class EmpDao {
    }

    IOC 容器中同类型的 bean 有多个:

    1
    2
    3
    4
    5
    6
    7
    8
    package get.bean.situation1;

    import org.springframework.stereotype.Repository;

    @Repository
    public class EmpSubDao extends EmpDao {
    }

2、情景二

  • bean 对应的类实现了接口,这个接口也只有这一个实现类

    • 测试:根据接口类型获取 bean

    • 测试:根据类获取 bean

      结果:上面两种情况其实都能够正常获取到 bean,而且是同一个对象

    测试程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Test
    public void testSituation2() {
    // 情景:EmpDao是接口,EmpDaoImpl实现了EmpDao接口
    // 1.根据EmpDao接口类型获取bean
    get.bean.situation2.EmpDao empDao01 = iocContainer.getBean(get.bean.situation2.EmpDao.class);
    System.out.println("empDao01 = " + empDao01);

    // 2.根据EmpDaoImpl类获取bean
    EmpDaoImpl empDao02 = iocContainer.getBean(EmpDaoImpl.class);
    System.out.println("empDao02 = " + empDao02);

    }

3、情景三

  • 声明一个接口

  • 接口有多个实现类

  • 接口所有实现类都放入 IOC 容器

    • 测试:根据接口类型获取 bean

      结果:会抛出NoUniqueBeanDefinitionException异常,表示 IOC 容器中这个类型的 bean 有多个

    • 测试:根据类获取 bean

      结果:正常

    测试程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Test
    public void testSituation3() {
    // 情景3:EmpService是一个接口,EmpServiceImpl01、EmpServiceImpl02是两个实现类
    // 1.根据EmpService接口类型获取bean
    // EmpService empService = iocContainer.getBean(EmpService.class);
    // System.out.println("empService = " + empService);

    // 2.根据EmpServiceImpl01类获取bean
    EmpServiceImp01 empServiceImp01 = iocContainer.getBean(EmpServiceImp01.class);
    System.out.println("empServiceImp01 = " + empServiceImp01);

    // 3.根据EmpServiceImpl02获取bean
    EmpServiceImp02 empServiceImp02 = iocContainer.getBean(EmpServiceImp02.class);
    System.out.println("empServiceImp02 = " + empServiceImp02);
    }

4、情景四(动态代理)

  • 声明一个接口

  • 接口有一个实现类

  • 创建一个切面类,对上面接口的实现类应用通知

    • 测试:根据接口类型获取 bean

      结果:正常,可以获取到 bean

    • 测试:根据实现类获取 bean

    • 结果:会抛出NoSuchBeanDefinitionException

    接口:

    1
    2
    3
    4
    package get.bean.situation4;

    public interface EmpController {
    }

    实现类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package get.bean.situation4;

    import org.springframework.stereotype.Controller;

    @Controller
    public class EmpControllerImpl implements EmpController {

    public void showMessage() {

    }
    }

    创建一个环绕通知:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Aspect
    @Component
    public class Situation4Aspect {

    @Around("execution(* get.bean.situation4.EmpControllerImpl.showMessage())")
    public Object doAround(ProceedingJoinPoint joinPoint) {

    Object result = null;

    try {
    result = joinPoint.proceed(joinPoint.getArgs());
    } catch (Throwable throwable) {
    throwable.printStackTrace();
    }
    return result;
    }
    }

    测试程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Test
    public void testSituation4() {

    // 情景:EmpController是接口;EmpControllerImpl是实现类;对实现类应用了切面
    // 1.根据接口类型获取bean
    EmpController empController = iocContainer.getBean(EmpController.class);
    System.out.println("empController = " + empController);

    // 2.根据实现类类型获取bean
    EmpControllerImpl empController1 = iocContainer.getBean(EmpControllerImpl.class);
    System.out.println("empController1 = " + empController1);
    }

原因分析

  • 应用了切面后,真正放在 IOC 容器中的是代理类的对象
  • 目标类并没有被放到 IOC 容器中,所以根据目标类的类型从 IOC 容器中是找不到的

images

从内存分析的角度来说,IOC 容器中引用的是代理对象,代理对象引用的是目标对象。IOC 容器并没有直接引用目标对象,所以根据目标类本身在 IOC 容器范围内查找不到。

images

debug 查看代理类的类型:

images

5、情景五(cglib

  • 声明一个类

  • 创建一个切面类,对上面的类应用通知

    • 测试:根据类获取 bean

      结果:正常,可以获取到 bean

    测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void testSituation5() {

    // 情景:StuService是类;Situation5Aspect是切面,对StuService类应用切面
    // 根据StuService类本身的类型来获取bean
    StuService stuService = iocContainer.getBean(StuService.class);
    System.out.println("stuService = " + stuService);

    }

    原因分析

images

debug 查看实际类型:

images

二、自动装配

1、情景一

  • 目标 bean 对应的类没有实现任何接口

  • 根据 bean 本身的类型获取 bean

    • 测试:IOC 容器中同类型的 bean 只有一个

      结果:正常装配

    • 测试:IOC 容器中同类型的 bean 有多个

      结果:会抛出NoUniqueBeanDefinitionException异常,表示 IOC 容器中这个类型的 bean 有多个

2、情景二

  • 目标 bean 对应的类实现了接口,这个接口也只有这一个实现类

    • 测试:根据接口类型装配 bean

      结果:正常

    • 测试:根据类装配 bean

      结果:正常

3、情景三

  • 声明一个接口

  • 接口有多个实现类

  • 接口所有实现类都放入 IOC 容器

    • 测试:根据接口类型装配 bean

      结果:根据接口类型会找到多个符合的 bean,然后根据成员变量名作为 bean 的 id 进一步筛选,如果没有 id 匹配的,则会抛出 NoUniqueBeanDefinitionException 异常,表示 IOC 容器中这个类型的 bean 有多个

    • 测试:根据类装配 bean

      结果:正常

4、情景四(动态代理)

  • 声明一个接口

  • 接口有一个实现类

  • 创建一个切面类,对上面接口的实现类应用通知

    • 测试:根据接口类型装配 bean

      结果:正常

    • 测试:根据类装配 bean

      结果:此时获取不到对应的 bean,所以无法装配

5、情景五(cglib)

  • 声明一个类

  • 创建一个切面类,对上面的类应用通知

    • 测试:根据类装配 bean

      结果:正常