Spring| Spring 组件扫描和自动装配5

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
InjectIdentifies injectable constructors, methods, and fields.
NamedString-based qualifier.
QualifierIdentifies qualifier annotations.
ScopeIdentifies scope annotations.
SingletonIdentifies 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方法还是其他的有参方法上所声明的依赖都是可以进行自动装配,说明与方法名称无关.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值