GVKun编程网logo

基于属性(来自配置服务器)是否覆盖/替换基于Java的路由配置?(基于属性的处理方法有哪些)

11

这篇文章主要围绕基于属性和来自配置服务器是否覆盖/替换基于Java的路由配置?展开,旨在为您提供一份详细的参考资料。我们将全面介绍基于属性的优缺点,解答来自配置服务器是否覆盖/替换基于Java的路由配

这篇文章主要围绕基于属性来自配置服务器是否覆盖/替换基于Java的路由配置?展开,旨在为您提供一份详细的参考资料。我们将全面介绍基于属性的优缺点,解答来自配置服务器是否覆盖/替换基于Java的路由配置?的相关问题,同时也会为您带来7.12 Java-based container configuration (基于java的容器配置)、AJAX Https 请求显示 CORS 错误 (来自本地服务器)、Angular23 loading组件、路由配置、子路由配置、路由懒加载配置、c# – 基于属性的路由VS基于约定的路由 – ASP.net核心RESTful API的最佳实践的实用方法。

本文目录一览:

基于属性(来自配置服务器)是否覆盖/替换基于Java的路由配置?(基于属性的处理方法有哪些)

基于属性(来自配置服务器)是否覆盖/替换基于Java的路由配置?(基于属性的处理方法有哪些)

在问题线程中回答 https://github.com/spring-cloud/spring-cloud-gateway/issues/1953#issuecomment-705081934

TL;博士 需要开启 CachingRouteLocator

 @Bean 
 @Primary 
 @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator") 
 // TODO: property to disable composite? 
 public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) { 
    return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators))); 
 } 

7.12 Java-based container configuration (基于java的容器配置)

7.12 Java-based container configuration (基于java的容器配置)

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-java-instantiating-container-web ##7.12 Java-based container configuration (基于java的容器配置) ###7.12.1 Basic concepts:@Bean and @Configuration spring对于新的java配置的支持的核心武器是@Configuration-annotated类以及@bean-annotated方法.

@Bean注解是来标记一个可以实例化,配置,初始化一个新的有spring-ioc容器管理的新的对象.这里有些像spring的xml配置中的<beans/>标签,@Bean则相当于里面的<bean>标签.你可以讲@Bean标记到任何@Components类的方法上,但是,它们一般只在@Configuration的beans里使用.

用@Configuration表明他的主要目标是作为bean定义的资源.另外,@Configuration类允许通过调用同一个类里面的@Bean标注的方法定义的内部bean依赖.最简单的@Configuration类用法如下:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

}

上面的AppConfig类等价于下面的<beans/> xml:

<beans>
    <bean id="myService"/>
</beans>

####Full @Configuration vs ''lite'' @Beans mode? 当 @Bean方法出现在没有@Configuration注解标志的类中时,它们实际上是在''lite''模式下处理的.例如,在@Component类里或一个POJO类里定义的bean方法都认为是''lite'';

不同于full @Confuguration,lite @Bean 方法不易申明内部bean依赖.一般在''lite''模式下,一个@Bean方法不能调用其他的@Bean方法.

只有使用在@Configuration类里的@Bean方法才是保证''full''模式经常被使用的好方法.它可以阻止相同的@Bean方法意外的被多次调用,并有助于减少在''lite''模式下很难被追踪的微妙的bugs.

@Bean和@Configuration注解会在以后的部分深入讨论.但是,首先让我们来了解使用java-based configuration来创建spring容器的几种方式吧. ###7.12.2 Instantiating the Spring container using AnnotationConfigApplicationContext(用AnnotationConfigApplicationContext来实例化spring上下文容器) 本节中的AnnotationConfigApplicationContext,是spring3.0的新加内容.这个ApplicationContext的变种实现,不仅能够接受@Configuration的类,还可以接受平常的@Component类和JSR-330元数据标注的类.

当@Configuration类作为输入类时,@Configuration类本身会注册为一个bean,且所有声明的@Bean方法也会注册为一个bean定义.

当@component和JSR-330类被提供时,他们会被注册为bean定义.并且所有的依赖注入元数据例如@Autowired或@Inject将会被使用在这些类需要的地方.

###简单的构造器 同实例化一个ClassPathXmlApplicationContext时使用spring xml文件一样,实例化一个AnnotationConfigApplicationContext时也会使用@Configuration类.它允许的spring容器完全不使用xml.

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

和上文提到的一样,AnnotationConfigApplicationContext不止限于@Configuration类.任何@Componnet和用JSR-330标记的类都可以作为入参提供到构造器里,如下:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

以上认为MyServiceImpl, Dependency1 and Dependency2等类使用了spring依赖注入的注解如@Autowired.

####Building the container programmatically using register(Class<?>...) 使用注册器来动态创建容器 一个AnnotationConfigApplicationContext可以被一个无参的构造器实例化并用register()方法进行注册.这个方式在动态构建一个AnnotationConfigApplicationContext是特别有用.

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

####Enabling component scanning with scan(String ..) 可用组件扫描 要保证组件扫描,只如下标志你的@Configuration类即可:

@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig  {
    ...
}

有经验的spring用户可以发现其用法和xml使用context命名空间相似:

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

在上面的例子中,com.acme包将被扫描,查找任意被@Component标志的类,这些类将注册为spring容器里的bean定义.

AnnotationConfigApplicationContext 暴露了scan(String..)方法,使其有component-scanning的功能.

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();//进行注册
    MyService myService = ctx.getBean(MyService.class);
}

记住@Configuratin类的元注解也是@Component,所以它们是组件扫描的候选者.在上面的例子中,默认AppConfig是在com.acme包里的,在调用scan()方法它会被扫描,在refrsh()方法之后所有的@Bean方法会被容器处理并注册为一个bean定义. ####Support for web applications with AnnotationConfigWebApplicationContext(对web应用的支持)

AnnotationConfigApplicationContext的WebApplicationContext的版本可以通过AnnotationConfigWebApplicationContext实现.当你配置spring的ContextLoaderListener servlet Listener,spring MVC DispatcherServlet时会非常有用.下面是一个典型的spring MVC的web应用配置的web.xml.记住context-param和init-param参数的使用.

<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

###7.12.3 使用@Bean注解 @Bean是一个方法级别的注解,且是xml中<bean/>元素的替代物.这个注解支持<bean/>里的一些属性,例如:init-method,destroy-method,autowiring ,name.

你可以在用@Configuration或@Component标志的类里使用@Bean注解. ####Declaring a bean 声明一个bean 要申明一个bean,可以简单的用@Bean来标注一个方法.你可以在ApplicationContext中用这个方法的特定返回值类型来注册一个bean定义.默认,bean的名称同方法名相同.下面是使用@Bean方法申明的简单例子;

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

上面的配置等价于下面的spring xmlns

<beans>
    <bean id="transferService"/>
</beans>

这两个申明都可以在ApplicationContext中注册transferService,限制这个实例类型为TransferServiceImpl:

transferService->com.acme.TransferServiceImpl

####Bean dependencies (Bean的依赖) 一个@bean标记的方法可以有任意数量的参数来描述构建这个bean所必需的依赖.例如如果我们的TransferService需要一个AccountRepository,我们可以通过方法参数来实现这个依赖.

@Configuration public class AppConfig {

@Bean
public TransferService transferService(AccountRepository accountRepository) {
    return new TransferServiceImpl(accountRepository);
}

}

这个解决机制是基于构造器依赖注入的替代物,查看相关片段已查看更多细节.

Recieving lifecycle callbacks (接受生命周期回调)

任何被@Bean注解标记的类都支持常规的生命周期回调,并可以使用@PostConstruct和@PreDestroy注解.

常规的spring生命周期回调同样支持.如果一个bean实现了InitializingBean,DisposableBean或者Lifecycle,他们的表现方法会被容器调用.

也完全支持*Aware接口的标准的设置,如BeanFactoryAware,BeanNameAware,MessageSourceAware,ApplicationContextAware等.

这个@Bean注解支持指定的任意初始化和销毁回调方法,同spring xml元素里的init-mehod,destroy-method属性一致.

public class Foo {
  public void init() {
      // initialization logic
  }
}

public class Bar {
  public void cleanup() {
      // destruction logic
  }
}

@Configuration
public class AppConfig {

  @Bean(initMethod = "init")
  public Foo foo() {
      return new Foo();
  }

  @Bean(destroyMethod = "cleanup")
  public Bar bar() {
      return new Bar();
  }

}

一般而言,使用java配置定义的beans都有一个公共的可以被销毁回调调用的close或shutdown方法.如果你有一个公共的close或shutdown方法且你不希望在容器关闭时它们被调用,简单的在你的bean定义上加上@Bean(destroyMethos="")来屏蔽这个默认模式.你可能想这么做,通过默认的JNDI来获取资源但它的生命周期管理需要在应用之外.特别,DataSource需要这么处理,因为在Java EE应用服务中它是个众所周知的问题.

@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS");
}

另外,用@Bean标注的方法,你一般会选择使用动态JNDI查找策略:spring的JndiTemplate或JndiLocateDelegate helpers或者直接的JNDI InitialContext用法,而不是JndiObjectFactoryBean的变种.JndiObjectFactoryBean会强迫你将返回类型定义为FactoryBean而不是实际的目标类型,结果在其他@Bean方法中需要指定该bean提供资源时会很难交叉引用调用.

当然,在上个例子的Foo中,等同于下面在构造器中调用init()方法.

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        Foo foo = new Foo();
        foo.init();
        return foo;
    }

    // ...

}

当你直接使用java时,你可以直接同你的对象来做任何事,且不需要依赖容器的生命周期.

###Specifying bean scope 指定bean的作用域 ####Using the @Scope annotation使用@Scope注解 你可以用指定你的用@Bean标记的bean定义有一个特定的作用域.你可以使用在Bean Scope节里的任意标准的作用域.

默认作用域是单例(singleton),但你可以用@Scope注解重写该作用域

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }

}

###@Scope and scoped-proxy 作用域和作用域代理 spring通过scopes proxies提供一个合适的方式来同作用域依赖工作.最简单创建代理这种代理的方式是使用XML配置时的aop:scoped-proxy/元素.用@Scope配置你的beans用proxyMode属性提供了相同的支持.默认是没有代理的(ScopedProxyMode.NO),你可以指定为ScopedProxyMode.TRAGET_CLASS或ScopedPrxoyMode.INTERFACES. 如果你要将scoped proxy从xml引用文档转为使用java配置的Bean,你可以如下使用:

@Bean
@SessionScope
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}

####自定义bean命名 一般,配置类使用@bean方法的名字作为该bean的名字.不过,这个功能可以使用name属性来重写.

@Configuration
public class AppConfig {

    @Bean(name = "myFoo")
    public Foo foo() {
        return new Foo();
    }

}

###Bean aliasing (Bean的别名) 同7.3.1''Naming beans''讨论的那样,有时要给单个的bean多个名字,这就是Bean aliasing.@Bean注解的name属性接受一个String数组,这样可以了.

@Configuration
public class AppConfig {

    @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }

}

####Bean description bean描述 有时给bean提供详细的文本描述非常有帮助.特别是当这些bean暴露(通过JMX)出来以用于监控时. 你可以这样给一个Bean添加描述

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Foo foo() {
        return new Foo();
    }

}

###7.12.4 Using the @Configuration annotation(使用@Configuration 注解) @Configuration是一个类级别的注解来标注该对象是一个bean定义的资源.@Configuration类通过@Bean注解方法来声明beans定义.调用@Configuration类的@Bean方法也可以用于定义内部bean依赖.

####Injecting inner-bean dependencies(注入内置的依赖) 当一个Bean有其他依赖时,你可以这样表示一个依赖有一个bean方法来调用其他bean:

@Configuration
public class AppConfig {

   @Bean
   public Foo foo() {
       return new Foo(bar());
   }

   @Bean
   public Bar bar() {
       return new Bar();
   }

}

在上面的例子中,foo的bean通过构造器注入来接受bar的引用;;

注:只有在@Configuration类使用@Bean注解申明的方法时,这种申明内部bean依赖的方法才有效.你不能在平常的@Component类里使用内部bean依赖.

####Lookup method injection(查找方法注入) 和以前提过的一样,查找方法注入是一个我们很少使用的高级功能.当一个单例bean有一个原生作用域依赖时,这点会很有用.为这种配置类型使用java配置,它为实现这个模式提供了一个合适的方式;

public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();

        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
    return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

使用java配置支持,你可以创建一个CommandManager的子类并使用这种方式来重写它的createCommand()方法,这样它就能查找一个新的command对象.

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with command() overridden
    // to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}

貌似也要在@Configuration类中.一个单例bean有一个原生bean的引用. ####Further information about how java-based configuration works internally 基于java配置协同工作的更多信息 下面的例子,一个@Bean方法被调用两次:

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }

}

clientDao()方法在clientService1()和clientService2()方法里分别调用一次.既然这个方法串讲一个新的ClientDaoImpl的实例并返回它,那么你应该期待两个实例(一个service一个).这个定义会出现问题:在spring里,实例bean默认是单例作用域.这个关键点在于,所有的@Configuration类在启动时都是用CGLIB代理的子类启动的.在子类中,子方法在父方法调用和创建新实例之前首先查找容器中缓存的bean.记住在spring3.2中,你不需要额外引用CGLIB的jar包了,因为它已经在org.springframework.cglib的包下了,并收到了spring核心jar包里了.

这个行为会根据作用域的不同而差异.我们只讨论单例情况.

因为CGLIB动态代理在启动时需要添加功能所以这里有些限制,特别是配置类必须不是final.但是,4.3之后,配置类中允许所有的构造器,包括使用了@Autowired的,或无参构造器来申明默认注入.

如果你更倾向于避免CGLIB强加的限制,可以考虑在非@Configuration类里申明@Bean方法,例如,使用@Component类替代.@Bean方法之间的交叉调用不会被拦截,所有你必须依赖构造器或方法级别的依赖注入.

###7.12.5 Composing java-based configurations 集成java配置 ####Using the @Import annotaion 就像<import/>元素使用在spring的xml文件里的模块化配置中使用的一样,这个@Import注解允许从其他配置类里导入bean的定义.

@Configuration
public class ConfigA {

     @Bean
    public A a() {
        return new A();
    }

}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }

}

现在,当你实例化上下文时不需要指定ConfigA.class或ConfigB.class,你只需明确的提供ConfigB的类.

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

这种方法简化了容器实例化,因为只有一个类需要处理,而不需要开发者在构造期间记住大量的@Configuration类.

在spring 4.2中,@Import还支持常规组件类,与AnnotationConfigContext.register()方法相似.当你要避免组件扫描时,使用少量的配置类作为定义你们所有组件的关键点. ####Injecting dependencies on imported @Bean definitions 通过@Import来引入bean定义 上面的例子虽然很简洁,但仍可以其作用.在大多数实际场景里,bean会用其他配置文件里的依赖.当使用xml时,这不是问题.因为这里没有编译器,且可以简单的宣布ref="someBean",并相信spring会在容器初始化时解决好.当然,当使用@Configuration类时,在这种配置模式下java编译器会提出一些限制,就是这些引用的bean必须经过java语法的检验;

但是,解决这个问题很简单,就想我们之前讨论的,@Bean方法可以有任意数量的参数来描述bean的依赖.让我们讨论一个使用@Configuration类的真实场景,每个bean都有其他类中的依赖:


@Configuration
public class ServiceConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }

}

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }

}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

这里有其他的方式来达到相同的效果.记住@Configuration类是容器里的其他bean.这意味着你也可以像其他bean一样利用@Autowired和@Value进行注入.

保证你用最简单的方式来注入依赖.@Configuration类会在容器初始化时就会处理,并强制要注入的依赖超出预期的过早初始化.如有可能,重组上个例子中的基于参数的注入.

另外,通过@Bean来处理BeanPostProcessor和BeanFactoryPostProcessor定义时要特别小心.他们一般要申明为静态Bean方法,无需触发到包含它们的配置类的实例化.另外,@Autowired和@Value不会对配置类自身其作用,因为它作为一个bean实例创建太早了.

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }

}

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    @Autowired
    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

spring4.3支持@COnfiguration类里的构造器注入.记住如果目标bean只有一个构造器,那就不需要指定@Autowired的值.上面的例子,Repository的构造器无需添加@Autowired注解.

在上面的例子中,使用@Autowired作用得当,并提供了理想的模块,但在表现注入的bean定义需要申明这方面仍有些模糊.例如,一个开发者查找ServiceConfig,你怎么能准确的知道@Autowired AccounRepository注入的bean是哪一个呢.它可能在代码中不明确,但却是最好的那个.Spring Tool Suite提供了一个工具,它可以画出所有需要注入bean的层次图表,可能是所有你需要的bean.当然,你的其他java IDE也可以轻易的发现所有的AccountRepository类的申明和使用,并快速的表明所有返回该类型的@Bean方法的位置.

当歧义没有被接受,你想用你的IDE直接导航到另一个@Configuration类中,可以考虑注入配置类本身:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        // navigate ''through'' the config class to the @Bean method!
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }

}

在上面的例子中,AccountRepository定义的地方非常明确.但是,ServiceConfig现在和RepositoryConfig类耦合了,这是个折中.这个轻微耦合在使用基于接口或抽象类的@Configuration类时会减轻.思考以下:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();

}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(...);
    }

}

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

现在ServiceConfig通过集成DefaultRepositoryConfig,其耦合度已降低;而内置的IDE工具仍有效,对开发者来说很容易获得RepositoryConfig实现的类型结构.通过这种方式,导航到@Configuration类和它们的依赖和一般的导航到易于接口的代码没有太大的差异.

Conditionally include @Configuration classes or @Bean methods

根据任意系统状态,用条件控制使@Configuration类或单个的@bean方法有效或无效很有用.一个平常的例子是当spring环境中只有一个特定的profile可用时,使用@Profile注解来启动beans.

@Profile注解实际上是使用更灵活的@Conditional注解实现的.这个@Conditional注解是特定的org.springframework.context.annotation.Condition的实现,表明在@Bean注册之前应该先询问它.

实现Condition接口提供了一个返回true或false的matches()方法.例如,这里有一个使用@Profile的Condition实际实现;

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    if (context.getEnvironment() != null) {
        // Read the @Profile annotation attributes
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                    return true;
                }
            }
            return false;
        }
    }
    return true;
}

查看@Conditional的javadoc来获取更多内容.

####Combining java and Xml configuration 基础java和XML配置

spring的@Configuration类的支持并不打算100%的替代spring的xml配置.一些基础设施如spring的xml命名空间仍是解决容器配置的理想方式.当xml适使或必须时,你可以如此:可以用XML-centric的方式实例化你的容器,例如,ClassPathXmlApplicationContext,或者用"java-centric"方式使用AnnotationConfigApplicationContext或@ImportResource注解来引入需要的XML.

####XML-centric use of @Configuration classes 以xml为中心使用@Configuration

当你倾向于从xml里使用spring容器并以ad-hoc的方式引入@Configuration类.例如,如果已存在大量的使用spring XML的代码,那么在需求上创建@Configuration类和在xml里引入它们会更加容易.下面你会找到在"XML-centric"情景下使用@Configuration类的技巧;

记住@Configuration里的类最后都是容器里的bean定义.在这个例子,我们新建了一个名为AppConfig的@Configuration类,并在system-test-config.xml里作为<bean/>定义引入它.因为context:annotation-config/已经转换,这个容器将会识别@Configuration注解,并正常处理AppConfig里的@Bean方法.

@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }

}

system-test-config.xml:

<beans>
    <!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean/>

    <bean>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

jdbc.properties:

jdbc.username=sa
jdbc.password=

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

注:在上面的system-test-config.xml里,AppConfig的<bean/>没有申明id属性.之所以如此,是因为没有其他bean引用时不需要命名,而且容器也不大可能通过命名来获取它.对比DataSource的bean,它只能通过类型进行注入,所以一个明确的bean的id属性不是必须的.

针对以上场景,你可以设置路径扫描;component-scan包含annotation-scan的功能,所以不需要annotation-scan,通过设置component-scan的base-package属性来定义;现在system-test-config.xml如下:

<beans>
    <!-- picks up and registers AppConfig as a bean definition -->
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

####@Configuration class-centric use of XML with @ImportResource 通过使用@ImportResource来引入xml文件,这个是java-centric的做法,该注解需指明xml文件的位置

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }

}

properties-config.xml

<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

jdbc.properties

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

然后我们可以像在xml里一样使用property-placeholder里的属性了.

AJAX Https 请求显示 CORS 错误 (来自本地服务器)

AJAX Https 请求显示 CORS 错误 (来自本地服务器)

转到该网址并点击 Raw 按钮并使用结果 raw.githubusercontent.com 启用 CORS 的网址

$.getJSON('https://raw.githubusercontent.com/sab99r/Indian-States-And-Districts/master/states-and-districts.json')
.then(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Angular23 loading组件、路由配置、子路由配置、路由懒加载配置

Angular23 loading组件、路由配置、子路由配置、路由懒加载配置

 

1 需求

  由于Angular是单页面的应用,所以在进行数据刷新是进行的局部刷新;在进行数据刷新时从浏览器发出请求到后台响应数据是有时间延迟的,所以在这段时间就需要进行遮罩处理来提示用户系统正在请求数据。

 

2 loading组件简介

  loading组件就是专门负责遮罩处理的,可以自定一个loading组件,也可以使用别人创建号的loading模块;loading组件生效后的效果如下:

  参考资料:点击前往

    

 

3 编程步骤

  3.1 创建一个angular项目

    技巧01:版本必须是angular4及以上

  3.2 创建一个组件

  3.3 创建一个user模块

    技巧01:在user模块中创建多个组件

  3.4 路由配置

    技巧01:每个模块单独设置路由配置文件

    技巧02:利用路由实现模块懒加载

    3.4.1 子模块路由配置文件

      技巧01:子模块配置类中需要使用 forCild

        

      技巧02:子模块的配置文件配置好后需要在子模块中引入配置文件,直接引入配置模块中的那个类就行啦

        

import { NgModule } from ''@angular/core'';
import { Routes, RouterModule } from ''@angular/router'';
import { UserListComponent } from ''./user-list/user-list.component'';
import { UserHomeComponent } from ''./user-home/user-home.component'';
import { UserInfoComponent } from ''./user-info/user-info.component'';

const routes: Routes = [
  {
    path:'''',
    component:UserHomeComponent,
    children: [
        {
            path:'''',
            redirectTo:''userList'',
            pathMatch:''full''
        },
        {
            path:''userList'',
            component:UserListComponent
        },
        {
            path:''userInfo'',
            component:UserInfoComponent
        }
    ]
}
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class UserRoutingModule { }
user.routing.module.ts
import { NgModule } from ''@angular/core'';
import { CommonModule } from ''@angular/common'';

import { UserRoutingModule } from ''./user-routing.module'';
import { UserListComponent } from ''./user-list/user-list.component'';
import { UserInfoComponent } from ''./user-info/user-info.component'';
import { UserEditComponent } from ''./user-edit/user-edit.component'';
import { UserDetailComponent } from ''./user-detail/user-detail.component'';
import { UserListsComponent } from ''./user-lists/user-lists.component'';
import { UserHomeComponent } from ''./user-home/user-home.component'';

@NgModule({
  imports: [
    CommonModule,
    UserRoutingModule
  ],
  declarations: [UserListComponent, UserInfoComponent, UserEditComponent, UserDetailComponent, UserListsComponent, UserHomeComponent]
})
export class UserModule { }
user.module.ts

  3.4.2 根模块路由配置

    技巧01:根模块的路由配置文件中需要用 forRoot

      

    技巧02:需要在根模块中引入根路由配置类

      

import { LoginComponent } from "./login/login.component";
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";


export const routes = [
    {
        path:'''',
        redirectTo:''login'',
        pathMatch:''full''
    },
    {
        path: ''login'',
        component: LoginComponent
    },
    {
        path:''user'',
        loadChildren:''./user/user.module#UserModule''
    },
    {
        path:''**'',
        component: LoginComponent
    }
]

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
  })
  export class AppRoutesModule { }
app.routes.module.ts
import { BrowserModule } from ''@angular/platform-browser'';
import { NgModule } from ''@angular/core'';

import { NgZorroAntdModule } from ''ng-zorro-antd'';
import { LoadingModule, ANIMATION_TYPES  } from ''ngx-loading'';

import { AppComponent } from ''./app.component'';
import { TestDemoComponent } from ''./test-demo/test-demo.component'';
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { LoginComponent } from ''./login/login.component'';
import { RouterModule } from ''@angular/router'';
import { AppRoutesModule } from ''./app.routes.module'';


@NgModule({
  declarations: [
    AppComponent,
    TestDemoComponent,
    LoginComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    LoadingModule.forRoot({
      animationType: ANIMATION_TYPES.wanderingCubes,
      backdropBackgroundColour: ''rgba(0,0,0,0.1)'', 
      backdropBorderRadius: ''4px'',
      primaryColour: ''#ffffff'', 
      secondaryColour: ''#ffffff'', 
      tertiaryColour: ''#ffffff''
  }),
    NgZorroAntdModule.forRoot(),
    AppRoutesModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
app.module.ts

  3.5 集成loading模块

    3.5.1 下载相关依赖

npm install --save ngx-loading

    3.5.2 在模块级别引入

      技巧01:loading模块需要共享,所以需要在共享模块或者跟模块进行引入

        

    3.5.3 在组件级别使用loading组件

      3.5.3.1 html编写

        

<div class="my-container">
  <ngx-loading [show]="loading" [config]="config"></ngx-loading>
  <h2>
    这是登录页面 
  </h2>
  <hr />
  
  <label for="username">用户名</label>
  <input type="text" id="username" name="username" /> 
  <br />
  <label for="password">用户密码</label>
  <input type="password" id="password" name="password" />
  <button (click)="on_login_click()">登陆</button>
  
</div>
LoginComponent.html

       3.5.3.2 ts编写

        技巧01:点击登陆按钮后,开启遮罩;之后间隔5秒后交替开启遮罩

import { Component, OnInit } from ''@angular/core'';
import { ANIMATION_TYPES } from ''ngx-loading'';

@Component({
  selector: ''login'',
  templateUrl: ''./login.component.html'',
  styleUrls: [''./login.component.css'']
})
export class LoginComponent implements OnInit {
  loading: boolean = false;
  config: object = {};

  private timer;

  constructor() {
    
  }

  ngOnInit() {
    this.config = {
      
      animationType: ANIMATION_TYPES.rectangleBounce,
      backdropBorderRadius: ''0px'',
      // backdropBackgroundColour: ''#9f9ec8'',
      fullScreenBackdrop: true,
      primaryColour: ''skyblue'',
      secondaryColour: ''red''
    }
  }

  on_login_click() {
    this.loading = true;
    this.timer = setInterval(
      () => {
        this.loading = !this.loading;
      },
      5000
    );
    alert("登陆");
  }

  ngOnDestroy() {
    if (this.timer) {
      alert(''清除'');
      clearInterval(this.timer);
    }
  }

}
LoginComponent.ts

  3.6 loading模块源代码

    参考资料 -> 点击前往

   3.7 本博文源代码

    获取源代码 -> 点击前往

 

c# – 基于属性的路由VS基于约定的路由 – ASP.net核心RESTful API的最佳实践

c# – 基于属性的路由VS基于约定的路由 – ASP.net核心RESTful API的最佳实践

我是一名经验丰富的.NET开发人员,在过去的几天里,我在.NET Core中迈出了第一步,更具体地说是使用ASP.net Core构建RESTful API.

我知道有两种可能的路由选项 – 约定路由(传统)与基于属性的路由(更新).

我读过ASP.net核心开发团队的建议是使用基于属性的路由来实现API,而不是传统的基于约定的路由,它更适合于MVC应用程序.

我真的很想明白 – 为什么?

似乎基于属性的路由使我们编写更多代码,最终具有与传统路由相同的行为.

解决方法

这两个选项都有效.以下是关于何时使用每一个的一些建议.
考虑选择传统路线:

>您希望集中配置所有路线.
>您使用自定义约束对象.
>您有一个不想更改的现有工作应用程序

考虑选择属性路线:

>您希望将路线与行动代码保持在一起
>您正在创建新应用程序或对其进行重大更改
现有.
>您希望将路径参数名称与方法(操作)的实际参数进行匹配,这将使路由参数更具描述性,并省略路径ID不匹配的奇怪错误,这通常是因为我们没有配置路由正确,我们使用asp.net默认路由.

但是许多开发人员推荐属性路由的原因是因为它允许您更灵活,并将路由放在实际使用它们的操作旁边.您可以随时从选项切换到另一个并不困难

今天关于基于属性来自配置服务器是否覆盖/替换基于Java的路由配置?的介绍到此结束,谢谢您的阅读,有关7.12 Java-based container configuration (基于java的容器配置)、AJAX Https 请求显示 CORS 错误 (来自本地服务器)、Angular23 loading组件、路由配置、子路由配置、路由懒加载配置、c# – 基于属性的路由VS基于约定的路由 – ASP.net核心RESTful API的最佳实践等更多相关知识的信息可以在本站进行查询。

本文标签:

上一篇如何使用模块化Java在Gradle 6.6中设置mainClassName(gradle多模块构建)

下一篇无法在Django Docker上为SQL Server打开lib ODBC驱动程序17(无法使用com.mysql.jdbc)