zoukankan      html  css  js  c++  java
  • Spring注解开发之Spring常用注解

    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
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    liunx 解压与压缩
    缓存设计与优化
    易混乱javascript知识点简要记录
    初识RedisCluster集群
    Redis Sentinel(哨兵模式)
    JavaScript作用域简单记录
    JavaScript引用类型简单记录
    redis主从复制初识
    javascript基础知识点
    持久化的一些问题
  • 原文地址:https://www.cnblogs.com/jimcsharp/p/9907934.html
Copyright © 2011-2022 走看看