Spring深度解析

时间:2021-08-16
本文章向大家介绍Spring深度解析,主要包括Spring深度解析使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Spring应用1


XML配置中的手动注入方式

`

@Service
public class TestService {
MXService mXService;

public MXService getMxService() {
	return mXService;
}

public void setMXService(MXService mxService) {
	this.mXService = mxService;
}
}`

在XML配置文件中基于要相应的为MXService进行手动注入配置:

<bean name="TestService" class="com.abc.TestService">
	<property name="MXService"> 注意这里的要和set方法名后的一样
		<ref bean="MXService"></ref>
	</property>
</bean>
<bean name="MXService" class="com.abc.service.MXService"></bean>

这种方式称为手动注入方式,较为复杂

XML配置中自动装配

根据官网文档,XML中的Autowiring Modes分为四种,no,byName,byType,constructor
·注意这个概念仅仅针对于XML的配置方式,如果为javaConfig方式进行配置则没有该装配模型。

  • @Autowired注解
    现根据类型找,找不到再根据名字找,其实这不是一种自动注入模型,如果是的话那么一种方式找不到就会报错,这也是手动装配的一种,在源码中的体现为并不对AUTO_XXX进行判断。

  • XML开启自动装配

<beans xmlns="http://www.springframework.org/schema/beans" ....... default-autowire="byName" >
· 值得注意的是,在byType时只要有set方法就可以,set后面的名字跟什么无所谓,但是当byName时,set方法后面跟的名字就必须为XML中给这个要被注入的Bean的BeanName。并且根据设定的方式找不到就找不到了,不会再根据另一种方式寻找。

@Autowired注解和@Resource有什么区别

首先每个地方都会介绍:@Autowired先根据type找,再根据name找、@Resouce反之。
不同之处在于@Autowired是spring包下面的,@Resource是javax包下面的。
并且@Autowired的处理经过AutowiredAnnotationBeanPostProcessor,
@Resource是由CommonAnnotationBeanPostrProcessor处理的。

两个类实现同一个接口从而XML中byType报错

在XML中byType自动装配时如果两个Bean类型一样就不知道装什么了,直接报错

  • 但是@Autowired会根据要注入的名字来进行寻找,并且不需要set方法

`

public class TestService {

@Autowired
MX MXService2;

public MX getMxService() {
	return MXService2;
}

public void setMXService(MX mxService) {
	this.MXService2 = mxService;
}`

此时根据名字“MXService2”找到了MXService2而不是MXService1来进行注入

@Lookup注解

在使用Spring时,可能会遇到这种情况:一个单例的Bean依赖另一个非单例的Bean。如果简单的使用自动装配来注入依赖,就可能会出现一些问题,如下所示:

单例的Class A

@Component
public class ClassA {
 @Autowired
 private ClassB classB;
 
 public void printClass() {
  System.out.println("This is Class A: " + this);
  classB.printClass();
 }
}

非单例的Class B

@Component
@Scope(value = SCOPE_PROTOTYPE)
public class ClassB {
  public void printClass() {
    System.out.println("This is Class B: " + this);
  }
}

这里Class A采用了默认的单例scope,并依赖于Class B, 而Class B的scope是prototype,因此不是单例的,这时候跑个测试就看出这样写的问题:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {ClassA.class, ClassB.class})
public class MyTest {
  @Autowired
  private ClassA classA;
 
  @Test
  public void simpleTest() {
    for (int i = 0; i < 3; i++) {
      classA.printClass();
    }
  }
}

输出的结果是:

This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79
This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79
This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79

可以看到,两个类的Hash Code在三次输出中都是一样。Class A的值不变是可以理解的,因为它是单例的,但是Class B的scope是prototype却也保持Hash Code不变,似乎也成了单例?

产生这种的情况的原因是,Class A的scope是默认的singleton,因此Context只会创建Class A的bean一次,所以也就只有一次注入依赖的机会,容器也就无法每次给Class A提供一个新的Class B。

不那么好的解决方案

要解决上述问题,可以对Class A做一些修改,让它实现ApplicationContextAware。

@Component
public class ClassA implements ApplicationContextAware {
  private ApplicationContext applicationContext;
 
  public void printClass() {
    System.out.println("This is Class A: " + this);
    getClassB().printClass();
  }
 
  public ClassB getClassB() {
    return applicationContext.getBean(ClassB.class);
  }
 
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }
}

这样就能够在每次需要到Class B的时候手动去Context里找到新的bean。再跑一次测试后得到了以下输出:

This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@31206beb
This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@3e77a1ed
This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@3ffcd140

可以看到Class A的Hash Code在三次输出中保持不变,而Class B的却每次都不同,说明问题得到了解决,每次调用时用到的都是新的实例。

但是这样的写法就和Spring强耦合在一起了,Spring提供了另外一种方法来降低侵入性。

@Lookup

Spring提供了一个名为@Lookup的注解,这是一个作用在方法上的注解,被其标注的方法会被重写,然后根据其返回值的类型,容器调用BeanFactory的getBean()方法来返回一个bean。

@Component
public class ClassA {
  public void printClass() {
    System.out.println("This is Class A: " + this);
    getClassB().printClass();
  }
 
  @Lookup
  public ClassB getClassB() {
    return null;
  }
}

可以发现简洁了很多,而且不再和Spring强耦合,再次运行测试依然可以得到正确的输出。
被标注的方法的返回值不再重要,因为容器会动态生成一个子类然后将这个被注解的方法重写/实现,最终调用的是子类的方法。

原文地址:https://www.cnblogs.com/moxi-moxi/p/15148092.html