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

    xiaoxiao2022-07-12  172

    Spring是从两个角度来实现自动化装配:

    组件扫描(component scanning) : Spring会自动发现应用上下文中所创建的bean自动装配(autowiring) : Spring自动满足bean之间的依赖

    文章目录

    1.组件扫描1.1Java配置类方式-配置组件扫描1.2XML方式-配置组件扫描 2.自动装配2.1自动装配关键注解2.2自动装配原理 (主要是@Autowired注解的使用)2.2.1@Autowired的简单例子2.2.2@Autowired具体使用介绍


    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 SummaryInjectIdentifies 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方法还是其他的有参方法上所声明的依赖都是可以进行自动装配,说明与方法名称无关.

    最新回复(0)