美文网首页
Spring 常见问题(2)

Spring 常见问题(2)

作者: _River_ | 来源:发表于2021-05-09 00:01 被阅读0次
    1:@Component 与 @Controller @Service @Repository的关系
    @Controller,@Service  @Repository这些都差不多,其实都是基于@Component注解,
     只不过看起来不同的层用不用的注解比较舒服而已 
     
     也可以认为 @Controller,@Service  @Repository 实际上都是@Component
     
    @ComponentScan作用就是spring启动时候扫描到配置的包后把所有Component注解都实例化出来。
    也就是可以扫描出 @Controller,@Service  @Repository  @Component 等注解
    

    注意:
        @AliasFor(annotation = Component.class)        
        @AliasFor(先了解)后面会详细讲解   
    
    //@Service 注解
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Service {      
        @AliasFor(annotation = Component.class)        
        String value() default "";
     }
    
    2:@Component 与 @Configuration的关系
    @Configuration 注解本质上还是 @Component
    @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
    
    //@Configuration 注解源码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
        String value() default "";
    }
    
    配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
    配置类不能是 final 类(没法动态代理)。
    配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,
    配置类必须是非本地的(即不能在方法中声明,不能是 private)。
    任何嵌套配置类都必须声明为static。
    @Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)
    
    看一个最常见的 @Configuration 与   @Bean
    1:@ComponentScan 扫描 @Component注解
    2:@Configuration 注解本质上还是 @Component
    3:@Bean 获取的是  SpringDataSource类  (有相关的连接参数   如账号密码)
    5:来替换原本SpringDataSource类(没有相关的连接参数)
    6:后续再使用SpringDataSource时  使用的就是替换后的SpringDataSource类(增强类)
    
    @Configuration
    public class JdbcConfiguration {    
        @Bean    
        public DataSource dataSource(JdbcProperties jdbcProperties) {        
            DruidDataSource dataSource = new DruidDataSource();        
            dataSource.setPassword("password");        
            dataSource.setUsername("userName");        
            dataSource.setUrl("jdbc:mysql://47.115.38.181:3306/data_name");        
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");        
             return dataSource;   
            }
    }
    
    
    3:@Configuration加载过程 主要做了什么
    替换@Configuration中添加@Bean注解的类对象   实际上是替换了类
    

    Spring 容器在启动时,会加载默认的一些 PostPRocessor,其中就有 ConfigurationClassPostProcessor,
    这个后置处理程序专门处理带有 @Configuration 注解的类,
    这个程序会在 bean 定义加载完成后,在 bean 初始化前进行处理。
    主要处理的过程就是使用 cglib 动态代理增强类,而且是对其中带有 @Bean 注解的方法进行处理。
    
    ConfigurationClassPostProcessor里面具有核心方法enhanceConfigurationClasses
    1:第一次循环中,查找到所有带有 @Configuration 注解的 bean 定义
    2:第二个 for 循环中 使用cglib代理的方式 对类进行增强  增强后的类替换了原有的 beanClass
    3:所以到此时,所有带有 @Configuration 注解的 bean 都已经变成了增强的类。
    
    4:@Component 与 @Configuration的区别(了解即可)
         观察以下逻辑: 
         userInfo() 中调用 country() 时,这里的 Country 和上面 @Bean 方法返回的 Country 是不是同一个对象?
         
         添加@Configuration  由于使用cglib代理的方式       是同一个对象
         添加@Component     由于没使用cglib代理的方式     是两个对象
    
    @Configuration
    @Component
    public class MyBeanConfig {
    
        @Bean
        public Country country(){
            return new Country();
        }
    
        @Bean
        public UserInfo userInfo(){
            return new UserInfo(country());
        }
    
    }
    

    那么有没有一种方法 既不使用cglib代理的方式的方式 又保证Country是同一个对象
    使用以下方式即可:
    这个时候 return new UserInfo(country);  获取的就是 return new Country(); 的Bean对象
    
    @Component
    public class MyBeanConfig {
    
        @Autowired
        private Country country;
    
        @Bean
        public Country country(){
            return new Country();
        }
    
        @Bean
        public UserInfo userInfo(){
            return new UserInfo(country);
        }
    
    }
    
    5:@AliasFor注解
    在上面我们经常可以看到@AliasFor注解 那么这个注解有什么作用呢
    
    1:继承父注解 使用父注解功能
    2:起别名方便使用该注解时的理解(顾名思义)
    3:组合父注解的功能  并且通过  起别名的方式方便理解   产生一个新注解
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    @Documented
    public @interface AliasFor {
        @AliasFor("attribute")
        String value() default "";
     
        @AliasFor("value")
        String attribute() default "";
     
        Class<? extends Annotation> annotation() default Annotation.class;
    }
    
    1:继承注解的功能
    如@Controller,@Service,@Repository都继承了@Component的功能
    他们的基本作用和@Component完全一样都是标明某个类是Spring的Bean,需要Spring容器进行管理。
    不同之处在于对Spring bean进行了归类。
    
    这个也就是本文章开头 提到的
    @Service 现在就获取到了 @Component的功能了
    
    //@Service 注解
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Service {      
        @AliasFor(annotation = Component.class)        
        String value() default "";
     }
    
    2:同个注解中为同一个功能定义两个名称不一样的属性
    1:在同个注解中为同一个功能定义两个名称不一样的属性,那么这两个属性彼此互为别名
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Mapping
    public @interface RequestMapping {
        
        String name() default "";
        
        @AliasFor("path")
        String[] value() default {};
        
        @AliasFor("value")
        String[] path() default {};
        
        RequestMethod[] method() default {};
        ...
    } 
    
    这么做的目的在于
    1.更便捷
    当我们只定义一个属性的时候往往可以省略属性名如:
    @RequestMapping(“/user”)
    2.顾名思义
    当我门定义多个属性时为了能做到顾名思义
    使之达到一目了然的效果我们需要选择一个更加贴合特定场景的名称。
    @RequestMapping(path = “/user”,method = RequestMethod.GET)
    当然你也可以这样:
    @RequestMapping(value = “/user”,method = RequestMethod.GET)
    

    3:把多个元注解的属性组合在一起形成新的注解
        如我们熟知的@SpringBootApplication 
        @SpringBootApplication并没有定义新的属性而是复用其他注解已有的注解属性并对其进行组合
        形成新的注解从而到达到便捷的目的。这样的注解我们可以称之为复合注解。
        所以在使用SpringBoot 时我们只需要@SpringBootApplication一个注解就能开启自动配置,自动扫描的功能。
        而不再需要使下面三个注解来达到同样的目的。
        @Configuration
        @ComponentSan
        @EnnableAutoConfiguration
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM,
                    classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
     
         //获取了  @EnableAutoConfiguration功能
        @AliasFor(annotation = EnableAutoConfiguration.class)
        Class<?>[] exclude() default {};
     
         //获取了  @EnableAutoConfiguration功能
        @AliasFor(annotation = EnableAutoConfiguration.class)
        String[] excludeName() default {};
     
        //获取了   @ComponentSan功能
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
        String[] scanBasePackages() default {};
       //获取了    @ComponentSan功能
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
        Class<?>[] scanBasePackageClasses() default {};
     
    }
    
    6:@ComponentScan 里面有什么
    @ComponentScan 可以扫描包 也可以 扫描类
    
    @ComponentScan({"com.company.order","com.company.user"})
    @ComponentScan(basePackageClasses={XxxService.class}) 
    
     @ComponentScan 其实也就是扫描带有 @Component注解的类而已
     然后把扫描到的类 存放在Spring容器中
     
     加入类里面有类似@Bean注解  也会把该@Bean注解返回的类放入到 Spring容器中
    
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
        //指定包扫描路径,value属性的值,就是项目中的一个具体路径。
        @AliasFor("basePackages")
        String[] value() default {};
        
        //指定包扫描路径,basePackages属性的值,就是项目中的一个具体路径
        @AliasFor("value")
        String[] basePackages() default {};
        ...
    }
    

       相关参考教程:
        @Component 与 @Configuration的关系 :
            https://blog.csdn.net/isea533/article/details/78072133
        SpringBoot中的@AliasFor注解:
            https://blog.csdn.net/u012043390/article/details/89391518

    相关文章

      网友评论

          本文标题:Spring 常见问题(2)

          本文链接:https://www.haomeiwen.com/subject/owzpdltx.html