美文网首页
spring @configuration 注解中 proxy

spring @configuration 注解中 proxy

作者: simians | 来源:发表于2021-08-16 14:08 被阅读0次

    proxyBeanMethods 默认为true 表示这个类会被代理,如果为false 这个类不会被代理其作用就类似于component注解。而如果为true那么他的作用类似事物的注解凡事带有@Bean的方法将被代理具体的可以看下@Bean注解上的注释就可以知道,最后放了@Bean和@configuration 两个注释的翻译大致能看。

    先说结果:如果设置为true 该类会被代理增强,并且如果在@Bean 里的方法被另一个@bean调用的时候会进入到代理而不是进入到这个@Bean里,如果为false则表示这个类不会被代理,在@Bean里调用另一个@Bean 时会进入到这个@Bean方法里。这样会造成什么样的后果呢?
    那就是被调用的@Bean方法会有两个对象。但是只有一个对象是spirng代理的,另一个是自己创建的并没有被spring代理这样就会有一个问题,那就是这个对象里所有带有spring相关的注解都会失效。举个例子:

    package org.demo.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PostConstruct;
    
    @Order(11)
    //@Service
    public class MyService1  implements MyServiceInterface{
            @Autowired
        public MyService myService;
        //@PostConstruct
        public void init(){
            System.out.println("postConstruce:myService1");
        }
    
        public MyService1(MyService myService) {
            this.myService = myService;
        }
    
        public MyService1() {
        }
    }
    
    
    package org.demo.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PostConstruct;
    import java.util.List;
    
    //@Service
    public class MyService {
        @Autowired
        private List<MyServiceInterface> myServiceInterfaceList;
        private String name;
        private Long height;
        @Autowired
        public MyService1 myService1;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Long getHeight() {
            return height;
        }
    
        public void setHeight(Long height) {
            this.height = height;
        }
    
        public MyService() {
        }
        //@PostConstruct
        public void send(){
            System.out.println("myServicePostConstruct");
        }
    
    
        public void toList(){
            myServiceInterfaceList.stream().forEach(myServiceInterface -> System.out.println(myServiceInterface.getClass()));
        }
    
    
        public MyService(MyService1 myService1) {
            this.myService1 = myService1;
        }
    }
    
    
    package org.demo;
    
    import org.demo.config.MyConfig;
    import org.demo.service.MyService;
    import org.demo.service.MyService1;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Application {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
            annotationConfigApplicationContext.register(MyConfig.class);
            annotationConfigApplicationContext.refresh();
    
            MyService myService = annotationConfigApplicationContext.getBean(MyService.class);
            System.out.println("mian:"+myService);
            MyService1 myService1 = annotationConfigApplicationContext.getBean(MyService1.class);
            System.out.println("mian:"+myService1.myService);
            System.out.println("mian:"+myService);
    
    
        }
    }
    
    
    package org.demo.config;
    
    import org.demo.service.MyService;
    import org.demo.service.MyService1;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import javax.annotation.Resource;
    
    @ComponentScan("org.demo")
    @Configuration(proxyBeanMethods = true)
    @DependsOn(value ="staticProperties")
    public class MyConfig {
        @Resource
        StaticProperties staticProperties;
        @Bean
        public MyService myService(){
            MyService myService = new MyService();
            myService.setHeight(staticProperties.getHeight());
            myService.setName(staticProperties.getName());
            System.out.println("config:myservice:"+myService.toString());
            return myService;
        }
        @Bean
        public MyService1 myService1(){
            MyService myService =myService();
            System.out.println("config:myservice1:"+myService.toString());
            MyService1 myService1 = new MyService1(myService);
            return myService1;
            
        }
    }
    

    myService1.myService!=myService
    这里的不等于是在spring 创建bean的时候其实用户调用的时候仍然是同一个对象
    下图的调用栈显示spirng 会进入intercept 这个方法


    图片.png

    方法的注释:


    图片.png
    运行结果:
    图片.png

    如果我将proxyBeanMethods=false 时再看下调用栈。下图所示这里是直接进入了原方法没有进入代理的方法。


    图片.png

    下面在看下输出的日志:config:是MyConfig类 里输出的
    main:main方法调用getBean后输出的


    图片.png
    这里为啥会main 方法里和config类里的日志不一样呢,那是因为myService1()方法会调用myService()方法new出来新的对象
    但是在对象初始化的时候因为有注解@Autowired 导致我们自己放入的对象被覆盖;这就导致我们new 出来的对象没有用了

    如果我输出myService1.myService.myService1 会是什么样的结果?

    package org.demo.config;
    
    import org.demo.service.MyService;
    import org.demo.service.MyService1;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import javax.annotation.Resource;
    
    @ComponentScan("org.demo")
    @Configuration(proxyBeanMethods = true)
    @DependsOn(value ="staticProperties")
    public class MyConfig {
        @Resource
        StaticProperties staticProperties;
        @Bean
        public MyService myService(){
            MyService myService = new MyService();
            myService.setHeight(staticProperties.getHeight());
            myService.setName(staticProperties.getName());
            System.out.println("config:myservice:"+myService.toString());
            return myService;
        }
        @Bean
        public MyService1 myService1(){
                    //这里会调用的是myService() 会new 出来一个新对象
            MyService myService =myService();
            System.out.println("config:myservice1:"+myService.toString());
            MyService1 myService1 = new MyService1(myService);
            return myService1;
    
        }
    }
    
    

    最后放@Bean的注释,大致还能看

    
    org.springframework.context.annotation @Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE}) 
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented 
    public interface Bean
    extends annotation.Annotation
    指示一个方法产生一个由 Spring 容器管理的 bean。
    概述
    此注释的属性的名称和语义有意类似于 Spring XML 模式中<bean/>元素的名称和语义。 例如:
           @Bean
           public MyBean myBean() {
               // instantiate and configure MyBean obj
               return obj;
           }
       
    豆名
    虽然name属性可用,但确定 bean 名称的默认策略是使用@Bean方法的名称。 这既方便又直观,但如果需要显式命名,可以使用name属性(或其别名value )。 还要注意name接受一个字符串数组,允许为单个 bean 使用多个名称(即一个主要 bean 名称加上一个或多个别名)。
           @Bean({"b1", "b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
           public MyBean myBean() {
               // instantiate and configure MyBean obj
               return obj;
           }
       
    Profile、Scope、Lazy、DependsOn、Primary、Order
    请注意, @Bean注释不为配置文件、范围、惰性、依赖或主要提供属性。 相反,它应该与@Scope 、 @Lazy 、 @DependsOn和@Primary注释结合使用来声明这些语义。 例如:
           @Bean
           @Profile("production")
           @Scope("prototype")
           public MyBean myBean() {
               // instantiate and configure MyBean obj
               return obj;
           }
       
    上述注释的语义与它们在组件类级别的使用相匹配: @Profile允许选择性地包含某些 bean。 @Scope将 bean 的作用域从单例更改为指定的作用域。 @Lazy只有在默认单例范围的情况下才有实际效果。 @DependsOn强制在创建此 bean 之前创建特定的其他 bean,以及 bean 通过直接引用表示的任何依赖项,这通常有助于单例启动。 如果需要注入单个目标组件但多个 bean 按类型匹配, @Primary是一种在注入点级别解决歧义的机制。
    此外, @Bean方法还可以声明限定符注解和@Order值,在注入点解析过程中被考虑,就像相应组件类上的相应注解一样,但每个 bean 定义可能非常独立(如果多个定义具有相同的豆类)。 限定符在初始类型匹配后缩小候选集; 在集合注入点的情况下,顺序值确定解析元素的顺序(多个目标 bean 按类型和限定符匹配)。
    注意: @Order值可能会影响注入点的优先级,但请注意,它们不会影响单例启动顺序,这是由依赖关系和@DependsOn声明确定的正交问题,如上所述。 此外, javax.annotation.Priority在此级别不可用,因为它不能在方法上声明; 它的语义可以通过@Order值结合@Primary在每个类型的单个bean 上建模。
    @Configuration类中的@Bean方法
    通常, @Bean方法在@Configuration类中声明。 在这种情况下,bean 方法可以通过直接调用来引用同一个类中的其他@Bean方法。 这确保了 bean 之间的引用是强类型和可导航的。 这种所谓的“豆间引用”保证尊重范围和 AOP 语义,就像getBean()查找一样。 这些是从原始“Spring JavaConfig”项目中已知的语义,它们需要在运行时对每个此类配置类进行 CGLIB 子类化。 因此, @Configuration类及其工厂方法在此模式下不得标记为 final 或 private。 例如:
       @Configuration
       public class AppConfig {
      
           @Bean
           public FooService fooService() {
               return new FooService(fooRepository());
           }
      
           @Bean
           public FooRepository fooRepository() {
               return new JdbcFooRepository(dataSource());
           }
      
           // ...
       }
    @Bean精简模式
    @Bean方法也可以在没有用@Configuration注释的类中声明。 例如,bean 方法可以在@Component类中声明,甚至可以在普通的旧类中声明。 在这种情况下, @Bean方法将以所谓的“精简”模式进行处理。
    精简模式下的 Bean 方法将被容器视为普通工厂方法(类似于 XML 中的factory-method声明),并正确应用范围和生命周期回调。 在这种情况下,包含类保持不变,并且包含类或工厂方法没有异常约束。
    与@Configuration类中 bean 方法的语义相反,精简模式不支持“bean 间引用” 。 相反,当一个@Bean -method 在精简模式下调用另一个@Bean -method 时,该调用是标准的 Java 方法调用; Spring 不会通过 CGLIB 代理拦截调用。 这类似于@Transactional方法间调用,在代理模式下,Spring 不拦截调用——Spring 只在 AspectJ 模式下这样做。
    例如:
       @Component
       public class Calculator {
           public int sum(int a, int b) {
               return a+b;
           }
      
           @Bean
           public MyBean myBean() {
               return new MyBean();
           }
       }
    引导
    见@ Configuration的javadoc进一步详情,包括如何使用来引导容器AnnotationConfigApplicationContext和朋友。
    BeanFactoryPostProcessor返回@Bean方法
    必须特别考虑返回 Spring BeanFactoryPostProcessor ( BFPP ) 类型的@Bean方法。 因为BFPP对象必须在容器生命周期的早期实例化,所以它们会干扰@Configuration类中的@Autowired 、 @Value和@PostConstruct等注释的处理。 为避免这些生命周期问题, BFPP返回@Bean方法标记为static 。 例如:
           @Bean
           public static PropertySourcesPlaceholderConfigurer pspc() {
               // instantiate, configure and return pspc ...
           }
       
    通过将此方法标记为static ,可以在不导致其声明的@Configuration类的实例化的情况下调用它,从而避免上述生命周期冲突。 但是请注意, @Bean , static @Bean方法不会针对作用域和 AOP 语义进行增强。 这适用于BFPP情况,因为它们通常不被其他@Bean方法引用。 提醒一下,对于具有可分配给BeanFactoryPostProcessor的返回类型的任何非静态@Bean方法,都将发出 WARN 级别的日志消息。
    自从:
    3.0
    也可以看看:
    Configuration 、 Scope 、 DependsOn 、 Lazy 、 Primary 、 org.springframework.stereotype.Component 、 org.springframework.beans.factory.annotation.Autowired 、 org.springframework.beans.factory.annotation.Value
      spring.spring-context.main
    
    
    org.springframework.context.annotation @Target(ElementType.TYPE) 
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented 
    @Component 
    public interface Configuration
    extends annotation.Annotation
    表示一个类声明了一个或多个@Bean方法,并且可能会被 Spring 容器处理以在运行时为这些 bean 生成 bean 定义和服务请求,例如:
       @Configuration
       public class AppConfig {
      
           @Bean
           public MyBean myBean() {
               // instantiate, configure and return bean ...
           }
       }
    引导@Configuration类
    通过AnnotationConfigApplicationContext
    @Configuration类通常使用AnnotationConfigApplicationContext或其支持 web 的变体AnnotationConfigWebApplicationContext引导。 前者的一个简单示例如下:
       AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
       ctx.register(AppConfig.class);
       ctx.refresh();
       MyBean myBean = ctx.getBean(MyBean.class);
       // use myBean ...
       
    有关详细信息,请参阅AnnotationConfigApplicationContext javadocs,有关Servlet容器中的 Web 配置说明,请参阅AnnotationConfigWebApplicationContext 。
    通过 Spring <beans> XML
    作为直接针对AnnotationConfigApplicationContext注册@Configuration类的替代方法,可以将@Configuration类声明为 Spring XML 文件中的普通<bean>定义:
       <beans>
          <context:annotation-config/>
          <bean class="com.acme.AppConfig"/>
       </beans>
       
    在上面的示例中,需要<context:annotation-config/>以启用ConfigurationClassPostProcessor和其他与注释相关的后处理器,这些后处理器有助于处理@Configuration类。
    通过元件扫描
    @Configuration使用@Component元注释,因此@Configuration类是组件扫描的候选者(通常使用 Spring XML 的<context:component-scan/>元素),因此也可以像任何常规@Component一样利用@Autowired / @Inject @Component 。 特别是,如果存在单个构造函数,自动装配语义将透明地应用于该构造函数:
       @Configuration
       public class AppConfig {
      
           private final SomeBean someBean;
      
           public AppConfig(SomeBean someBean) {
               this.someBean = someBean;
           }
      
           // @Bean definition using "SomeBean"
      
       }
    @Configuration类不仅可以使用组件扫描进行引导,还可以使用@ComponentScan注释自己配置组件扫描:
       @Configuration
       @ComponentScan("com.acme.app.services")
       public class AppConfig {
           // various @Bean definitions ...
       }
    有关详细信息,请参阅@ComponentScan javadocs。
    使用外化值
    使用Environment API
    可以通过将 Spring org.springframework.core.env.Environment注入@Configuration类来查找外化值——例如,使用@Autowired注释:
       @Configuration
       public class AppConfig {
      
           @Autowired Environment env;
      
           @Bean
           public MyBean myBean() {
               MyBean myBean = new MyBean();
               myBean.setName(env.getProperty("bean.name"));
               return myBean;
           }
       }
    通过Environment解析的属性驻留在一个或多个“属性源”对象中, @Configuration类可以使用@PropertySource注释将属性源贡献给Environment对象:
       @Configuration
       @PropertySource("classpath:/com/acme/app.properties")
       public class AppConfig {
      
           @Inject Environment env;
      
           @Bean
           public MyBean myBean() {
               return new MyBean(env.getProperty("bean.name"));
           }
       }
    有关更多详细信息,请参阅Environment和@PropertySource javadocs。
    使用@Value注释
    可以使用@Value注释将外部化的值注入到@Configuration类中:
       @Configuration
       @PropertySource("classpath:/com/acme/app.properties")
       public class AppConfig {
      
           @Value("${bean.name}") String beanName;
      
           @Bean
           public MyBean myBean() {
               return new MyBean(beanName);
           }
       }
    这种方法通常与 Spring 的PropertySourcesPlaceholderConfigurer结合使用,它可以通过<context:property-placeholder/>在 XML 配置中自动启用,或者通过专用的static @Bean方法在@Configuration类中显式@Bean (请参阅“关于 BeanFactoryPostProcessor-returning @Bean方法”的@Bean的 javadocs 的详细信息)。 但是请注意,通常仅当您需要自定义配置(例如占位符语法等)时,才需要通过static @Bean方法显式注册PropertySourcesPlaceholderConfigurer 。具体来说,如果没有 bean 后处理器(例如PropertySourcesPlaceholderConfigurer )已注册ApplicationContext的嵌入值解析器,Spring 将注册一个默认的嵌入值解析器,它根据Environment注册的属性源解析占位符。 请参阅以下有关使用@ImportResource使用 Spring XML 组合@Configuration类的部分; 请参阅@Value javadocs; 有关使用BeanFactoryPostProcessor类型(例如PropertySourcesPlaceholderConfigurer详细信息,请参阅@Bean javadocs。
    组合@Configuration类
    使用@Import注释
    @Configuration类可以使用@Import注释组成,类似于<import>在 Spring XML 中的工作方式。 因为@Configuration对象在容器内作为 Spring bean 进行管理,所以可以注入导入的配置——例如,通过构造函数注入:
       @Configuration
       public class DatabaseConfig {
      
           @Bean
           public DataSource dataSource() {
               // instantiate, configure and return DataSource
           }
       }
      
       @Configuration
       @Import(DatabaseConfig.class)
       public class AppConfig {
      
           private final DatabaseConfig dataConfig;
      
           public AppConfig(DatabaseConfig dataConfig) {
               this.dataConfig = dataConfig;
           }
      
           @Bean
           public MyBean myBean() {
               // reference the dataSource() bean method
               return new MyBean(dataConfig.dataSource());
           }
       }
    现在, AppConfig和导入的DatabaseConfig都可以通过仅针对 Spring 上下文注册AppConfig来引导:
       new AnnotationConfigApplicationContext(AppConfig.class);
    使用@Profile注释
    @Configuration类可以用@Profile注释标记,以表明只有在给定的一个或多个配置文件处于活动状态时才应该处理它们:
       @Profile("development")
       @Configuration
       public class EmbeddedDatabaseConfig {
      
           @Bean
           public DataSource dataSource() {
               // instantiate, configure and return embedded DataSource
           }
       }
      
       @Profile("production")
       @Configuration
       public class ProductionDatabaseConfig {
      
           @Bean
           public DataSource dataSource() {
               // instantiate, configure and return production DataSource
           }
       }
    或者,您也可以在@Bean方法级别声明配置文件条件 - 例如,对于同一配置类中的替代 bean 变体:
       @Configuration
       public class ProfileDatabaseConfig {
      
           @Bean("dataSource")
           @Profile("development")
           public DataSource embeddedDatabase() { ... }
      
           @Bean("dataSource")
           @Profile("production")
           public DataSource productionDatabase() { ... }
       }
    有关更多详细信息,请参阅@Profile和org.springframework.core.env.Environment javadocs。
    使用@ImportResource注释的 Spring XML
    如上所述, @Configuration类可以在 Spring XML 文件中声明为常规 Spring <bean>定义。 也可以使用@ImportResource注释将 Spring XML 配置文件导入到@Configuration类中。 可以注入从 XML 导入的 Bean 定义——例如,使用@Inject注释:
       @Configuration
       @ImportResource("classpath:/com/acme/database-config.xml")
       public class AppConfig {
      
           @Inject DataSource dataSource; // from XML
      
           @Bean
           public MyBean myBean() {
               // inject the XML-defined dataSource bean
               return new MyBean(this.dataSource);
           }
       }
    使用嵌套的@Configuration类
    @Configuration类可以相互嵌套,如下所示:
       @Configuration
       public class AppConfig {
      
           @Inject DataSource dataSource;
      
           @Bean
           public MyBean myBean() {
               return new MyBean(dataSource);
           }
      
           @Configuration
           static class DatabaseConfig {
               @Bean
               DataSource dataSource() {
                   return new EmbeddedDatabaseBuilder().build();
               }
           }
       }
    在引导这种安排时,只需要针对应用程序上下文注册AppConfig 。 由于是嵌套的@Configuration类, DatabaseConfig将自动注册。 当AppConfig和DatabaseConfig之间的关系已经隐式明确时,这避免了使用@Import注释的需要。
    另请注意,嵌套的@Configuration类可以与@Profile注释一起使用,以向封闭的@Configuration类提供相同 bean 的两个选项。
    配置延迟初始化
    默认情况下, @Bean方法将在容器引导时急切地实例化。 为了避免这种情况, @Configuration @Lazy可以与@Lazy注释结合使用,以指示类中声明的所有@Bean方法默认是延迟初始化的。 请注意, @Lazy也可以用于单独的@Bean方法。
    对@Configuration类的测试支持
    spring-test模块中可用的 Spring TestContext 框架提供了@ContextConfiguration注释,它可以接受@ContextConfiguration件类引用——通常是@Configuration或@Component类。
       @RunWith(SpringRunner.class)
       @ContextConfiguration(classes = {AppConfig.class, DatabaseConfig.class})
       public class MyTests {
      
           @Autowired MyBean myBean;
      
           @Autowired DataSource dataSource;
      
           @Test
           public void test() {
               // assertions against myBean ...
           }
       }
    有关详细信息,请参阅TestContext 框架 参考文档。
    使用@Enable注释启用内置 Spring 功能
    Spring 的特性,例如异步方法执行、计划任务执行、注解驱动的事务管理,甚至 Spring MVC 都可以从@Configuration类使用它们各自的“ @Enable ”注解来启用和配置。 有关详细@EnableAsync ,请参阅@EnableAsync 、 @EnableScheduling 、 @EnableTransactionManagement 、 @EnableAspectJAutoProxy和@EnableWebMvc 。
    创作@Configuration类时的约束
    配置类必须作为类提供(即不是作为从工厂方法返回的实例),允许通过生成的子类进行运行时增强。
    配置类必须是非最终的(允许在运行时使用子类),除非proxyBeanMethods标志设置为false在这种情况下不需要运行时生成的子类。
    配置类必须是非本地的(即不能在方法中声明)。
    任何嵌套的配置类都必须声明为static 。
    @Bean方法可能不会反过来创建更多的配置类(任何此类实例都将被视为常规 bean,它们的配置注释仍然未被检测到)。
    自从:
    3.0
    也可以看看:
    Bean 、 Profile 、 Import 、 ImportResource 、 ComponentScan 、 Lazy 、 PropertySource 、 AnnotationConfigApplicationContext 、 ConfigurationClassPostProcessor 、 org.springframework.core.env.Environment 、 org.springframework.test.context.ContextConfiguration
      spring.spring-context.main
    

    相关文章

      网友评论

          本文标题:spring @configuration 注解中 proxy

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