基础5:注解详解(组件注册)

整体思路

基于XML配置的时候,我们需要先建立一个XML文件,然后再让ApplicationContext去主动加载该XML文件。

按照这个思路,注解驱动开发也是这个流程,首先用@Configuration注解标记一个配置类(充当XML配置文件),然后在配置类中用@Bean注解去标记方法(用方法充当<bean/>节点),最后用AnnotationConfigApplicationContext取代ClassPathXml-
ApplicationContext。

基础5:注解详解(组件注册)

基础5:注解详解(组件注册)

@Bean

@Bean表示一个方法实例化、配置或者初始化一个Spring IOC容器管理的新对象。

@Component

@Component自动被comonent扫描。表示被注解的类会自动被component扫描。

基础5:注解详解(组件注册)

@Repository

@Repository用于持久层,用来表示一个持久层bean,即数据访问层DAO组件。

Spring MVC采用经典的三层分层控制结构,在持久层、业务层、控制层分别采用@Repository、@Service、@Controller注解来对分层中的相关类进行注解。

这个在学习Spring Data时会说明。

@Service

@Service表示被注解的类是位于业务层的业务component。

Spring MVC采用经典的三层分层控制结构,在持久层、业务层、控制层分别采用@Repository、@Service、@Controller注解来对分层中的相关类进行注解。

@Controller

@Controller表明被注解的类是控制component,主要用于展现层。

Spring MVC采用经典的三层分层控制结构,在持久层、业务层、控制层分别采用@Repository、@Service、@Controller注解来对分层中的相关类进行注解。

@Controller标记的类就是一个Spring MVC中的Controller对象,分发处理器会扫描使用该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。@Controller只是定义了一个控制器类,而使用@RequestMapping注解的方法才是处理请求的处理器(可以理解为Struts的action类与方法)。

@Controller标记的类,要么在XML中进行定义,要么在<context:component-scan/>中进行自动扫描。

<context:component-scan/>

之前通过<context:component-scan/>只会发现@Controller、@Service、@Repository、@Component注解。

默认use-default-filters="true",即默认扫描@Component注解及其子注解,可以配置filter过滤只扫描哪些注解、不扫描哪些注解。

@ComponentScan自动扫描组件

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.FilterType;

import org.springframework.stereotype.Controller;

import org.springframework.stereotype.Service;

 

/**

 * @Configuration:标准配置类

 * @ComponentScan:发现@Service、@Repository、@Component注解,该注解作用类似于<context:component-scan/>,

 * 其中的value参数用来指定扫描的包路径,

 * includeFilters则是包含什么,其要求是一个ComponentScan.Filter[]

 * excludeFilters则是不包含什么,其要求是一个ComponentScan.Filter[],其中type = FilterType.ANNOTATION表达的意思是按照注解进行过滤,

 * 而classes是一个数组,里面可以填写各种注解的class对象

 * 这里可以参考之前的XML配置,基本与XML是完全一样的。

 * 2020 - 07 - 16

 * 16:21

 */

@Configuration

@ComponentScan(

        value = "com.spring.annotation",

        excludeFilters = {

                @ComponentScan.Filter(

                        type = FilterType.ANNOTATION,

                        classes = {Controller.class, Service.class})

        })

public class MainConfig {

 

    /**

     * @Bean,类型为返回值的类型,id默认是方法名(大小写与方法名完全一致)

     * */

    @Bean

    public Persion persion(){

        return new Persion("ZhangSan",30);

    }

}

@Scope组件作用域

Bean默认都是单实例,通过@Scope来改变这种情况。默认单实例的时候,IOC容器启动的时候就会创建对象放到IOC容器中,以后每次获取都是直接从容器中获取。

多实例情况下,每次获取的时候才会调用方法,来创建对象,每调用一次方法,就新创建一个对象实例。

/**

 * @Bean,类型为返回值的类型,id默认是方法名(大小写与方法名完全一致)

 * */

    @Bean

    @Scope("prototype")

    public Persion persion(){

        return new Persion("ZhangSan",30);

    }

@Lazy懒加载

只能用于单实例的Bean。因为单实例Bean会在IOC容器启动的时候就被初始化,所谓懒加载,就是IOC容器启动时暂不创建对象,直到第一次获取该Bean的时候再去创建对象并初始化。

/**

 * @Bean,类型为返回值的类型,id默认是方法名(大小写与方法名完全一致)

 * */

    @Bean

    @Lazy

    public Persion persion(){

        return new Persion("ZhangSan",30);

    }

@Conditional按照条件注册Bean

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Conditional {

    Class<? extends Condition>[] value();

}

该注解可以用在类上,也可以用在方法上;参数是Condition接口的实现类即可。

当用于注解类的时候,则只有满足条件,这个类中所有的方法才能被注册Bean。

当用于注解方法的时候,则只有满足条件,这个方法才能被注册Bean。

@Bean

@Lazy

@Conditional(MyConditional.class)

public Persion persion(){

        return new Persion("ZhangSan",30);

}

 

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import org.springframework.context.annotation.Condition;

import org.springframework.context.annotation.ConditionContext;

import org.springframework.core.env.Environment;

import org.springframework.core.type.AnnotatedTypeMetadata;

import sun.misc.OSEnvironment;

 

public class MyConditional implements Condition {

    /**

     * ConditionContext:判断条件能使用的上下文环境

     * AnnotatedTypeMetadata:注解信息

     * */

    @Override

public boolean matches(

ConditionContext conditionContext,

AnnotatedTypeMetadata annotatedTypeMetadata) {

        // 获取IOC使用的BeanFactory

        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();

        // 获取类加载器

        ClassLoader classLoader = conditionContext.getClassLoader();

        // 获取环境

        Environment environment =   conditionContext.getEnvironment();

        // 获取bean定义的注册类

        BeanDefinitionRegistry beanDefinitionRegistry =   conditionContext.getRegistry();

        String osName = environment.getProperty("os.name");

        if(osName.toUpperCase().contains("WINDOW")){

            return true;

        }

        return false;

    }

}

@Import给容器快速导入一个组件

如果想把一个类导入进来,可以使用@Import注解。该注解只能用在类文件上,而至于被引入的类,不管其有没有注解,都可以被引入(特别是第三方框架里的文件,因为第三方的jar文件,你是无法修改源代码的)。

id默认是类的全类名。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Import {

    Class<?>[] value();

}

 

@Import(ImportTest.class)

@Configuration

@ComponentScan(

        value = "com.spring.annotation",

        excludeFilters = {

                @ComponentScan.Filter(

                        type = FilterType.ANNOTATION,

                        classes = {Controller.class, Service.class})

        })

public class MainConfig {

}

扩展:

可以通过ImportSelector接口来返回一个数组,数组元素就是类文件的全类名,然后@Import注解就会解析这个数组,然后去加载对应的类文件。

还可以通过ImportBeanDefinitionRegistrar接口,直接自己来注册一个Bean,不需要@Import注解。

使用FactoryBean注册组件

注意&符的使用。