Spring的@Configuration、@Bean、@Conponent 和 @ComponentScane

        这几日通过编写后端程序,突然对以上几个注解产生点迷糊,一直在懂与不懂得边缘游走。今天特写一篇文章,帮助我以及其他遇到此问题得小伙伴排忧解难。

一、@Configuration、@Conponent 

一句话概括就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。

而 @Conponent 修饰的类不会被代理,每实例化一次就会创建一个新的对象。

1、实现的细节。

Configuration注解源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

Component注解源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
    String value() default "";
}


从定义来看, @Configuration 注解本质上还是 @Component,因此 <context:component-scan/> 或者 @ComponentScan 都能处理@Configuration 注解的类。

1、@Configuration 中所有带 @Bean 注解的方法都会被动态代理(cglib),因此调用该方法返回的都是同一个实例

2、@Conponent 修饰的类不会被代理,每实例化一次就会创建一个新的对象。

@Configuration 标记的类必须符合下面的要求:

1、配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
2、配置类不能是 final 类(没法动态代理)。
3、配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,配置类必须是非本地的(即不能在方法中声明,不能是 private)。
4、任何嵌套配置类都必须声明为static。
5、@Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有@Configuration,也不会被特殊处理,只会作为普通的 bean)。

2、加载过程

Spring 容器在启动时,会加载默认的一些PostPRocessor,其中就有ConfigurationClassPostProcessor,这个后置处理程序专门处理带有@Configuration注解的类,这个程序会在bean 定义加载完成后,在bean初始化前进行处理。主要处理的过程就是使用cglib动态代理增强类,而且是对其中带有@Bean注解的方法进行处理。

因此我们在@Configuration注解定义的 bean 方法中可以直接调用方法,不需要@Autowired注入后使用。

3、@Component注解

1、@controller 控制器(注入服务)
2、@service 服务(注入dao)
3、@repository dao(实现dao访问)
4、@component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)

1、@controller 控制器(注入服务)
        用于标注控制层,相当于struts中的action层

2、@service 服务(注入dao)
        用于标注服务层,主要用来进行业务的逻辑处理

3、@repository(实现dao访问)
        用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件.

4、@component (把普通pojo实例化到spring容器中,相当于配置文件中的 
<bean id="" class=""/>)
        泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。

说明: 
<context:component-scan base-package=”com.*”> 
上面的这个例子是引入Component组件的例子,其中base-package表示为需要扫描的所有子包。 
共同点:被@controller 、@service、@repository 、@component 注解的类,都会把这些类纳入进spring容器中进行管理

4、区别

@Configuration 注解定义的 bean 方法中可以直接调用方法,不需要 @Autowired 注入后使用。

因为源码是直接调用CGlib动态代理的代理类,obtainBeanInstanceFromFactory 方法比较简单,就是通过 beanFactory.getBean 获取 Country,如果已经创建了就会直接返回,如果没有执行过,就会通过 invokeSuper 首次执行。
 

二、Bean注解


Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

value: name属性的别名,在不需要其他属性时使用,也就是说value 就是默认值
name: 此bean 的名称,或多个名称,主要的bean的名称加别名。如果未指定,则bean的名称是带注解方法的名称。如果指定了,方法的名称就会忽略,如果没有其他属性声明的话,bean的名称和别名可能通过value属性配置。

1、@Bean的“full”模式和“lite”模式

在一般常见情况下,@Bean注解在@Configuration类中声明,称之为“full”模式;

                                当@Bean注解和@Component注解组合使用时,称之为“lite”模式。

2、差异

如果只是把@Bean注解用在方法上,并且各个@Bean注解的方法之间没有调用,上述两种模式达到的效果基本相同,都可以把@Bean注解方法返回的对象作为bean注册到容器中。

如果各个@Bean注解的方法之间有相互调用,那么两种模式就会有很大的区别-与full模式下的@Configuration不同,lite模式下 @Bean方法互相调用无法声明Bean之间的依赖关系。

这点在@Bean注解源码注释上也有说明。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};
    @AliasFor("value")
    String[] name() default {};
    Autowire autowire() default Autowire.NO;
    String initMethod() default "";
    String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}

综上所述,在使用@Bean注解时需要注意两种模式的区别,一般情况下@Bean都是和@Configuration注解搭配使用的。

@Configration 注解和@Bean 注解的区别:

  • @Configration 注解:声明当前类是一个配置类,相当于 Spring 中的一个 XML 文件
  • @Bean 注解:作用在方法上,声明当前方法的返回值是一个 Bean

三、@ComponentScan 注解

@ComponentScan 注解在配置 bean 中进行使用,等同于 XML 配置文件中的 <context:component-scan> 标签

目的:进行相关注解的扫描(@Component、@Value、@Autowired …)

基本使用:

XML 方式:
<context:component-scan base-package="com.yusael.scan"/>
注解方式:
@Configuration
@ComponentScan(basePackages = "com.yusael.scan")
public class AppConfig2 {
}

排除策略的的使用:

XML 方式:
<context:component-scan base-package="com.yusael">
    <context:exclude-filter type="assignable" expression="com.yusael.bean.User"/>
</context:component-scan>

注解方式:
排除特定的注解:type = FilterType.ANNOTATION, value={}
排除特定的类型:type = FilterType.ASSIGNABLE_TYPE , value={]
切入点表达式:type = FilterType.ASPECTJ, pattern=""
正则表达式:type = FilterType.REGEX, pattern=""
自定义排除策略:type = FilterType.CUSTOM, pattern=""
@ComponentScan(basePackages = "com.yusael.scan",
               excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION, value={Service.class}),
                                 @ComponentScan.Filter(type= FilterType.ASPECTJ, pattern = "*..User1")})

包含策略的使用:

XML 方式:
<context:component-scan base-package="com.yusael" use-default-filters="false">
    <context:include-filter type="" expression=""/>
</context:component-scan>


注解方式:参数与排除策略中一样

@ComponentScan(basePackages = "com.yusaael.scan",
               useDefaultFilters = false,
               includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class})})

四、参考文献:

原文链接:https://blog.csdn.net/weixin_43734095/article/details/115947808
原文链接:https://blog.csdn.net/tiantangdizhibuxiang/article/details/81784878
原文链接:https://blog.csdn.net/isea533/article/details/78072133

   https://www.jb51.net/article/153430.htm

原文链接:https://blog.csdn.net/y516369/article/details/127598350

猜你喜欢

转载自blog.csdn.net/m0_57037182/article/details/127746661