Spring是从两个角度来实现自动化装配:
- 组件扫描(component scanning) : Spring会自动发现应用上下文中所创建的bean
- 自动装配(autowiring) : Spring自动满足bean之间的依赖
文章目录
1.组件扫描
1.1Java配置类方式-配置组件扫描
问题: 这个类Spring是如何发现该类的?--> 参考使用Java配置类创建Spring容器(上下文)
在 Spring| 上下文和Bean的生命周期 一文中已经介绍了Spring基于配置类生成上下文的例子,这里同样是使用基于配置类来构建上下文,与之前不同的是,我们的Bean配置是通过扫描包的方式自动生成,而不是使用Bean注解来配置了.
A.配置组件扫描包
这里我讨论的是Spring强推的使用Java配置类来配置组件扫描的方式:类似如下代码
// @Named
@Component
public class Role {
private String id;
private String name;
private String desc;
//省略get,set方法
}
package com.yveshe.chapter2.bean;
public interface ScanPackageInfoInterface {
}
@Configuration
@ComponentScan(basePackageClasses = com.yveshe.chapter2.bean.ScanPackageInfoInterface.class)
public class SpringContextConf {
}
public class JavaConfComponetScanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(com.yveshe.chapter2.contextconf.javaconf.SpringContextConf.class);
com.yveshe.chapter2.bean.Role role = context.getBean(com.yveshe.chapter2.bean.Role.class);
context.close();
System.out.println(JsonUtil.toJson(role));
}
}
/**输出结果:
{
"age":18,
"name":"yveshe-JavaConfComponetScanTest",
"role":{
"desc":"拥有整个系统的管理权限",
"id":"4d53a9ab-e858-4369-8240-46dd497ace3e",
"name":"系统管理员"
}
}
**/
@Component
申明该类为组件类,允许Spring管理
@Configuration
申明该类是配置类
@ComponentScan
配置扫描路径(支持String类型的包名配置和Class类型的类所在包下)
这里主要讲解的是@ComponentScan注解的使用,默认的方式是可以配置String类型的basePackages,但是考虑到代码的重构,建议大家考虑在包中创建一个用来进行扫描的空标记接口(markerinterface) ,使用basePackageClasses参数的方式来配置扫描包.,比如我这里配置的ScanPackageInfoInterface就是一个空的接口
ComponentScan注解相关参数如下,我们可以发现常用的支持String类型的包名配置,和Class类型的配置,也支持配置多个包:
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
B.配置生成Bean的Id
Spring应用上下文中所有的bean都会给定一个ID。当没有指定ID的时候,默认的是类名将首字母小写后的结果,比如 Role 类的Id默认为 role
// @Named("role")
@Component("role")
public class Role {
private String id;
private String name;
private String desc;
// 省略 get,set
}
如果用户需要指定Bean的Id,可以通过主机@Component注解的value来设置,也可以使用Java依赖注入规范(Java Dependency Injection) 中所提供的@Named注解来为bean设置ID.Spring支持将@Named作为@Component注解的替代方案。 两者之间有一些细微的差异, 但是在大多数场景中, 它们是可以互相替换的。话虽如此, 但是我更加强烈地喜欢@Component注解,因为@Component简单的表达了组件的含义.这两个注解中都只有一个属性value.
Java依赖注入规范(Java Dependency Injection)
Java依赖注入规范(Java Dependency Injection) 中所提供的@Named注解来生成Bean,@Inject注解来注入-> 大部分时候等价Spring中的@Autowriter注解
参考: http://tool.oschina.net/uploads/apidocs/javaEE6/javax/inject/package-summary.html
更多详细可以参考: Spring| Java注解规范与Spring注解对比
1.2XML方式-配置组件扫描
使用XML来启用组件扫描的话, 那么可以使用Spring context命名空间的<context:component-scan>
元素,<context:component-scan>
元素会有与@ComponentScan注解相对应的属性和子元素。
XML配置扫描方式,不需要Java配置类只需要在Spring上下文配置XMl中配置扫描标签既可.同样在需要被扫描注册的Bean也是需要使用@Component之类的标签说明的,下面是流程代码示例:
// @Named
@Component
public class Role {
private String id;
private String name;
private String desc;
//省略get,set方法
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<context:component-scan base-package="com.yveshe.chapter2.bean" />
</beans>
public class XmlComponetScanTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/com/yveshe/chapter2/contextconf/xml/spring-context.xml");
com.yveshe.chapter2.bean.Role role = context.getBean(com.yveshe.chapter2.bean.Role.class);
context.close();
System.out.println(JsonUtil.toJson(role, true));
}
}
<context:annotation-config />
:
是用于激活那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过Java注解方式),这样在自动注入依赖的时候,Spring才会将已经注册的Bean自动装载进其他需要该依赖的Bean中.
<context:component-scan />
:
除了具有<context:annotation-config>
的功能之外,<context:component-scan />
还可以在指定的package下扫描以及注册javabean 。
参考: context:annotation-config 和 context:component-scan的区别
2.自动装配
如果我们的应用程序所有对象都是独立的, 彼此之间没有任何依赖,那么仅仅使用Spring的扫描功能就算完事了,它会自动的帮我们把Java对象创建.但是如果对象与对象之间如果存在相互依赖的联系时,我们就需要用的Spring给我们提供的自动装配了.
2.1自动装配关键注解
- @Autowired (Spring定义)
- @Inject (Java依赖注入规范)
关于Java依赖规范中定义了如下注解: (Named 大部分情况下等价Spring中Component注解,Spring对该规范做了支持)
Annotation Types Summary | |
---|---|
Inject | Identifies injectable constructors, methods, and fields. |
Named | String-based qualifier. |
Qualifier | Identifies qualifier annotations. |
Scope | Identifies scope annotations. |
Singleton | Identifies a type that the injector only instantiates once. |
可以参看: http://tool.oschina.net/uploads/apidocs/javaEE6/javax/inject/package-summary.html
@Autowired的替代品:
如果你不愿意在代码中到处使用Spring的特定注解@Autowired来完成自动装配任务的话,那么你可以考虑将其替换为@Inject注解,不过在Spring的项目中还是建议使用@Autowired注解
2.2自动装配原理 (主要是@Autowired注解的使用)
什么是自动装配?
简单来说, 自动装配就是让Spring自动满足bean依赖的一种方法, 在满足依赖的过程中, 会在Spring应用上下文中寻找匹配某个bean需求的其他bean。
如何使用Spring的自动装配功能?
在Spring中为了声明要进行自动装配, 我们可以借助Spring的@Autowired注解来实现.
2.2.1@Autowired的简单例子
代码结果如下:
@Component
public class Role {
private String id;
private String name;
private String desc;
// 省略get,set方法
}
没有使用自动装配的User类
@Component
public class User {
private String name;
private int age;
private Role role;
//省略get,set
}
没有使用自动装配的User类
@Component
public class User2 {
private String name;
private int age;
private Role role;
/**
* 不管是构造方法、 Setter方法还是其他的有参方法上所声明的依赖都是可以进行自动装配的
*
* 也就是说: 与这个方法名称没有关系setRole于yveshe是一样的效果,不过在Java中,通常我们还是用这些默认的名称
*
* @param role
*/
@Autowired
public void aaa(Role role) {
this.role = role;
}
//省略其他get,set方法
}
测试结果:
public class AutowiredTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/yveshe/chapter2/autowired/contextconf/spring-context-4autowired.xml");
com.yveshe.chapter2.autowired.bean.User user = context.getBean(com.yveshe.chapter2.autowired.bean.User.class);
com.yveshe.chapter2.autowired.bean.User2 user2 = context.getBean(com.yveshe.chapter2.autowired.bean.User2.class);
context.close();
}
}
我们可以发现role实例对象注入进了user2中,而user中却没有背注入
2.2.2@Autowired具体使用介绍
注解Autowired源码如下:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
- 如果
没有匹配的bean
, 那么在应用上下文创建的时候, Spring会抛出一个异常。为了避免异常的出现, 你可以将@Autowired的required属性设置为false,将required属性设置为false时, Spring会尝试执行自动装配, 但是如果没有匹配的bean的话, Spring将会让这个bean处于未装配的状态,(也就相当于没有进行赋值,引用类型就是Null了)如果在你的代码中没有进行null检查的话, 这个处于未装配状态的属性有可能会出现NullPointerException。 - 如果
匹配出现多个
满足条件的, Spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配。( 在第3章中, 我们会进一步讨论自动装配中的歧义性。)
总结自动装配本质:
不管是构造方法、 Setter方法还是其他的有参方法上所声明的依赖都是可以进行自动装配,说明与方法名称无关
.