zoukankan      html  css  js  c++  java
  • spring 注解学习 一 Bean的注入

    1、Configuration注解

    最早的spring中bean对象的配置是采用配置文件的形式,这种形式的配置过繁琐,自从spring 3.0之后 提供了基于java-based configuration。

    XML方式的配置如下:

    <?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:p="http://www.springframework.org/schema/p"
           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
            https://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:component-scan base-package="com"></context:component-scan>
    
        <bean id="person" class="com.caopeng.com.caopeng.bean.Person"></bean>
    
    </beans>
    

    采用java配置类的配置方式:

    package com.caopeng.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    /*
    1、带有@Configuration注解的类,表示这个类是一个配置类。这样的配置类就像一个xml的配置文件。
    2、默认情况下以@Configuration注解作为配置类创建的spring容器,spring容器扫描的包范围是配置类所在的包及其子包,
       会把包下带有@Controller、@Component、@Service、@Repository注解的类注入到spring容器中
    3、可以通过@ComponentScan注解来指定spring容器要扫描的包
    */
    @Configuration
    @ComponentScan("com.caopeng.*")
    public class AppConfig {
    
    }
    
    package com.caopeng.main;
    
    import com.caopeng.bean.Person;
    import com.caopeng.config.AppConfig;
    import org.junit.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
    
    
        @Test
        public void fun(){
            AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
            Person person = applicationContext.getBean(Person.class);
            System.out.println(person);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String name:beanDefinitionNames ) {
                Object bean = applicationContext.getBean(name);
                System.out.println(name+"="+bean);
            }
        }
    }
    

    2、CompontScan注解

    ComponentScan注解主要用来指定spring容器要扫描那些包下的类,其作用和xml配置中的开启注解扫描的标签作用一样

     <context:component-scan base-package="com"></context:component-scan>
    

    ComponentScan注解的定义如下:

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.core.annotation.AliasFor;
    import org.springframework.core.type.filter.TypeFilter;
    
    
    @Retention(RetentionPolicy.RUNTIME)//注解保留到运行时
    @Target(ElementType.TYPE)//注解打在类上
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
    
    	//对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
    	@AliasFor("basePackages")
    	String[] value() default {};
        //和value一样是对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
    	@AliasFor("value")
    	String[] basePackages() default {};
        //指定具体的扫描的类
    	Class<?>[] basePackageClasses() default {};
      /**
         命名注册的Bean,可以自定义实现命名Bean,
      1、@ComponentScan(value = "spring.annotation.componentscan",nameGenerator = MyBeanNameGenerator.class)
            MyBeanNameGenerator.class 需要实现 BeanNameGenerator 接口,所有实现BeanNameGenerator 接口的实现类都会被调用
        
     2、使用 AnnotationConfigApplicationContext 的 setBeanNameGenerator方法注入一个BeanNameGenerator
    
    BeanNameGenerator beanNameGenerator = (definition,registry)-> String.valueOf(new Random().nextInt(1000)); AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.setBeanNameGenerator(beanNameGenerator);
    annotationConfigApplicationContext.register(MainConfig2.class);
    annotationConfigApplicationContext.refresh();
    第一种方式只会重命名@ComponentScan扫描到的注解类
    第二种只有是初始化的注解类就会被重命名
    列如第一种方式不会重命名 @Configuration 注解的bean名称,而第二种就会重命名 @Configuration 注解的Bean名称
    */
    	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    	/**
    用于解析@Scope注解,可通过 AnnotationConfigApplicationContext 的 setScopeMetadataResolver 方法重新设定处理类
    ScopeMetadataResolver scopeMetadataResolver = definition -> new ScopeMetadata();  这里只是new了一个对象作为演示,没有做实际的逻辑操作
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    
    annotationConfigApplicationContext.setScopeMetadataResolver(scopeMetadataResolver);
    annotationConfigApplicationContext.register(MainConfig2.class);
    annotationConfigApplicationContext.refresh();
    也可以通过@ComponentScan 的 scopeResolver 属性设置
    @ComponentScan(value = "spring.annotation.componentscan",scopeResolver = MyAnnotationScopeMetadataResolver.class)
        */
        Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    	
        /**
        * 用来设置类的代理模式
        */
        ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
        /**
        * 扫描路径 如 resourcePattern = "**/*.class"
        * 使用  includeFilters 和 excludeFilters 会更灵活
        */
    	String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
    	/**
        指示是否应启用对带有@Component, @Repository,@Service, @Controller注释的类的自动检测。
        默认情况下是开启的,带有上述四个注解的类都会被注入到spring容器
        */
        boolean useDefaultFilters() default true;
        /**
        *含义是只扫描哪些类
        * 对被扫描的包或类进行过滤,若符合条件,不论组件上是否有注解,Bean对象都将被创建
        * @ComponentScan(value = "spring.annotation.componentscan",includeFilters = {
        *     @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}),
        *     @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SchoolDao.class}),
        *     @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}),
        *     @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "spring.annotation..*"),
        *     @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$")
        * },useDefaultFilters = false)
        
        * 必须配合 useDefaultFilters 必须设为 false
        */
    	Filter[] includeFilters() default {};
         /**
        * 指定哪些类型不适合进行组件扫描。
        * 用法同 includeFilters 一样,
        * 一般配合 useDefaultFilters 必须设为 true
        */
    	Filter[] excludeFilters() default {};
        /**
        * 指定是否应注册扫描的Bean以进行延迟初始化。
        * @ComponentScan(value = "spring.annotation.componentscan",lazyInit = true)
        */
    	boolean lazyInit() default false;
        /**
        * 用于 includeFilters 或 excludeFilters 的类型筛选器
        */
    	@Retention(RetentionPolicy.RUNTIME)
    	@Target({})
    	@interface Filter {
            /**
           * 要使用的过滤器类型,默认为 ANNOTATION 注解类型
           * @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
           */
    		FilterType type() default FilterType.ANNOTATION;
             /**
           * 过滤器的参数,参数必须为class数组,单个参数可以不加大括号
           * 只能用于 ANNOTATION 、ASSIGNABLE_TYPE 、CUSTOM 这三个类型
           * @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Service.class})
           * @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SchoolDao.class})
           * @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
           */
    		@AliasFor("classes")
    		Class<?>[] value() default {};
             /**
           * 作用同上面的 value 相同
           * ANNOTATION 参数为注解类,如  Controller.class, Service.class, Repository.class
           * ASSIGNABLE_TYPE 参数为类,如 SchoolDao.class
           * CUSTOM  参数为实现 TypeFilter 接口的类 ,如 MyTypeFilter.class
           * MyTypeFilter 同时还能实现 EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware,ResourceLoaderAware 
           * 这四个接口
           * EnvironmentAware
           * 此方法用来接收 Environment 数据 ,主要为程序的运行环境,Environment 接口继承自 PropertyResolver 接口,
           * 详细内容在下方
           * @Override
           * public void setEnvironment(Environment environment) {
           *    String property = environment.getProperty("os.name");
           * }
           * 
           * BeanFactoryAware
           * BeanFactory Bean容器的根接口,用于操作容器,如获取bean的别名、类型、实例、是否单例的数据
           * @Override
           * public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
           *     Object bean = beanFactory.getBean("BeanName")
           * }
           * 
           * BeanClassLoaderAware
           * ClassLoader 是类加载器,在此方法里只能获取资源和设置加载器状态
           * @Override
           * public void setBeanClassLoader(ClassLoader classLoader) {
           *     ClassLoader parent = classLoader.getParent();
           * }
           * 
           * ResourceLoaderAware
           * ResourceLoader 用于获取类加载器和根据路径获取资源
           * public void setResourceLoader(ResourceLoader resourceLoader) {
           *     ClassLoader classLoader = resourceLoader.getClassLoader();
           * }
           */
    		@AliasFor("value")
            Class<?>[] classes() default {};
            /**
           * 这个参数是 classes 或 value 的替代参数,主要用于 ASPECTJ 类型和  REGEX 类型
           * ASPECTJ  为 ASPECTJ 表达式
           * @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "spring.annotation..*")
           * REGEX  参数为 正则表达式
           * @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$")
           */
    		
    		String[] pattern() default {};
    
    	}
    
    }
    

    3、ComponentScan示例

    package com.caopeng.config;
    
    import com.caopeng.filter.MyTypeFilter;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    /**
    1、只扫描com.caopeng包及其子包下的类
    2、不开启自动检测 @Controller、@Component、@Service、@Repository的类
    3、对com.caopeng包及其子包下的类进行遍历,只有match方法返回true时,被遍历的类才会加入spring容器
    */
    @Configuration
    @ComponentScan(value="com.caopeng.*",useDefaultFilters = false,includeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
    public class AppConfig {
    
    }
    
    package com.caopeng.filter;
    
    import org.springframework.core.io.Resource;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.core.type.ClassMetadata;
    import org.springframework.core.type.classreading.MetadataReader;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.core.type.filter.TypeFilter;
    
    import java.io.IOException;
    
    public class MyTypeFilter implements TypeFilter {
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
            System.out.println(">>>>>>>>>>>>>>"+annotationMetadata.getAnnotationTypes());
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            System.out.println(">>>>>>>>>>>>>>"+classMetadata.getClassName());
            Resource resource = metadataReader.getResource();
            System.out.println(">>>>>>>>>>>>>>"+resource.getURI());
    
            return false;
        }
    }
    

    运行结果:

    >>>>>>>>>>>>>>[org.springframework.stereotype.Component]
    >>>>>>>>>>>>>>com.caopeng.bean.Person
    >>>>>>>>>>>>>>file:/D:/workspace_idea/maven-pro-tesst/target/classes/com/caopeng/bean/Person.class
    >>>>>>>>>>>>>>[org.springframework.stereotype.Controller]
    >>>>>>>>>>>>>>com.caopeng.controller.PersonController
    >>>>>>>>>>>>>>file:/D:/workspace_idea/maven-pro-tesst/target/classes/com/caopeng/controller/PersonController.class
    >>>>>>>>>>>>>>[org.springframework.stereotype.Component]
    >>>>>>>>>>>>>>com.caopeng.dao.PersonDao
    >>>>>>>>>>>>>>file:/D:/workspace_idea/maven-pro-tesst/target/classes/com/caopeng/dao/PersonDao.class
    >>>>>>>>>>>>>>[]
    >>>>>>>>>>>>>>com.caopeng.filter.MyTypeFilter
    >>>>>>>>>>>>>>file:/D:/workspace_idea/maven-pro-tesst/target/classes/com/caopeng/filter/MyTypeFilter.class
    >>>>>>>>>>>>>>[]
    >>>>>>>>>>>>>>com.caopeng.main.Main
    >>>>>>>>>>>>>>file:/D:/workspace_idea/maven-pro-tesst/target/classes/com/caopeng/main/Main.class
    >>>>>>>>>>>>>>[org.springframework.stereotype.Service]
    >>>>>>>>>>>>>>com.caopeng.service.PersonService
    >>>>>>>>>>>>>>file:/D:/workspace_idea/maven-pro-tesst/target/classes/com/caopeng/service/PersonService.class
    

    4、spring中bean的注解注入方式

    4.1、自定义类的注入注解 @Controller、@Component、@Service、@Repository

    程序员自己定义的类,一般采取四种注解注入到spring容器,这些注解功能一样,只是为了语义上明确所以定义了四个注解

    分别是: @Controller、@Component、@Service、@Repository四个注解

    在@ComponentScan注解指定的包下,默认会把带这四个注解的类注入到spring容器

    4.2、第三方包的类的注入注解@Bean

    如果是我们引入的第三方的jar包,此时开发者是无法获取到类的源码的,如果想把第三方jar包中的类注入spring容器,可以采用@Bean注解

    package com.caopeng.config;
    
    import com.caopeng.bean.Person;
    import com.caopeng.filter.MyTypeFilter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    
    @Configuration
    @ComponentScan(value="com.caopeng.*",useDefaultFilters = false,includeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
    @Import({MySelector.class, Red.class})//采用@Import的方式
    public class AppConfig {
    
        
      // 采用@Bean注解的方式
       @Bean("person")
        public Person getPerson(){
            Person person=new Person();
            return  person;
        }
        
    
    }
    

    4.3、@Import注解

    @Import注解的定义

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
    
    	/**
    	 * {@link Configuration @Configuration}, {@link ImportSelector},
    	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
    	 */
    	Class<?>[] value();
    
    }
    

    @Import注解的参数是一个Class的数组,数组中可以防止三类Class对象

    1 要注入spring容器对象的Class对象

    2 实现了ImportSelector接口的类的Class对象

    3 实现了ImportBeanDefinitionRegistrar接口的对象

    也可以采用ImportSelect接口对要注入的类进行筛选,使用方法如下:

    /**
    MySelector类是实现ImportSelector接口的类
    Red是要注入的实体类
    MyImportBeanDefinitionRegistrar是实现了ImportBeanDefinitionRegistrar接口的类
    */
    @Import({MySelector.class, Red.class, MyImportBeanDefinitionRegistrar.class})
    

    ImportSelect接口

    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MySelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    
            return new String[]{"com.caopeng.bean.Blue"};
        }
    }
    

    selectImports方法返回的类的全路径名就是要注入的类

    ImportBeanDefinitionRegistrar接口

    import com.caopeng.bean.RainBow;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            BeanDefinition beanDefinition=new RootBeanDefinition(RainBow.class) ;
    
            registry.registerBeanDefinition("rainBow",beanDefinition);
        }
    }
    

    5、@Scope注解

    基本上spring容器中只有单例的bean,当然可以采用@Bean注解把同一个类的多个对象注入到spring容器中。

    被@Scope标注的类,只有singleton的bean对象才会放入单例池中

    @Scope("prototype")//多实例,IOC容器启动创建的时候,并不会创建对象放在容器在容器当中,当你需要的时候,需要从容器当中取该对象的时候,就会创建。
    @Scope("singleton")//单实例 IOC容器启动的时候就会调用方法创建对象,以后每次获取都是从容器当中拿同一个对象(map当中)。
    @Scope("request")//同一个请求创建一个实例
    @Scope("session")//同一个session创建一个实例

    6、@Lazy 注解

    value 取值有 true 和 false 两个 默认值为 true

    true 表示使用 延迟加载,只是配合单例才有意义,当第一次从spring容器中取时才会创建bean

    false 表示不使用,false 纯属多余,如果不使用,不标注该注解就可以了。spring容器初始化时就会创建bean

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Lazy {
    	boolean value() default true;
    
    }
    

    7、@Conditional注解

    注解的定义

    @Target({ElementType.TYPE, ElementType.METHOD})//该注解可以标注在类上,也可以标注在方法上
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
    
    	Class<? extends Condition>[] value();
    
    }
    

    使用实例:

    @Configuration
    @ComponentScan(value="com.caopeng.*",useDefaultFilters = false,includeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
    public class AppConfig {
    
        @Bean("person")
        @Conditional(MyCondition.class)
        public Person getPerson(){
            Person person=new Person();
            return  person;
        }
    
    }
    

    实现Condition接口的类:

    在扫描Person时,如果MyCondition的matches方法返回的是true,则注入,如果返回的是false,则不注入

    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    public class MyCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return false;
        }
    }
    

    如果@Conditional注解标注在方法上,表示这个方法产生的bean要满足一定条件才会注入到spring容器

    如果@Conditional注解标注在配置类上,表示这个配置类的所有产生bean的方法 都有@Conditional注解

    如果@Conditional注解标注在普通的类上,且这个类上有@Component注解,表示这个类的对象要满足一定条件才能注入到spring容器

    8、FactoryBean

    FactoryBean 用来创建一类bean,用来创建一些特殊或者复杂的Bean,例如Map等

    8.1、实现FactoryBean接口

    import com.caopeng.bean.Yellow;
    import org.springframework.beans.factory.FactoryBean;
    
    public class MyFactoryBean implements FactoryBean<Yellow> {
    
        @Override
        public Yellow getObject() throws Exception {
            return new Yellow();
        }
    
        @Override
        public Class<?> getObjectType() {
            return Yellow.class;
        }
    //是否单例
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    

    8.2、把FactoryBean的实现类注入spring容器

    import com.caopeng.factory.MyFactoryBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ComponentScan
    public class AppConfig2 {
    
        //把FactoryBean注入spring容器
        @Bean("myFactoryBean")
        public MyFactoryBean getFactoryBean(){
            return new  MyFactoryBean();
        }
    }
    

    8.3、从spring容器中获取

        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig2.class);
            //获取的是工厂bean
            MyFactoryBean bean = applicationContext.getBean(MyFactoryBean.class);
            System.out.println(bean);
    		//获取的是工厂bean生产的对象
            Object obj = applicationContext.getBean("myFactoryBean");
            System.out.println(obj);
    		 //获取的是工厂bean
            Object ob2 = applicationContext.getBean("&myFactoryBean");
            System.out.println(ob2);
    
        }
    

    8.4、运行结果

    com.caopeng.factory.MyFactoryBean@1a0dcaa
    com.caopeng.bean.Yellow@3bd40a57
    com.caopeng.factory.MyFactoryBean@1a0dcaa
  • 相关阅读:
    MAX导致数据库超时
    mysql查询效率提高技巧
    微信回调报文解析, 获取请求体内容
    炖汤秘方
    首字母小写
    List分页
    HttpServletRequest通过InputStream获取参数
    github命令行
    mysql死锁
    分布式锁-redis
  • 原文地址:https://www.cnblogs.com/cplinux/p/14528363.html
Copyright © 2011-2022 走看看