SpringApplication

org.springframework.boot.SpringApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}

EnvironmentPostProcessor 接口的实现类定义在 META-INF/spring.factories 文件中, Spring Boot 应用启动时, 调用上面的 prepareEnvironment 方法时,会发布 ApplicationEnvironmentPreparedEvent 事件,相应的监听器类 ConfigFileApplicationListener 监听到改时间后,会通过类似于 java SPI 机制加载所有指定 EnvironmentPostProcessor 接口的实现类,并且初始化,调用内部方法 postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application),实现自定义 spring 的 Environment

spring.factories 机制

META-INF

Spring 装配 Bean

装配 Bean

1 Spring 进行装配 Bean 的方式

1.1 组件扫描和自动装配

通过在 Bean 的定义类上加上注解 @Component 向 Spring 表明这是一个 Bean 组件;接着在 Spring 的配置类中用注解 @ComponentScan 让 Spring在启动的时候自动扫面有 @Component 注解的 Bean, 并在 Spring 上下文中创建他们的实例。Spring会根据类名为其指定一个ID,将类名的第一个字母变为小写,也可以传一个 bean id 给 @Component

Bean 类

1
2
3
4
5
6
@Component("enyaAlbum")
public class EnyaAlbum implements CompactDisk{
public void play() {
System.out.println("Playing..." + "\nTitle: May It Be" + "\nSinger: Enya");
}
}

Configuration 类

1
2
3
4
5
@Configuration
//指定扫描的基础包
@ComponentScan(basePackages={"sugar.cdsystem", "sugar.player"})
public class CDConfiguration {
}

basePackages 指定组件扫描的基础包

1
@ComponentScan(basePackages={"sugar.cdsystem", "sugar.player"})

basePackageClasses属性所设置的数组中包含了类。这些类所在的包将会作为组件扫描的基础包

1
@ComponentScan(basePackageClasses = {CompateDisc.class})

在依赖此对象的 Class 中,通过 @Autowired (Spring 特有的注解)或者 @Inject (Java 依赖注入规范) 注解用于构造器、setter 方法或任意方法,进行注入。

1.2 Java 配置类中配置

@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。

所有 Bean 的声明都可以在配置类中进行,而 Bean 的代码中无需添加任何代码,每一个 Bean 的声明都通过一个 ==Bean== 进行注解的方法返回一个实例。

带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。构造器和Setter方法只是@Bean方法的两个简单样例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class CDConfiguration {

//默认 bean id 为方法名
//也可以用 name 指定
@Bean
public CompactDisk enyaAlbum() {
return new EnyaAlbum();
}

@Bean
public Player cdPlayer(CompactDisk compactDisk) {
return new CDPlayer(compactDisk);
}
}

1.3 XML 文件装配

XML时,需要在配置文件的顶部声明多个XML模式(XSD)文件,这些文件定义了配置Spring的XML元素。用来装配bean的最基本的XML元素包含在spring-beans模式之中

在XML中声明 DI 时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:

  • <constructor-arg>元素
  • 使用Spring 3.0所引入的c-命名空间

在装配集合方面,<constructor-arg>比c-命名空间的属性更有优势。

1
2
xmlns:c="http://www.springframework.org/schema/c"
<bean id="electricalPlayer" class="com.something.learn.bean.ElectricalPlayer" c:_0-ref="mp3Song"/>

属性注入时,既可以使用 <property> 元素,也可以使用 p-命名空间

1
2
xmlns:p="http://www.springframework.org/schema/p"
<bean id="blankCD" class="com.something.learn.bean.BlankCD" p:remark="音乐启迪人生">

无论是构造器注入还是属性注入, c-命名空间与 p-命名空间都不支持列表等数据结构,只支持引用及字面常量,但是可以使用 util-命名空间,声明 List、Set、Map 等结构,并且像 bean 一样可以在 c-命名空间或 p-命名空间中使用其引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<util:list id="trackList">
<value>突然的自我</value>
<value>晚风</value>
<value>浪人情歌</value>
</util:list>

<bean id="blankCD" class="com.something.learn.bean.BlankCD"
c:artist="伍佰"
c:albumName="冬之火 九重天演唱会特选录音专辑"
c:tracks-ref="trackList"
p:remark="音乐启迪人生"/>

2 Java 配置与 XML 配置的引用

2.1 在 JavaConfig 引用 xml 中声明的 bean

  1. Java 配置文件之间的引用

    使用 @Import 注解导入到另一个配置文件中

1
2
3
4
5
6
7
@Configuration
public class CDConfiguration {
@Bean
public CompactDisk enyaAlbum() {
return new EnyaAlbum();
}
}
1
2
3
4
5
6
7
8
@Configuration
@Import(CDConfiguration.class)
public class PlayerConfiguration {
@Bean
public Player cdPlayer(CompactDisk compactDisk) {
return new CDPlayer(compactDisk);
}
}
  1. 创建一个总的配置类,根配置类,将所有 Java 配置类导入
1
2
3
4
@Configuration
@Import({CDConfiguration.class, PlayerConfiguration.class})
public class Configuration {
}
  1. 在 Java 配置文件中引用 XML 的配置
1
2
3
4
5
6
7
8
9
/*
* 假设Player配置在Java文件中
* CD配置在XML文件中
*/
@Configuration
@Import(PlayerConfiguration.class)
@ImportResource("classpath:CDConfiguration.xml")
public class Configuration {
}

2.2 在 xml 中引用 JavaConfig 声明的 bean

  1. 在 XML 文件中引用另一个 XML 配置文件
1
<import resource="CDConfiguration.xml" />
  1. 在 XML 文件中引用 Java 的配置

    为了将 JavaConfig 类导入到 XML 配置中,我们可以这样声明bean:

1
2
3
4
5
6
7
 <!--CDConfiguration是Java中的配置类-->
<bean class="sugar.configuration.CDConfiguration" />

<!--cdPlayer是在XML文件中配置的-->
<bean id="cdPlayer"
class="sugar.player.CDPlayer"
c:cd-ref="compactDisc" />
  1. 使用第三个配置文件组合, 也称之为==根配置==
1
2
<bean class="sugar.configuration.CDConfiguration" />
<import resource="playerConfiguration.xml" />

根配置类或根配置文件中启用组件扫描,通过<context:component-scan>@ComponentScan

@Autowired 可以使用在构造器上(构造器注入),也可以使用在 setter 方法上(方法注入)

3 总结

Spring 中多种装配方式、多个配置文件可能看起来很是杂乱无章,因此最好有一个根配置,将所有的配置导入进来,进行声明。

尽量选择自动化配置,接着是 Java 配置(类型安全、易于重构、自定义Bean的实例化方式),最后采用 xml 方式的配置。 显示配置越少越好。

JNDI

二 高级装配

2.1 为环境相关的 Bean 启用 profile 配置

有连个参数可以设置激活 active 的 profile:

  • spring.profiles.active

  • spring.profiles.default

有多种方式来设置这两个属性:

  • 作为DispatcherServlet的初始化参数;

  • 作为Web应用的上下文参数;

  • 作为JNDI条目;

  • 作为环境变量;

  • 作为JVM的系统属性;

  • 在集成测试类上,使用@ActiveProfiles注解设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
@ActiveProfiles("word")
public class JunitTest {
@Autowired private DataSource dataSource;

@Test
public void dataSourceTest() {
assertNotNull(dataSource);
try {
DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
System.out.println(metaData.getDatabaseProductName());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
org.springframework.context.annotation @Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public interface Profile
extends annotation.Annotation
Indicates that a component is eligible for registration when one or more specified profiles are active.
A profile is a named logical grouping that may be activated programmatically via ConfigurableEnvironment.setActiveProfiles or declaratively through setting the spring.profiles.active property, usually through JVM system properties, as an environment variable, or for web applications as a Servlet context parameter in web.xml.
The @Profile annotation may be used in any of the following ways:
as a type-level annotation on any class directly or indirectly annotated with @Component, including @Configuration classes
as a meta-annotation, for the purpose of composing custom stereotype annotations
as a method-level annotation on any @Bean method
If a @Configuration class is marked with @Profile, all of the @Bean methods and @Import annotations associated with that class will be bypassed unless one or more of the specified profiles are active. This is very similar to the behavior in Spring XML: if the profile attribute of the beans element is supplied e.g., <beans profile="p1,p2">, the beans element will not be parsed unless profiles 'p1' and/or 'p2' have been activated. Likewise, if a @Component or @Configuration class is marked with @Profile({"p1", "p2"}), that class will not be registered/processed unless profiles 'p1' and/or 'p2' have been activated.
If a given profile is prefixed with the NOT operator (!), the annotated will be registered if the profile is not active. e.g., for @Profile({"p1", "!p2"}), registration will occur if profile 'p1' is active or if profile 'p2' is not active.
If the @Profile annotation is omitted, registration will occur, regardless of which (if any) profiles are active.
When defining Spring beans via XML, the "profile" attribute of the <beans> element may be used. See the documentation in spring-beans XSD (version 3.1 or greater) for details.

2.2 条件化的 bean

2.3 歧义性

当有多个 bean 满足自动装配所需的类型时,如何消除歧义

  • @primary 注解设置首选 bean

    各种配置方式中都可以指定

    多个类型相同 bean 都指定 @primary, 注入的时候(使用类型)也会带来歧义性

  • @qualifier 限定某个 bean

    @Component 、JavaConfig 的 @bean 组合使用

    每个 bean 都可以指定一个限定符,如果没有指定,限定符与 bean id 相同,但如果类的名称(组件扫描)或 bean id 变化时,需要修改注入时指定的限定符;为了避免这种不一致性,可以为 bean 通过 @qualifier("qua1") 指定限定符,注入的时候,也指定相同的限定符即可,这样类的名字和 bean id 就可以随意修改;

    可以为每个 bean 指定多个限定符,例如,可以按属性、特性等为 bean 指定限定符,多个 bean 可以拥有相同的限定符,这时,注入的时候,也会产生歧义,但是可以指定更多的限定符,将范围缩小至一个,但 Java 不支持同时指定多个相同的注解,可以通过自定义注解实现(创建自定义注解,同时在该注解的定于上使用 @qualifier 注解)。

1
2
3
4
5
6
7
8
9
10
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy { }

@Component
@Cold
@Creamy
public class IceCream implements Dessert { ... }

自定义配置

属性文件外置配置

@ConfigurationProperties

@DataSourceProperties

DataSourceAutoConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* {@link Condition} to detect when an embedded {@link DataSource} type can be used.
* If a pooled {@link DataSource} is available, it will always be preferred to an
* {@code EmbeddedDatabase}.
*/
static class EmbeddedDatabaseCondition extends SpringBootCondition {

private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource");
if (anyMatches(context, metadata, this.pooledCondition)) {
return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source"));
}
EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();
if (type == null) {
return ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll());
}
return ConditionOutcome.match(message.found("embedded database").items(type));
}

}

不同工具的元字符支持

grep -P

pattern is a Perl regular expression. Based on PRCE. (兼容Perl的正则表达式包)

  • 八进制
1
echo -e 'a\nb' | grep -z -P 'a\012b'
  • 十六进制
    • \xFF
    • \x{…}
1
echo -e 'a\nb' | grep -z -P 'a\x{0A}b'
  • 元字符 . 不能匹配换行符, 其他 Unicode 换行符等可以匹配
  • \w 严格等价于 [a-zA-Z0-9_], java , PHP 也是一样的, 但是 Perl 语言的正则引擎室支持 Unicode 字符(未验证)

grep -E / egrep

  • 不支持八进制、十六进制转义表示

  • 元字符 . 可以匹配换行符(\n), Unicode 换行符 NEL (\u0085), Unicode 行分隔符 LS (\u2028), Unicode 段分隔符 PS (\u2029)

  • \w 可以匹配汉字

1
echo 'a严b' | grep -z -E 'a\wb'

对于字符组, 尤其是排除型字符组,都支持 \n, \r, Unicode 换行符,行分隔符,段分隔符都支持

Notepad++

Notepad++ 的 匹配新行模式, 应该是启用了 点通配符模式 - dotALL(Perl中的单行模式), 以及 多行模式 - 行锚点可以匹配行内的换行符前后的位置

POSIX 规定,点号不能匹配 NUL (值为0的字符), 具体工具可有有差异。

grep: PCRE does not support \L, \l, \N{name}, \U, or \u

PCRE(包括PHP), java.util.regex \w 严格等价于 [a-zA-Z0-9_]

java.util.regex

  • \w 严格等价于 [a-zA-Z0-9_]

  • \s 不匹配 Unicode 换行符

  • 普通模式下 . 不能匹配 \n, \r, Unicode 换行符 NEL (\u0085), Unicode 行分隔符 LS (\u2028), Unicode 段分隔符 PS (\u2029)

  • 字符组可以匹配换行符,包括 Unicode 的上述行终结符

  • 支持字符组的计算 [[a-c]&&[a-z]] [[a-c]OR[d-e]], 当然也可以使用环视来实现

  • 多行模式下(?:m),锚点可以匹配 Unicode 行终结符 Page.370

  • $ 既可以匹配目标字符串的末尾,也可以匹配整个字符串末尾的换行符之前的位置 (这里的换行符也包括Unicode 的行终结符)

  • \Z 和普通模式下的 $ 的意义一样

  • \z 只匹配文本的末尾的位置

  • \A 总是与普通的 ^ 一样

  • \b 单词分界符 与 \B 非单词分界符对单词字符的定义仅限于 ASCII 字符,如果需要扩展,可以使用环视功能以及 Unicode 属性实现

1
(?<!\pL)(?=\pL)......(?<=pL)(?!\pL)
  • 正则表达式中设定匹配模式,支持

    • (?i)...(?-i), (?i) 开启模式,(?-i)关闭模式
    • (?:(?i)very), (?i) 的作用范围只限于括号內部,闭括号后就失效
    • 支持模式修饰范围,(?i:...)
  • 文字文本模式(消除所有元字符的特殊含义进行匹配), java.util.regex 的引擎支持 \Q...\E 序列,但是它只会消除除了 \E 之外的所有的元字符的特殊含义,直接在字符串两边加上,会有问题(原始字符串出现 \E, 会截断,只对前面的部分应用该模式)。请直接使用 Pattern.quote 方法或自己写一个处理方法。

Perl 中的锚点都不支持 Unicode 行终结符, 而 Java 的普通模式下的 $ 以及多行模式下的 ^ 以及 $ 都支持 Unicode 行终结符。

基于 PCRE 的 grep -P 测试, 普通模式下,$ 只匹配末尾,不匹配换行符之前的位置

示例正则

Hibernate validator 源码中 RegexpURLValidator 对校验 url 的正则表达式

1
2
3
4
5
6
7
8
9
// see http://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url
private static final Pattern URL_REGEX = Pattern
.compile(
"(?i)^([a-z](?:[-a-z0-9\\+\\.])*)" + // protocol
":(?:\\/\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:])*@)?" + // auth
"((?:\\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4}:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:]+)\\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=@])*))" + // host/ip
"(?::([0-9]*))?" + // port
"(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|\\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])))(?:\\?(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\x{E000}-\\x{F8FF}\\x{F0000}-\\x{FFFFD}|\\x{100000}-\\x{10FFFD}\\/\\?])*)?(?:\\#(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~\\x{A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}\\x{10000}-\\x{1FFFD}\\x{20000}-\\x{2FFFD}\\x{30000}-\\x{3FFFD}\\x{40000}-\\x{4FFFD}\\x{50000}-\\x{5FFFD}\\x{60000}-\\x{6FFFD}\\x{70000}-\\x{7FFFD}\\x{80000}-\\x{8FFFD}\\x{90000}-\\x{9FFFD}\\x{A0000}-\\x{AFFFD}\\x{B0000}-\\x{BFFFD}\\x{C0000}-\\x{CFFFD}\\x{D0000}-\\x{DFFFD}\\x{E1000}-\\x{EFFFD}!\\$&'\\(\\)\\*\\+,;=:@])|[\\/\\?])*)?$"
);