Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
爱吃血肠
spring-framework
提交
40cff5e3
S
spring-framework
项目概览
爱吃血肠
/
spring-framework
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
spring-framework
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
40cff5e3
编写于
12月 07, 2015
作者:
J
Juergen Hoeller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Polishing
上级
ee35b5ed
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
42 addition
and
87 deletion
+42
-87
spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
...rg/springframework/test/context/ContextConfiguration.java
+1
-25
spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java
...va/org/springframework/test/context/ContextHierarchy.java
+1
-2
spring-test/src/main/java/org/springframework/test/context/ContextLoader.java
.../java/org/springframework/test/context/ContextLoader.java
+2
-9
spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java
...ingframework/test/context/support/ContextLoaderUtils.java
+38
-51
未找到文件。
spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
浏览文件 @
40cff5e3
...
...
@@ -91,10 +91,8 @@ public @interface ContextConfiguration {
/**
* Alias for {@link #locations}.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #locations}, but it may be used instead of {@link #locations}.
*
* @since 3.0
* @see #inheritLocations
*/
...
...
@@ -104,7 +102,6 @@ public @interface ContextConfiguration {
/**
* The resource locations to use for loading an
* {@link org.springframework.context.ApplicationContext ApplicationContext}.
*
* <p>Check out the Javadoc for
* {@link org.springframework.test.context.support.AbstractContextLoader#modifyLocations
* AbstractContextLoader.modifyLocations()} for details on how a location
...
...
@@ -113,7 +110,6 @@ public @interface ContextConfiguration {
* {@link org.springframework.test.context.support.AbstractContextLoader#generateDefaultLocations
* AbstractContextLoader.generateDefaultLocations()} for details on the
* default locations that are going to be used if none are specified.
*
* <p>Note that the aforementioned default rules only apply for a standard
* {@link org.springframework.test.context.support.AbstractContextLoader
* AbstractContextLoader} subclass such as
...
...
@@ -122,10 +118,8 @@ public @interface ContextConfiguration {
* which are the effective default implementations used at runtime if
* {@code locations} are configured. See the documentation for {@link #loader}
* for further details regarding default loaders.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #value}, but it may be used instead of {@link #value}.
*
* @since 2.5
* @see #inheritLocations
*/
...
...
@@ -135,14 +129,12 @@ public @interface ContextConfiguration {
/**
* The <em>annotated classes</em> to use for loading an
* {@link org.springframework.context.ApplicationContext ApplicationContext}.
*
* <p>Check out the Javadoc for
* <p>Check out the javadoc for
* {@link org.springframework.test.context.support.AnnotationConfigContextLoader#detectDefaultConfigurationClasses
* AnnotationConfigContextLoader.detectDefaultConfigurationClasses()} for details
* on how default configuration classes will be detected if no
* <em>annotated classes</em> are specified. See the documentation for
* {@link #loader} for further details regarding default loaders.
*
* @since 3.1
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.test.context.support.AnnotationConfigContextLoader
...
...
@@ -153,16 +145,13 @@ public @interface ContextConfiguration {
/**
* The application context <em>initializer classes</em> to use for initializing
* a {@link ConfigurableApplicationContext}.
*
* <p>The concrete {@code ConfigurableApplicationContext} type supported by each
* declared initializer must be compatible with the type of {@code ApplicationContext}
* created by the {@link SmartContextLoader} in use.
*
* <p>{@code SmartContextLoader} implementations typically detect whether
* Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
* annotation is present and sort instances accordingly prior to invoking them.
*
* @since 3.2
* @see org.springframework.context.ApplicationContextInitializer
* @see org.springframework.context.ConfigurableApplicationContext
...
...
@@ -174,7 +163,6 @@ public @interface ContextConfiguration {
/**
* Whether or not {@link #locations resource locations} or <em>annotated
* classes</em> from test superclasses should be <em>inherited</em>.
*
* <p>The default value is {@code true}. This means that an annotated
* class will <em>inherit</em> the resource locations or annotated classes
* defined by test superclasses. Specifically, the resource locations or
...
...
@@ -182,12 +170,10 @@ public @interface ContextConfiguration {
* resource locations or annotated classes defined by test superclasses.
* Thus, subclasses have the option of <em>extending</em> the list of resource
* locations or annotated classes.
*
* <p>If {@code inheritLocations} is set to {@code false}, the
* resource locations or annotated classes for the annotated class
* will <em>shadow</em> and effectively replace any resource locations
* or annotated classes defined by superclasses.
*
* <p>In the following example that uses path-based resource locations, the
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* for {@code ExtendedTest} will be loaded from
...
...
@@ -206,7 +192,6 @@ public @interface ContextConfiguration {
* // ...
* }
* </pre>
*
* <p>Similarly, in the following example that uses annotated
* classes, the
* {@link org.springframework.context.ApplicationContext ApplicationContext}
...
...
@@ -233,18 +218,15 @@ public @interface ContextConfiguration {
/**
* Whether or not {@linkplain #initializers context initializers} from test
* superclasses should be <em>inherited</em>.
*
* <p>The default value is {@code true}. This means that an annotated
* class will <em>inherit</em> the application context initializers defined
* by test superclasses. Specifically, the initializers for a given test
* class will be added to the set of initializers defined by test
* superclasses. Thus, subclasses have the option of <em>extending</em> the
* set of initializers.
*
* <p>If {@code inheritInitializers} is set to {@code false}, the
* initializers for the annotated class will <em>shadow</em> and effectively
* replace any initializers defined by superclasses.
*
* <p>In the following example, the
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* for {@code ExtendedTest} will be initialized using
...
...
@@ -272,12 +254,10 @@ public @interface ContextConfiguration {
* The type of {@link SmartContextLoader} (or {@link ContextLoader}) to use
* for loading an {@link org.springframework.context.ApplicationContext
* ApplicationContext}.
*
* <p>If not specified, the loader will be inherited from the first superclass
* that is annotated with {@code @ContextConfiguration} and specifies an
* explicit loader. If no class in the hierarchy specifies an explicit
* loader, a default loader will be used instead.
*
* <p>The default concrete implementation chosen at runtime will be either
* {@link org.springframework.test.context.support.DelegatingSmartContextLoader
* DelegatingSmartContextLoader} or
...
...
@@ -293,23 +273,19 @@ public @interface ContextConfiguration {
* {@link org.springframework.test.context.web.GenericXmlWebContextLoader GenericXmlWebContextLoader},
* {@link org.springframework.test.context.web.GenericGroovyXmlWebContextLoader GenericGroovyXmlWebContextLoader}, and
* {@link org.springframework.test.context.web.AnnotationConfigWebContextLoader AnnotationConfigWebContextLoader}.
*
* @since 2.5
*/
Class
<?
extends
ContextLoader
>
loader
()
default
ContextLoader
.
class
;
/**
* The name of the context hierarchy level represented by this configuration.
*
* <p>If not specified the name will be inferred based on the numerical level
* within all declared contexts within the hierarchy.
*
* <p>This attribute is only applicable when used within a test class hierarchy
* that is configured using {@code @ContextHierarchy}, in which case the name
* can be used for <em>merging</em> or <em>overriding</em> this configuration
* with configuration of the same name in hierarchy levels defined in superclasses.
* See the Javadoc for {@link ContextHierarchy @ContextHierarchy} for details.
*
* @since 3.2.2
*/
String
name
()
default
""
;
...
...
spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java
浏览文件 @
40cff5e3
/*
* Copyright 2002-201
3
the original author or authors.
* Copyright 2002-201
5
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -148,7 +148,6 @@ public @interface ContextHierarchy {
/**
* A list of {@link ContextConfiguration @ContextConfiguration} instances,
* each of which defines a level in the context hierarchy.
*
* <p>If you need to merge or override the configuration for a given level
* of the context hierarchy within a test class hierarchy, you must explicitly
* name that level by supplying the same value to the {@link ContextConfiguration#name
...
...
spring-test/src/main/java/org/springframework/test/context/ContextLoader.java
浏览文件 @
40cff5e3
/*
* Copyright 2002-201
2
the original author or authors.
* Copyright 2002-201
5
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -33,8 +33,7 @@ import org.springframework.context.ApplicationContext;
* The results of {@link #processLocations(Class, String...) processLocations()}
* should then be supplied to {@link #loadContext(String...) loadContext()}.
*
* <p>Concrete implementations must provide a {@code public} no-args
* constructor.
* <p>Concrete implementations must provide a {@code public} no-args constructor.
*
* <p>Spring provides the following out-of-the-box implementations:
* <ul>
...
...
@@ -52,10 +51,8 @@ public interface ContextLoader {
/**
* Processes application context resource locations for a specified class.
*
* <p>Concrete implementations may choose to modify the supplied locations,
* generate new locations, or simply return the supplied locations unchanged.
*
* @param clazz the class with which the locations are associated: used to
* determine how to process the supplied locations
* @param locations the unmodified locations to use for loading the
...
...
@@ -68,10 +65,8 @@ public interface ContextLoader {
* Loads a new {@link ApplicationContext context} based on the supplied
* {@code locations}, configures the context, and finally returns
* the context in fully <em>refreshed</em> state.
*
* <p>Configuration locations are generally considered to be classpath
* resources by default.
*
* <p>Concrete implementations should register annotation configuration
* processors with bean factories of {@link ApplicationContext application
* contexts} loaded by this ContextLoader. Beans will therefore automatically
...
...
@@ -79,13 +74,11 @@ public interface ContextLoader {
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
* {@link javax.annotation.Resource @Resource}, and
* {@link javax.inject.Inject @Inject}.
*
* <p>Any ApplicationContext loaded by a ContextLoader <strong>must</strong>
* register a JVM shutdown hook for itself. Unless the context gets closed
* early, all context instances will be automatically closed on JVM
* shutdown. This allows for freeing external resources held by beans within
* the context, e.g. temporary files.
*
* @param locations the resource locations to use to load the application context
* @return a new application context
* @throws Exception if context loading failed
...
...
spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java
浏览文件 @
40cff5e3
...
...
@@ -57,17 +57,12 @@ abstract class ContextLoaderUtils {
private
static
final
Log
logger
=
LogFactory
.
getLog
(
ContextLoaderUtils
.
class
);
private
ContextLoaderUtils
()
{
/* no-op */
}
/**
* Resolve the list of lists of {@linkplain ContextConfigurationAttributes context
* configuration attributes} for the supplied {@linkplain Class test class} and its
* superclasses, taking into account context hierarchies declared via
* {@link ContextHierarchy @ContextHierarchy} and
* {@link ContextConfiguration @ContextConfiguration}.
*
* <p>The outer list represents a top-down ordering of context configuration
* attributes, where each element in the list represents the context configuration
* declared on a given test class in the class hierarchy. Each nested list
...
...
@@ -77,13 +72,11 @@ abstract class ContextLoaderUtils {
* single {@code @ContextHierarchy} instance on the particular class.
* Furthermore, each nested list maintains the order in which
* {@code @ContextConfiguration} instances are declared.
*
* <p>Note that the {@link ContextConfiguration#inheritLocations inheritLocations} and
* {@link ContextConfiguration#inheritInitializers() inheritInitializers} flags of
* {@link ContextConfiguration @ContextConfiguration} will <strong>not</strong>
* be taken into consideration. If these flags need to be honored, that must be
* handled manually when traversing the nested lists returned by this method.
*
* @param testClass the class for which to resolve the context hierarchy attributes
* (must not be {@code null})
* @return the list of lists of configuration attributes for the specified class;
...
...
@@ -95,7 +88,6 @@ abstract class ContextLoaderUtils {
* {@code @ContextHierarchy} as top-level annotations.
* @throws IllegalStateException if no class in the class hierarchy declares
* {@code @ContextHierarchy}.
*
* @since 3.2.2
* @see #buildContextHierarchyMap(Class)
* @see #resolveContextConfigurationAttributes(Class)
...
...
@@ -109,23 +101,25 @@ abstract class ContextLoaderUtils {
final
Class
<
ContextHierarchy
>
contextHierarchyType
=
ContextHierarchy
.
class
;
final
List
<
List
<
ContextConfigurationAttributes
>>
hierarchyAttributes
=
new
ArrayList
<
List
<
ContextConfigurationAttributes
>>();
UntypedAnnotationDescriptor
descriptor
=
findAnnotationDescriptorForTypes
(
testClass
,
contextConfigType
,
contextHierarchyType
);
Assert
.
notNull
(
descriptor
,
String
.
format
(
"Could not find an 'annotation declaring class' for annotation type [%s] or [%s] and test class [%s]"
,
contextConfigType
.
getName
(),
contextHierarchyType
.
getName
(),
testClass
.
getName
()));
UntypedAnnotationDescriptor
desc
=
findAnnotationDescriptorForTypes
(
testClass
,
contextConfigType
,
contextHierarchyType
);
if
(
desc
==
null
)
{
throw
new
IllegalArgumentException
(
String
.
format
(
"Could not find an 'annotation declaring class' for annotation type [%s] or [%s] and test class [%s]"
,
contextConfigType
.
getName
(),
contextHierarchyType
.
getName
(),
testClass
.
getName
()));
}
while
(
desc
riptor
!=
null
)
{
Class
<?>
rootDeclaringClass
=
desc
riptor
.
getRootDeclaringClass
();
Class
<?>
declaringClass
=
desc
riptor
.
getDeclaringClass
();
while
(
desc
!=
null
)
{
Class
<?>
rootDeclaringClass
=
desc
.
getRootDeclaringClass
();
Class
<?>
declaringClass
=
desc
.
getDeclaringClass
();
boolean
contextConfigDeclaredLocally
=
isAnnotationDeclaredLocally
(
contextConfigType
,
declaringClass
);
boolean
contextHierarchyDeclaredLocally
=
isAnnotationDeclaredLocally
(
contextHierarchyType
,
declaringClass
);
if
(
contextConfigDeclaredLocally
&&
contextHierarchyDeclaredLocally
)
{
String
msg
=
String
.
format
(
"Class [%s] has been configured with both @ContextConfiguration "
+
"and @ContextHierarchy. Only one of these annotations may be declared on a test class "
+
"or composed annotation."
,
declaringClass
.
getName
());
String
msg
=
String
.
format
(
"Class [%s] has been configured with both @ContextConfiguration "
+
"and @ContextHierarchy. Only one of these annotations may be declared on a test class "
+
"or composed annotation."
,
declaringClass
.
getName
());
logger
.
error
(
msg
);
throw
new
IllegalStateException
(
msg
);
}
...
...
@@ -134,30 +128,28 @@ abstract class ContextLoaderUtils {
if
(
contextConfigDeclaredLocally
)
{
ContextConfiguration
contextConfiguration
=
AnnotationUtils
.
synthesizeAnnotation
(
descriptor
.
getAnnotationAttributes
(),
ContextConfiguration
.
class
,
descriptor
.
getRootDeclaringClass
());
convertContextConfigToConfigAttributesAndAddToList
(
contextConfiguration
,
rootDeclaringClass
,
configAttributesList
);
desc
.
getAnnotationAttributes
(),
ContextConfiguration
.
class
,
desc
.
getRootDeclaringClass
());
convertContextConfigToConfigAttributesAndAddToList
(
contextConfiguration
,
rootDeclaringClass
,
configAttributesList
);
}
else
if
(
contextHierarchyDeclaredLocally
)
{
ContextHierarchy
contextHierarchy
=
getAnnotation
(
declaringClass
,
contextHierarchyType
);
for
(
ContextConfiguration
contextConfiguration
:
contextHierarchy
.
value
())
{
convertContextConfigToConfigAttributesAndAddToList
(
contextConfiguration
,
rootDeclaringClass
,
configAttributesList
);
convertContextConfigToConfigAttributesAndAddToList
(
contextConfiguration
,
rootDeclaringClass
,
configAttributesList
);
}
}
else
{
// This should theoretically never happen...
String
msg
=
String
.
format
(
"Test class [%s] has been configured with neither @ContextConfiguration "
+
"nor @ContextHierarchy as a class-level annotation."
,
rootDeclaringClass
.
getName
());
String
msg
=
String
.
format
(
"Test class [%s] has been configured with neither @ContextConfiguration "
+
"nor @ContextHierarchy as a class-level annotation."
,
rootDeclaringClass
.
getName
());
logger
.
error
(
msg
);
throw
new
IllegalStateException
(
msg
);
}
hierarchyAttributes
.
add
(
0
,
configAttributesList
);
descriptor
=
findAnnotationDescriptorForTypes
(
rootDeclaringClass
.
getSuperclass
(),
contextConfigType
,
contextHierarchyType
);
desc
=
findAnnotationDescriptorForTypes
(
rootDeclaringClass
.
getSuperclass
(),
contextConfigType
,
contextHierarchyType
);
}
return
hierarchyAttributes
;
...
...
@@ -168,18 +160,15 @@ abstract class ContextLoaderUtils {
* test class} and its superclasses, taking into account context hierarchies
* declared via {@link ContextHierarchy @ContextHierarchy} and
* {@link ContextConfiguration @ContextConfiguration}.
*
* <p>Each value in the map represents the consolidated list of {@linkplain
* ContextConfigurationAttributes context configuration attributes} for a
* given level in the context hierarchy (potentially across the test class
* hierarchy), keyed by the {@link ContextConfiguration#name() name} of the
* context hierarchy level.
*
* <p>If a given level in the context hierarchy does not have an explicit
* name (i.e., configured via {@link ContextConfiguration#name}), a name will
* be generated for that hierarchy level by appending the numerical level to
* the {@link #GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX}.
*
* @param testClass the class for which to resolve the context hierarchy map
* (must not be {@code null})
* @return a map of context configuration attributes for the context hierarchy,
...
...
@@ -187,7 +176,6 @@ abstract class ContextLoaderUtils {
* @throws IllegalArgumentException if the lists of context configuration
* attributes for each level in the {@code @ContextHierarchy} do not define
* unique context configuration within the overall hierarchy.
*
* @since 3.2.2
* @see #resolveContextHierarchyAttributes(Class)
*/
...
...
@@ -217,9 +205,9 @@ abstract class ContextLoaderUtils {
// Check for uniqueness
Set
<
List
<
ContextConfigurationAttributes
>>
set
=
new
HashSet
<
List
<
ContextConfigurationAttributes
>>(
map
.
values
());
if
(
set
.
size
()
!=
map
.
size
())
{
String
msg
=
String
.
format
(
"The @ContextConfiguration elements configured via
"
+
"@ContextHierarchy in test class [%s] and its superclasses must "
+
"define unique contexts per hierarchy level."
,
testClass
.
getName
());
String
msg
=
String
.
format
(
"The @ContextConfiguration elements configured via
@ContextHierarchy in "
+
"test class [%s] and its superclasses must define unique contexts per hierarchy level."
,
testClass
.
getName
());
logger
.
error
(
msg
);
throw
new
IllegalStateException
(
msg
);
}
...
...
@@ -231,15 +219,13 @@ abstract class ContextLoaderUtils {
* Resolve the list of {@linkplain ContextConfigurationAttributes context
* configuration attributes} for the supplied {@linkplain Class test class} and its
* superclasses.
*
* <p>Note that the {@link ContextConfiguration#inheritLocations inheritLocations} and
* {@link ContextConfiguration#inheritInitializers() inheritInitializers} flags of
* {@link ContextConfiguration @ContextConfiguration} will <strong>not</strong>
* be taken into consideration. If these flags need to be honored, that must be
* handled manually when traversing the list returned by this method.
*
* @param testClass the class for which to resolve the configuration attributes (must
* not be {@code null})
* @param testClass the class for which to resolve the configuration attributes
* (must not be {@code null})
* @return the list of configuration attributes for the specified class, ordered
* <em>bottom-up</em> (i.e., as if we were traversing up the class hierarchy);
* never {@code null}
...
...
@@ -249,18 +235,19 @@ abstract class ContextLoaderUtils {
static
List
<
ContextConfigurationAttributes
>
resolveContextConfigurationAttributes
(
Class
<?>
testClass
)
{
Assert
.
notNull
(
testClass
,
"Class must not be null"
);
final
List
<
ContextConfigurationAttributes
>
attributesList
=
new
ArrayList
<
ContextConfigurationAttributes
>();
List
<
ContextConfigurationAttributes
>
attributesList
=
new
ArrayList
<
ContextConfigurationAttributes
>();
Class
<
ContextConfiguration
>
annotationType
=
ContextConfiguration
.
class
;
AnnotationDescriptor
<
ContextConfiguration
>
descriptor
=
findAnnotationDescriptor
(
testClass
,
annotationType
);
Assert
.
notNull
(
descriptor
,
String
.
format
(
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]"
,
annotationType
.
getName
(),
testClass
.
getName
()));
if
(
descriptor
==
null
)
{
throw
new
IllegalArgumentException
(
String
.
format
(
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]"
,
annotationType
.
getName
(),
testClass
.
getName
()));
}
while
(
descriptor
!=
null
)
{
convertContextConfigToConfigAttributesAndAddToList
(
descriptor
.
synthesizeAnnotation
(),
descriptor
.
getRootDeclaringClass
(),
attributesList
);
descriptor
.
getRootDeclaringClass
(),
attributesList
);
descriptor
=
findAnnotationDescriptor
(
descriptor
.
getRootDeclaringClass
().
getSuperclass
(),
annotationType
);
}
...
...
@@ -274,13 +261,13 @@ abstract class ContextLoaderUtils {
*/
private
static
void
convertContextConfigToConfigAttributesAndAddToList
(
ContextConfiguration
contextConfiguration
,
Class
<?>
declaringClass
,
final
List
<
ContextConfigurationAttributes
>
attributesList
)
{
if
(
logger
.
isTraceEnabled
())
{
logger
.
trace
(
String
.
format
(
"Retrieved @ContextConfiguration [%s] for declaring class [%s]."
,
contextConfiguration
,
declaringClass
.
getName
()));
contextConfiguration
,
declaringClass
.
getName
()));
}
ContextConfigurationAttributes
attributes
=
new
ContextConfigurationAttributes
(
declaringClass
,
contextConfiguration
);
ContextConfigurationAttributes
attributes
=
new
ContextConfigurationAttributes
(
declaringClass
,
contextConfiguration
);
if
(
logger
.
isTraceEnabled
())
{
logger
.
trace
(
"Resolved context configuration attributes: "
+
attributes
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录