JavaConfig
我们通常使用 Spring 都会使用 XML 配置,随着功能以及业务逻辑的日益复杂,应用伴随着大量的 XML 配置文件以及复杂的 bean 依赖关系,使用起来很不方便。
一个很大的项目,把一个大的项目拆分多个子项目(10 个 20 个 左右)(分布式项目) , 若现在还用 xml 配置, 想吐了
在 Spring 3.0 开始,Spring 官方就已经开始推荐使用 Java 配置来代替传统的 XML 配置了,它允许开发者将 bean 的定义和 Spring 的配置编写到到 Java 类中,不过似乎在国内并未推广盛行。当 Spring Boot 来临,人们才慢慢认识到 Java 配置的优雅,但是,也仍然允许使用经典的 XML 方式来定义 bean 和 配置 Spring。其有以下优势:
- 面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分使用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的 @Bean 方法等。
- 减少或者消除 XML 配置。提供了一种纯 Java 的方式来配置与 XML 配置概念相似的 Spring 容器。
- 类型安全和重构友好。提供了一种类型安全的方法了来配置 Spring 容器,由于 Java 5 对泛型的支持,现在可以按类型而不是名称检索 bean,不需要任何的强制转换或者基于字符串的查找。
xml配置与java配置的区别
搭建一个基于 Maven 构建的项目 java-config-demo,添加如下依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
XML 方式配置 IoC
在 XML 配置文件中去配置这些 Bean 交给 Spring 管理。
<bean id="someBean" class="cn.xxxx.bean.SomeBean"/>
启动 Spring 读取该 XML 文件创建容器对象,从容器中获取 SomeBean 对象。
public class IoCTest {
@Test
public void testXmlConfig() {
ApplicationContext ctx = new ClassPathXmlApplicationContexnt("classpath:applicationContext.xml");
SomeBean someBean = ctx.getBean(SomeBean.class);
System.out.println(someBean);
}
}
JavaConfig 方式配置 IoC
JavaConfig 方式中使用注解彻底的替代 XML 文件,那么到底要怎么告诉 Spring 容器,bean 没有定义在 XML 文件中,而是定义在一个 Java 配置类中。
@Configuration :在类上贴该注解表示该类是 Spring 的配置类,具有applicationContext.xml 文件的作用。@Bean :在 Spring 的配置类的方法上贴该注解后,该方法返回的对象会交给 Spring 容器管理,替代 applicationContext.xml 中的 bean 标签。@ComponentScan :在 Spring 配置类上贴该注解表示开启组件扫描器,默认扫描当前配置类所在的包,也可以自己指定,替代 XML 配置中的 <context:component-scan /> 标签。AnnotationConfigApplicationContext :该类是 ApplicationContext 接口的实现类,该对象是基于 JavaConfig 的方式来运作的 Spring 容器。
定义一个配置类 替代之前的 XML 文件,类中定义方法,返回 bean 对象交给 Spring 管理。
@Configuration
public class JavaConfig {
@Bean
public SomeBean someBean() {
return new SomeBean();
}
}
加载配置类,启动 AnnotationConfigApplicationContext 容器对象,测试效果。
public class IoCTest {
@Test
public void testJavaConfig() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
SomeBean someBean = ctx.getBean(SomeBean.class);
System.out.println(someBean);
}
}
@Bean 注解中的属性 在 XML 配置 bean 的方式中,我们可以在 bean 标签中的 id,name,init-method,destroy-method,scope 等属性来完成对应的配置,在使用 JavaConfig 方式中我们也一样能通过相应的配置来完成同样的效果,这些效果大多封装到 @Bean 注解的属性中。@Bean 注解中的属性有以下:
- name:对应 bean 标签中的 name 属性,用于给 bean 取别名;
- initMethod:对应 bean 标签中的 init-method 属性,配置 bean 的初始化方法;
- destroyMethod:对应 bean 标签中的 destroy-method 属性,配置 bean 的销毁方法。
注意:在配置类的方式中有许多的默认规定,比如:
- bean 的 id 就是当前方法名;
- 配置多例则是在方法上添加
@Scope("prototype") 注解来实现,一般不用配,默认单例即可。
XML 方式配置 DI
<bean id="someBean" class="cn.xxx.bean.someBean">
<property name="otherBean" ref="otherBean"/>
</bean>
<bean id="otherBean" class="cn.xx.bean.OtherBean"/>
JavaConfig 方式配置 DI
在配置类方式中我们有两种方式可以完成依赖注入,无论是哪种方式,前提都是要先把 bean 交给 Spring 管理,然后在把 bean 注入过去后再使用 setter 方法设置关系。通用步骤:先把两个 bean 交给 Spring 管理。
@Bean
public SomeBean someBean() {
SomeBean someBean = new SomeBean();
return someBean;
}
@Bean
public OtherBean otherBean() {
return new OtherBean();
}
通过方法形参注入
把需要注入的 bean 对象作为参数传入到另一个 bean 的方法声明中,形参名称最好跟 bean 的 id 一致。在容器里面有的 bean,都可以用这种方式注入。
@Bean
public SomeBean someBean(OtherBean otherBean) {
SomeBean someBean = new SomeBean();
someBean.setOtherBean(otherBean);
return someBean;
}
调用方法注入
@Bean
public SomeBean someBean() {
SomeBean someBean = new SomeBean();
someBean.setOtherBean(otherBean());
return someBean;
}
原理:Spring 容器在调用实例方法时,根据方法返回对象类型,判断容器中是否已经存在该类型的实例对象,如果不存在则执行实例方法,将返回对象实例交给容器管理,如果该实例已经存在了,直接从容器中拿已经存在实例对象方法,不执行实例方法。
使用 IoC DI 注解简化配置
以上案例中,在配置类内部去定义方法返回 bean 对象交给 Spring 管理的方式存在一个问题,就是如果需要创建的 bean 很多的话,那么就需要定义很多的方法,会导致配置类比较累赘,使用起来不方便。以前可以通过注解简化 XML 配置,现在同样也可以通过注解简化 JavaConfig,这里需要使用到 @ComponentScan 注解,等价于之前 XML 配置的 <context:component-scan base-package="贴了 IoC DI 注解的类所在的包"/> 。
@Configuration
@ComponentScan
public class JavaConfig { }
若需要扫描的包不是配置类所在的包时,我们可以通过注解中的 value 属性来修改扫描的包。
注意:组件扫描的方式只能扫描我们自己写的组件,若某个 bean 不是我们写的(这个类的设计者不一定使用了spring ioc 注解将这个类交给spring容器管理),则还是要通过在配置类中定义方法来处理,两者是可以同时存在的。
Spring Test 方式加载配置类
首先在 pom.xml 添加如下依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.3</version>
<scope>test</scope>
</dependency>
JUnit4 的方式 基于 XML
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:XML文件路径")
public class IoCTest {
@Autowired
private SomeBean someBean;
@Test
public void test() {
System.out.println(someBean);
}
}
JUnit4 的方式 基于配置类
@ContextConfiguration 注解不仅支持 XML 方式启动 Spring 测试,也支持配置类的方式,配置 classes 属性来指定哪些类是配置类即可。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={配置类1.class, 配置类2.class, ...})
public class IoCTest {
@Autowired
private SomeBean someBean;
@Test
public void test() {
System.out.println(someBean);
}
}
JUnit5 的方式
注意测试类和测试方法都不用 public 修饰,测试类只需要贴 @SpringJUnitConfig 指定加载的配置即可。
@SpringJUnitConfig(配置类.class)
class IoCTest {
@Autowired
private SomeBean someBean;
@Test
void test() {
System.out.println(someBean);
}
}
配置类的导入
在 Spring 项目中一般都会有多个 Spring 的配置文件,分别配置不同的组件,最后关联到主配置文件中,该功能也是同样可以在配置类的方式中使用的。
XML 方式
<!-- 例如 mvc.xml 中导入 applicationContext.xml -->
<import resource="classpath:applicationContext.xml"/>
配置类方式
需要使用 @Import 来完成,指定导入的配置类。
@Configuration
@Import(OtherJavaConfig.class)
public class JavaConfig { ... }
@Configuration
public class OtherJavaConfig { ... }
@SpringJUnitConfig(classes = JavaConfig.class)
public class IoCTest { ... }
配置类导入 XML 配置
需要使用 @ImportResource 来完成,指定导入 XML 配置文件的路径。
@Configuration
@ImportResource("classpath:XML文件路径")
public class JavaConfig { ... }
@SpringJUnitConfig(classes = JavaConfig.class)
public class IoCTest { ... }
|