Spring注解开发之Spring常用注解

时间:2022-12-15 14:31:49

https://blog.csdn.net/Adrian_Dai/article/details/80287557
主要的注解使用:

本文用的Spring源码是4.3.16
@Configuration

此注解的作用是告诉Spring,添加该注解的类是配置类。

@ComponentScan

相信大家看到这个组合单词就知道是有什么作用了,扫描用的。直接看看源码吧

@Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
     
        @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;
     
        @Retention(RetentionPolicy.RUNTIME)
        @Target({})
        @interface Filter {
     
            FilterType type() default FilterType.ANNOTATION;
     
            @AliasFor("classes")
            Class<?>[] value() default {};
     
            @AliasFor("value")
            Class<?>[] classes() default {};
     
            String[] pattern() default {};
     
        }
     
    }

由于不想篇幅过长,我把注释都去掉了,如果需要完整的Spring源码可以评论留下邮箱!

这边我主要讲一下我常用到的注释,其他注释读者可以自行学习。

value  指定要扫描的包。

==================================================简单的分隔符吧。下面一样

includeFilters 指定扫描的时候只需要包含哪些组件;

excludeFilters 指定扫描的时候按照什么规则排除那些组件;

它们两个都是一个Filter[],在上面的代码中也有,Filter中有个FilterType,它是一个枚举类,有5种类型。

FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则

来个示例吧:

@ComponentScan(value="com.csdn.dh",includeFilters = {
                            @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
                            @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService.class}),
                            @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
                    }

Controller.class是包含@Controller的注解的类,还有是UserService的类,然后最后一个是自定义的规则类,自定义规则类需要实现TypeFilter接口,实现match()方法;

boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException;

match 方法上的两个参数分别是:①metadataReader:读取到的当前正在扫描的类的信息

②metadataReaderFactory:可以获取到其他任何类信息的

留意到的读者会发现方法的返回值是boolean类型的,说明返回true就包含该组件,反之排除。我们来个尝试吧。看一下下面的代码和结果吧!

@Configuration
    @ComponentScans({
        @ComponentScan(value = "com.csdn.dh",
                excludeFilters = {@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})}
        )
    })
    public class MainConfig {
    }

public class MyTypeFilter implements TypeFilter {
        /**
         * metadataReader:读取到的当前正在扫描的类的信息
         * metadataReaderFactory:可以获取到其他任何类信息的
         */
        @Override
        public boolean match(MetadataReader metadataReader,
                MetadataReaderFactory metadataReaderFactory) throws IOException {
            //获取当前类注解的信息
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
            //获取当前正在扫描的类的类信息
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            //获取当前类资源(类的路径)
            Resource resource = metadataReader.getResource();
            
            //className是全类名  com.csdn.dh.pojo.User
            String className = classMetadata.getClassName();
            System.out.println(className);
            if (className.contains("Pojo"))
                return true;
            return false;
        }
    }

public class TestMain {
        public static void main(String[] args) {
            ApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
            String[] beanNames = ac.getBeanDefinitionNames();
            for (String name : beanNames) {
                System.out.println("---->" + name);
            }
        }
    }

输出的结果是:

com.csdn.dh.Filter.MyTypeFilter
    com.csdn.dh.pojo.User
    com.csdn.dh.pojo.UserPojo
    com.csdn.dh.test.TestMain
     
     
    ---->org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    ---->org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    ---->org.springframework.context.annotation.internalRequiredAnnotationProcessor
    ---->org.springframework.context.annotation.internalCommonAnnotationProcessor
    ---->org.springframework.context.event.internalEventListenerProcessor
    ---->org.springframework.context.event.internalEventListenerFactory
    ---->mainConfig
    ---->user

从结果中可以看到,UserPojo类已经被排除了。就是定义的MyTypeFilter起的作用。其他剩下的4个类型读者有兴趣的可自行尝试。

==================================================================

useDefaultFilters  这个属性是指是否自动扫描带有@Component、@Repository、@Service、@Controller注解的类,默认是true,是开启的。

@Scpoe

用于调整作用域,默认是singleton,单例的。来看一下源码里面怎么说吧:

/**
         * Specifies the name of the scope to use for the annotated component/bean.
         * <p>Defaults to an empty string ({@code ""}) which implies
         * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
         * @since 4.2
         * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
         * @see ConfigurableBeanFactory#SCOPE_SINGLETON
         * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
         * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
         * @see #value
         */
        @AliasFor("value")
        String scopeName() default "";

看着是有4种可选的

prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象;

singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿,

request:同一次请求创建一个实例

session:同一个session创建一个实例

@Lazy

懒加载:
    单实例的Bean:默认在容器启动的时候创建对象
    懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化

@Conditional

按照一定的条件进行判断,给容器中注册满组条件的Bean

来看一下源码:看一下他的value,我们需要自定义自己的条件,实现Condition,并重写matches方法,是不是眼熟这个方法?没错,刚才我们就写过了是吧?方法的意思

@Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
     
        /**
         * All {@link Condition}s that must {@linkplain Condition#matches match}
         * in order for the component to be registered.
         */
        Class<? extends Condition>[] value();
     
    }

public class MyCondition implements Condition {
        /**
         * ConditionContext:判断条件能使用的上下文(环境)
         * AnnotatedTypeMetadata:注释信息
         */
        @Override
        public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata) {
            //获取到容器使用的beanfactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            //获取类加载器
            ClassLoader classLoader = context.getClassLoader();
            //获取当前环境信息
            Environment environment = context.getEnvironment();
            //获取到Bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
            return true;
        }
    }

具体的例子我就不写上,相信认真的读者也会方法跟刚才的TypeFilter的实现是差不多的。有兴趣读者可以看看这个两个参数的具体一些方法,来实现一下满足自己设定条件的Bean吧。

@Import

快速给容器中导入一个组件。看一下源码:

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

属性是Class<?>的数组,这个注解的作用是导入一个组件,导入的组件默认是类的全类名,即@Import(User.class)    输出的BeanName是com.csdn.dh.pojo.User

认真看源码的读者能发现,它还能用ImportSelector和ImportBeanDefinitionRegistrar

我们可以自定义需要返回的组件:

public class MyImportSelector implements ImportSelector {
        //返回的String数组就是到导入到容器中的组件全类名
        //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            //方法不要返回null值,否则会报空指针异常(读者可以自行debug看一下原因)
            return new String[]{"com.csdn.dh.pojo.User"};
        }
    }

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        /**
         * AnnotationMetadata:当前类的注解信息
         * BeanDefinitionRegistry:BeanDefinition注册类;
         *         把所有需要添加到容器中的bean;调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
         */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //指定Bean定义信息;(Bean的类型)
            RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class);
            //注册一个Bean,指定bean名
            registry.registerBeanDefinition("user", beanDefinition);
            }
        }
    }

@Profile

指定组件在哪个环境的情况下才能被注册到容器中,不指定则在任何环境下都能注册这个组件。

①加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
②写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
③没有标注环境标识的bean在任何环境下都是加载的;

@PropertySource("classpath:/db.properties")
    @Configuration
    public class TestDHProfile implements EmbeddedValueResolverAware{
        @Value("${db.user}")
        private String user;
        
        private StringValueResolver resolver;
        private String  driverClass;
        
        @Profile("test")
        @Bean("testDataSource")
        public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setUser(user);
            dataSource.setPassword(pwd);
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
            dataSource.setDriverClass(driverClass);
            return dataSource;
        }
        
        @Profile("dev")
        @Bean("devDataSource")
        public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setUser(user);
            dataSource.setPassword(pwd);
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
            dataSource.setDriverClass(driverClass);
            return dataSource;
        }
     
        @Override
        public void setEmbeddedValueResolver(StringValueResolver resolver) {
            this.resolver = resolver;
            driverClass = resolver.resolveStringValue("${db.driverClass}");
        }
     
    }

public class TestProfileMain {
        //1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
        //2、代码的方式激活某种环境;
        @Test
        public void testMain(){
            //1、创建一个applicationContext
            AnnotationConfigApplicationContext applicationContext =
                    new AnnotationConfigApplicationContext();
            //2、设置需要激活的环境
            applicationContext.getEnvironment().setActiveProfiles("dev");
            //3、注册主配置类
            applicationContext.register(TestDHProfile.class);
            //4、启动刷新容器
            applicationContext.refresh();
            applicationContext.close();
        }
    }

相信认真看的读者也看到上面TestDHProfile中实现了EmbeddedValueResolverAware的接口。这边我说一下这个接口的作用吧,实现这个接口的setEmbeddedValueResolver方法可以读取配置文件,拿到配置文件中属性对应的值,如我代码中的driverClass就是利用resolver来获取的。

@Value

这个注解不讲了。

@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;
    }

(1)、[标注在方法位置]:参数从容器中获取,默认不写@Autowired效果是一样的,都能自动装配
(2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
(3)、放在参数位置:默认按照类型去容器查找对应组件,如果找到多个相同类型的会按照组件的id到容器中查找。

注意:自动注入一定要将属性值赋好值,否则会报错。因为注解中required默认是true的,即必须的。可以进行修改。
@Qualifier

使用@Qualifier指定需要装配的组件的id。

例如:当你有两个相同类型的service时,为@Service("userService1"),@Service("userService2");然后你

@Autowired注解了private UserService userService;这样子Spring不知道你需要哪个service,所以你可以在userService上加上@Qualifier("userService1")与自动注入搭配使用。
@Primary

让Spring进行自动装配的时候,默认使用首选的Bean。这个不说了,不过这个注解是加到Bean上的

@Resource

可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;

但是没有能支持@Primary功能没有支持@Autowired(reqiured=false);

@Inject
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
---------------------
作者:Adrian_Dai
来源:CSDN
原文:https://blog.csdn.net/adrian_dai/article/details/80287557
版权声明:本文为博主原创文章,转载请附上博文链接!

Spring注解开发之Spring常用注解的更多相关文章

  1. Mybatis注解开发之&commat;CacheNamespace&colon;实现注解二级缓存的使用

    MyBatis二级缓存使用: 官方建议在service使用缓存,但是你也可以直接在mapper层缓存,这里的二级缓存就是直接在Mapper层进行缓存操作 Mybatis的二级缓存实现也十分简单,只要在 ...

  2. JavaEE开发之Spring中的条件注解组合注解与元注解

    上篇博客我们详细的聊了<JavaEE开发之Spring中的多线程编程以及任务定时器详解>,本篇博客我们就来聊聊条件注解@Conditional以及组合条件.条件注解说简单点就是根据特定的条 ...

  3. JavaEE开发之Spring中的条件注解、组合注解与元注解

    上篇博客我们详细的聊了<JavaEE开发之Spring中的多线程编程以及任务定时器详解>,本篇博客我们就来聊聊条件注解@Conditional以及组合条件.条件注解说简单点就是根据特定的条 ...

  4. Spring注解开发-全面解析常用注解使用方法之生命周期

    本文github位置:https://github.com/WillVi/Spring-Annotation/ 往期文章:Spring注解开发-全面解析常用注解使用方法之组件注册 bean生命周期 ​ ...

  5. JavaEE开发之Spring中Bean的作用域、Init和Destroy方法以及Spring-EL表达式

    上篇博客我们聊了<JavaEE开发之Spring中的依赖注入以及AOP>,本篇博客我们就来聊一下Spring框架中的Bean的作用域以及Bean的Init和Destroy方法,然后在聊一下 ...

  6. JavaEE开发之Spring中的多线程编程以及任务定时器详解

    上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于<JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换&g ...

  7. iOS开发之Xcode常用调试技巧总结

    转载自:iOS开发之Xcode常用调试技巧总结 最近在面试,面试过程中问到了一些Xcode常用的调试技巧问题.平常开发过程中用的还挺顺手的,但你要突然让我说,确实一脸懵逼.Debug的技巧很多,比如最 ...

  8. Spring 的Bean管理的常用注解

    属性注入的注解(使用注解注入的方式,可以不用提供set方法) @Value 用于注入普通类型 @Autowired 自动装配 :默认按类型进行装配  按名称注入 @Qualifier 强制使用名称注入 ...

  9. spring boot之入门Controller常用注解

    Controller常用注解 @Controller  处理http请求 @RestController Spring4之后新加的注解,原来返回json数据需要@ResponseBody配合@Cont ...

随机推荐

  1. iOS Get方式带中文不能请求网络

    今天发现一个蛋疼的问题,使用ASIHTTPRequest Get方式请求数据时候带中文,iOS客户端不能正确进行网络请求. NSURL *url = [NSURL URLWithString:@htt ...

  2. English - little&comma;a little&comma;a few&comma;few的区别

    few.a few.little.a little的区别和联系: few / a few(形容词)用来修饰可数名词,few表示否定意义,没有,几乎没有, a few表示有肯定意思,有几个. 例如:a ...

  3. HDU 1870 愚人节的礼物

    题解:简单的数括号问题…… #include <cstdio> char s[1005]; int main(){ while(scanf("%s",s)!=EOF){ ...

  4. eclipse config 3 构造pydev

    什么是不是说生命是短暂的.我用python 准备工作 sudo apt-get install python3-dev 例如以下操作 依次点击菜单 Help->Install New Softw ...

  5. 初识Dapper

    16年年底开发一个项目,拍卖的项目,对于我这个不入流的程序员来说,雪微是个挑战.程序猿这个行业就是学到老用到老吧.个人比较喜欢sql原生的写法,对EF 还是不怎么感冒,EF 虽然强大,但是用起来还不怎 ...

  6. js模块化&sol;js模块加载器&sol;js模块打包器

    之前对这几个概念一直记得很模糊,也无法用自己的语言表达出来,今天看了大神的文章,尝试根据自己的理解总结一下,算是一篇读后感. 大神的文章:http://www.css88.com/archives/7 ...

  7. Oracle-更新字段-一张表的字段更新另一张的表的字段

    设备表ops_device_info中的终端号terminal_id值是以 'D'开头的字符串,而终端表ops__terminal_info中的终端号terminal_id是8位字符串, 它们之间是通 ...

  8. linux 使用sh&commat;d0ws0cks server

    [root@linux-node1 ~]# cat /etc/*.json { "server":"x.x.x.x", , "lo ...

  9. 第十五节&colon; EF的CodeFirst模式通过DataAnnotations修改默认协定

    一. 简介 1. DataAnnotations说明:EF提供以特性的方式添加到 domain classes上,其中包括两类:  A:System.ComponentModel.DataAnnota ...

  10. TCP 三次握爪 四次挥手

    TCP三次握手和四次挥手过程 1.三次握手 (1)三次握手的详述 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源.Client端接收到ACK报文后也向 ...