Struts2源码浅析-初始化

StrutsPrepareAndExecuteFilter实现了Filter接口 init方法为初始化入口
StrutsPrepareAndExecuteFilter init方法
public void init(FilterConfig filterConfig) throws ServletException {  
//初始化辅助类 类似一个Delegate
InitOperations init = new InitOperations();
try {
// FilterHostConfig 封装了FilterConfig参数对象
FilterHostConfig config = new FilterHostConfig(filterConfig);
//LoggerFactory配置加载
//如果没有web.xml 没有配置“loggerFactory”参数 尝试org.apache.commons.logging.LogFactory
//如果失败 使用JdkLoggerFactory
//TODO SPI
init.initLogging(config);
//TODO 创建Dispatcher 注册加载器 执行加载器 创建容器 解析xml
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//预处理类 请求处理时才会真正用到
//1.主要负责在每次请求 创建ActionContext 清除ActionContext
//2.当接收到一个请求时 通过uri查找 ActionConfig 创建ActionMapping
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
//处理请求 Delegate
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//空实现 留作扩展
postInit(dispatcher, filterConfig);
} finally {
init.cleanup();
}
}

InitOperations 类似与一个Delegate 主要负责实例化Dispatche 再把初始化操作转交给Dispatche init处理

public Dispatcher initDispatcher( HostConfig filterConfig ) {  
//创建Dispatcher
Dispatcher dispatcher = createDispatcher(filterConfig);
//核心方法 Container容器的创建 xml解析在此方法发生
dispatcher.init();
return dispatcher;
}

private Dispatcher createDispatcher( HostConfig filterConfig ) {
Map<String, String> params = new HashMap<String, String>();
for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
String name = (String) e.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
return new Dispatcher(filterConfig.getServletContext(), params);
}

Dispatcher init方法 1.针对配置文件 注册不同的加载器 保存到ConfigurationManager类中的一个变量中 2.创建容器 解析xml

public void init() {  
//创建配置操作管理类 , 会保存元素加载器
if (configurationManager == null) {
configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
/**初始化各种形式加载器,保存到ConfigurationManager#containerProviders Map集合中 没有真正执行加载 解析逻辑*/

//org/apache/struts2/default.properties属性文件 里面定义了一系列struts常量
init_DefaultProperties(); // [1]

//web.xml配置的 config参数 [配置多个用","分开]
//如果没有该参数 默认为 struts-default.xml[框架级],struts-plugin.xml[框架级],struts.xml[系统级别]
//根据文件名称 创建加载器 加载xml主要有一下两个解析器
//XmlConfigurationProvider[xwork.xml],
//StrutsXmlConfigurationProvider[struts相关配置文件]配置元素加载器
init_TraditionalXmlConfigurations(); // [2]

//struts.locale 注册
init_LegacyStrutsProperties(); // [3]

//实例化 我们自定义的加载器 保存到containerProviders集合中
// web.xml configProviders参数 多个用","分开 配置器必须是ConfigurationProvider接口的实例
//TODO SPI
init_CustomConfigurationProviders(); // [5]

//web.xml配置的init-param参数 加载器 最终会保存到Container容器中
init_FilterInitParameters() ; // [6]

//TODO 根据我们在struts.xml定义的 常量 选择插件类
//比如集成spring 会用到org.apache.struts2.spring.StrutsSpringObjectFactory
init_AliasStandardObjects() ; // [7]

/** 执行加载器 */
//TODO 创建容器 解析xml 真正执行加载器方法
Container container = init_PreloadConfiguration();
//执行当前Dispatcher对象 依赖关系注入
container.inject(this);

//额外动作
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);

} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}

ConfigurationManager 主要管理 创建的各种加载器

public class ConfigurationManager {  

protected static final Logger LOG = LoggerFactory.getLogger(ConfigurationManager.class);
//配置元素管理器
protected Configuration configuration;

protected Lock providerLock = new ReentrantLock();
//创建的xml加载器会保存到次集合中
private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();

}

Dispatcher的 createConfigurationManager方法

protected ConfigurationManager createConfigurationManager(String name) {  
//name - > struts
return new ConfigurationManager(name);
}

1.default.properties 属性文件加载器

private void init_DefaultProperties() {  
//保存到ConfigurationManager加载器集合中
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}

2.创建struts相关文件加载器 StrutsXmlConfigurationProvider

private void init_TraditionalXmlConfigurations() {  
//web.xml 配置的config
String configPaths = initParams.get("config");
if (configPaths == null) {
//如果没有配置 默认 struts-default.xml,struts-plugin.xml,struts.xml
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split("\\s*[,]\\s*");
for (String file : files) {
if (file.endsWith(".xml")) {
if ("xwork.xml".equals(file)) {
configurationManager.addConfigurationProvider(createXmlConfigurationProvider(file, false));
} else {
//struts xml加载器
//StrutsXmlConfigurationProvider
configurationManager.addConfigurationProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}

protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
return new XmlConfigurationProvider(filename, errorIfMissing);
}

protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
}

3.web.xml扩展的ContainerProviders加载器 实例化

private void init_CustomConfigurationProviders() {  
//web.xml 中configProviders 节点
String configProvs = initParams.get("configProviders");
if (configProvs != null) {
String[] classes = configProvs.split("\\s*[,]\\s*");
for (String cname : classes) {
Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
}
}
}

init_PreloadConfiguration 方法主要完成创建容器, 解析xml动作

private Container init_PreloadConfiguration() {  
//创建Container 解析xml
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();

boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
return container;
}

init_PreloadConfiguration 方法中调用了 ConfigurationManager的getConfiguration 方法

public synchronized Configuration getConfiguration() {  
//创建配置元素管理器
if (configuration == null) {
// defaultFrameworkBeanName - > struts
setConfiguration(createConfiguration(defaultFrameworkBeanName));
try {
// getContainerProviders 返回注册的各种加载器
// reloadContainer 创建Container 解析xml
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
conditionalReload();
}
return configuration;
}


protected Configuration createConfiguration(String beanName) {  
return new DefaultConfiguration(beanName);
}

DefaultConfiguration的reloadContainer方法 会去执行已注册的各种加载器 ,和创建容器

public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {  
packageContexts.clear();
loadedFileNames.clear();
List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
// 保存struts常量
ContainerProperties props = new ContainerProperties();
//容器构建器
ContainerBuilder builder = new ContainerBuilder();
for (final ContainerProvider containerProvider : providers) {
/**
* 初始化Document 准备解析
* 具体在XmlConfigurationProvider实现类 会处理include节点
* 处理完成之后Document会保存到XmlConfigurationProvider#documents list集合中
* include file路径会保存到XmlConfigurationProvider#loadedFileUrls set集合中
* 从代码中发现 include file属性中 可以使用通配符 "*"
*/
/** StrutsXmlConfigurationProvider 是 XmlConfigurationProvider的子类 */
/** StrutsXmlConfigurationProvider struts*.xml */
containerProvider.init(this);
//针对"bean","constant","unknown-handler-stack"节点 不包括"package"节点 解析xml
//每一个bean 对应一个LocatableFactory LocatableFactory保存了bean的定义
//bean定义 保存到ContainerBuilder#factories map集合中
//配置文件中定义的常量 保存到props中
containerProvider.register(builder, props);
}
//将常量保存到ContainerBuilder#factories map集合中
//每一个常量对应一个LocatableConstantFactory
props.setConstants(builder);
builder.factory(Configuration.class, new Factory<Configuration>() {
public Configuration create(Context context) throws Exception {
return DefaultConfiguration.this;
}
});

ActionContext oldContext = ActionContext.getContext();
try {
//创建辅助容器 ContainerImpl并且 实例化 struts一些核心类
Container bootstrap = createBootstrapContainer();
setContext(bootstrap);

//主容器 这是一个全局变量
container = builder.create(false);
setContext(container);
objectFactory = container.getInstance(ObjectFactory.class);

// Process the configuration providers first
for (final ContainerProvider containerProvider : providers) {
if (containerProvider instanceof PackageProvider) {
//com.opensymphony.xwork2.config.providers.XmlConfigurationProvider#setObjectFactory(ObjectFactory)
container.inject(containerProvider);
//解析 xml package节点
//保存packageContexts map集合中
//com.opensymphony.xwork2.config.providers.XmlConfigurationProvider line 481
((PackageProvider) containerProvider).loadPackages();
packageProviders.add((PackageProvider) containerProvider);
}
}

// Then process any package providers from the plugins
Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
if (packageProviderNames != null) {
for (String name : packageProviderNames) {
PackageProvider provider = container.getInstance(PackageProvider.class, name);
provider.init(this);
provider.loadPackages();
packageProviders.add(provider);
}
}
//TODO
rebuildRuntimeConfiguration();
} finally {
if (oldContext == null) {
ActionContext.setContext(null);
}
}
return packageProviders;
}

StrutsXmlConfigurationProvider的init方法 具体在父类XmlConfigurationProvider中实现

public void init(Configuration configuration) {  
this.configuration = configuration;
this.includedFileNames = configuration.getLoadedFileNames();
// configFileName ->struts.xml
//1.递归处理include节点
//2.生成Document 集合
loadDocuments(configFileName);
}

loadDocuments方法中调用了loadConfigurationFiles方法 返回一个Document集合

private void loadDocuments(String configFileName) {  
loadedFileUrls.clear();
//List<Document> documents
documents = loadConfigurationFiles(configFileName, null);
}

loadConfigurationFiles方法 递归处理include节点 最终生成Document集合

private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {  
List<Document> docs = new ArrayList<Document>();
List<Document> finalDocs = new ArrayList<Document>();
//防止include重复引入
if (!includedFileNames.contains(fileName)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Loading action configurations from: " + fileName);
}
includedFileNames.add(fileName);
Iterator<URL> urls = null;
InputStream is = null;
IOException ioException = null;
try {
urls = getConfigurationUrls(fileName);
} catch (IOException ex) {
ioException = ex;
}

if (urls == null || !urls.hasNext()) {
if (errorIfMissing) {
throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
} else {
LOG.info("Unable to locate configuration files of the name " + fileName + ", skipping");
return docs;
}
}
URL url = null;
while (urls.hasNext()) {
try {
url = urls.next();
is = FileManager.loadFile(url);
InputSource in = new InputSource(is);
in.setSystemId(url.toString());
//生成Document对象
docs.add(DomHelper.parse(in, dtdMappings));
} catch (XWorkException e) {
if (includeElement != null) {
throw new ConfigurationException("Unable to load " + url, e, includeElement);
} else {
throw new ConfigurationException("Unable to load " + url, e);
}
} catch (Exception e) {
final String s = "Caught exception while loading file " + fileName;
throw new ConfigurationException(s, e, includeElement);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOG.error("Unable to close input stream", e);
}
}
}
}
//sort the documents, according to the "order" attribute
Collections.sort(docs, new Comparator<Document>() {
public int compare(Document doc1, Document doc2) {
return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
}
});
for (Document doc : docs) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
if ("include".equals(nodeName)) {
String includeFileName = child.getAttribute("file");
//可以使用通配符匹配
if (includeFileName.indexOf('*') != -1) {
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Vector<String> wildcardMatches = wildcardFinder.findMatches();
for (String match : wildcardMatches) {
//递归处理include节点
finalDocs.addAll(loadConfigurationFiles(match, child));
}
} else {
//递归处理include节点
finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
finalDocs.add(doc);
loadedFileUrls.add(url.toString());
}
}
return finalDocs;
}

StrutsXmlConfigurationProvider的register方法 主要在父类 XmlConfigurationProvider中实现
1.遍历init方法中生成的Document 集合 解析xml文件中定义的bean,constant常量节点 不会处理package节点
2.解析bean节点的值 包装成LocatableFactory对象 注册到ContainerBuilder中factories map集合中
3.解析constant节点的值 保存到ContainerProperties 对象中
XmlConfigurationProvider 的register 这里只解析 bean , constant节点

public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {  
Map<String, Node> loadedBeans = new HashMap<String, Node>();
for (Document doc : documents) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
//解析bean节点
if ("bean".equals(nodeName)) {
String type = child.getAttribute("type");
String name = child.getAttribute("name");
String impl = child.getAttribute("class");
String onlyStatic = child.getAttribute("static");
String scopeStr = child.getAttribute("scope");
boolean optional = "true".equals(child.getAttribute("optional"));
Scope scope = Scope.SINGLETON;
if ("default".equals(scopeStr)) {
scope = Scope.DEFAULT;
} else if ("request".equals(scopeStr)) {
scope = Scope.REQUEST;
} else if ("session".equals(scopeStr)) {
scope = Scope.SESSION;
} else if ("singleton".equals(scopeStr)) {
scope = Scope.SINGLETON;
} else if ("thread".equals(scopeStr)) {
scope = Scope.THREAD;
}
if (StringUtils.isEmpty(name)) {
name = Container.DEFAULT_NAME;
}
try {
Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
Class ctype = cimpl;
if (StringUtils.isNotEmpty(type)) {
ctype = ClassLoaderUtil.loadClass(type, getClass());
}
if ("true".equals(onlyStatic)) {
// Force loading of class to detect no class def found exceptions
cimpl.getDeclaredClasses();
containerBuilder.injectStatics(cimpl);
} else {
// beanName + class 构成唯一约束
if (containerBuilder.contains(ctype, name)) {
//用loadedBeans map集合检查是否有重复配置的bean
Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
if (throwExceptionOnDuplicateBeans) {
throw new ConfigurationException("Bean type " + ctype + " with the name " + name + " has already been loaded by " + loc, child);
}
}
// Force loading of class to detect no class def found exceptions
cimpl.getDeclaredConstructors();

if (LOG.isDebugEnabled()) {
LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
}
//LocatableFactory 类似spring中 BeanDefinition
//bean定义 保存到ContainerBuilder#factories map集合中
//目前为止 并未真正实例化bean
containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
}
//loadedBeans 检查重复配置的bean
loadedBeans.put(ctype.getName() + name, child);
} catch (Throwable ex) {
if (!optional) {
throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode);
} else {
LOG.debug("Unable to load optional class: " + ex);
}
}
//constant常量节点
} else if ("constant".equals(nodeName)) {
String name = child.getAttribute("name");
String value = child.getAttribute("value");
//ContainerProperties ->props
props.setProperty(name, value, childNode);
} else if (nodeName.equals("unknown-handler-stack")) {
List<UnknownHandlerConfig> unknownHandlerStack = new ArrayList<UnknownHandlerConfig>();
NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref");
int unknownHandlersSize = unknownHandlers.getLength();
for (int k = 0; k < unknownHandlersSize; k++) {
Element unknownHandler = (Element) unknownHandlers.item(k);
unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name")));
}
if (!unknownHandlerStack.isEmpty()) configuration.setUnknownHandlerStack(unknownHandlerStack);
}
}
}
}
}

XmlConfigurationProvider的loadPackages方法 解析package节点下的所有子节点interceptor ,ResultType等等
保存到DefaultConfiguration packageContexts map集合中

public void loadPackages() throws ConfigurationException {  
List<Element> reloads = new ArrayList<Element>();
for (Document doc : documents) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
if ("package".equals(nodeName)) {
//解析package节点 包装成PackageConfig对象
PackageConfig cfg = addPackage(child);
if (cfg.isNeedsRefresh()) {
reloads.add(child);
}
}
}
}
//空实现 扩展时用
loadExtraConfiguration(doc);
}

if (reloads.size() > 0) {
reloadRequiredPackages(reloads);
}
for (Document doc : documents) {
//空实现 扩展时用
loadExtraConfiguration(doc);
}
documents.clear();
configuration = null;
}

package节点下的所有子节点

protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {  
PackageConfig.Builder newPackage = buildPackageContext(packageElement);
if (newPackage.isNeedsRefresh()) {
return newPackage.build();
}
//处理所有的ResultType 包括自定义的 , strust-default.xml中定义的
addResultTypes(newPackage, packageElement);
//interceptor节点
loadInterceptors(newPackage, packageElement);
//default-interceptor-ref
loadDefaultInterceptorRef(newPackage, packageElement);
//default-class-ref节点
loadDefaultClassRef(newPackage, packageElement);
//全局result global-results节点
loadGlobalResults(newPackage, packageElement);
//global-exception-mappings节点 异常处理
loadGobalExceptionMappings(newPackage, packageElement);

NodeList actionList = packageElement.getElementsByTagName("action");
for (int i = 0; i < actionList.getLength(); i++) {
Element actionElement = (Element) actionList.item(i);
// action节点 result节点处理
addAction(actionElement, newPackage);
}
//default-action-ref
loadDefaultActionRef(newPackage, packageElement);
PackageConfig cfg = newPackage.build();
//TODO 保存到Map<string packageconfig=""> packageContexts 集合中
configuration.addPackageConfig(cfg.getName(), cfg);
return cfg;
}

最后整理解析的ActionConfig Map集合[DefaultConfiguration#packageContexts] 最终已Map<nameSpace,Map<actionName, ActionConfig>>形式存储

public void rebuildRuntimeConfiguration() {  
runtimeConfiguration = buildRuntimeConfiguration();
}


protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {  
Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();
Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();

for (PackageConfig packageConfig : packageContexts.values()) {

if (!packageConfig.isAbstract()) {
String namespace = packageConfig.getNamespace();
Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);

if (configs == null) {
configs = new LinkedHashMap<String, ActionConfig>();
}

Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();

for (Object o : actionConfigs.keySet()) {
String actionName = (String) o;
ActionConfig baseConfig = actionConfigs.get(actionName);
//这里设置action的拦截器
//TODO buildFullActionConfig
configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
}
//key -> nameSpace
//value - > <actionName, ActionConfig> ,ActionConfig包含了拦截器
namespaceActionConfigs.put(namespace, configs);
if (packageConfig.getFullDefaultActionRef() != null) {
namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
}
}
}
return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);
}

解析完成后, 最终会保存到DefaultConfiguration runtimeConfiguration变量中
初始化顺序图:
[img]https://img-my.csdn.net/uploads/201205/16/1337158519_8650.jpg[/img]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
神速百度SEO排名优化器主要为广大站长提供网站排名优化功能。可以按百度和(google)的规则快速提升指定关键词的排名。 神速百度SEO排名优化软件特点: 1、最容易收录的是.html的静态页面; 2、网页内容中关键词的密度和突出性(包括Title、DeScription和Keyword,还有正文中关键词的密度); 3、网站结构(目录的结构); 4、更新频率; 5、内容的价值(主要看是否是原创); 6、外链的价值; 神速百度SEO排名优化软件功能: 1、您可以指定几个您网站内容相关的关键词,软件可以自动通过这些关键词搜索到与您网站内容相符的文章。并下载。 2、用这些文章自动生成网页,并按随机而合理的密度插入您指定的关键词,自动配置好网页中的Title、DeScription和Keyword,插入关键词的网页和原文件已经不同,所以,内容接近原创,更容易得到百度的信任。并按指定的间隔上传生成的网页,建议每10小时上传一次。 3、定时更新上传优化的网页,增强网站的活跃性,使百度收录您网站内容的频率更快,网站排名迅速上升。 4、自动生成网站地图,也就是您所知道的sitemap.xml文件,但我们的软件会给它自动随机生成一个文件名,这样,就不容易被百度抓到规律K站了。上传网页自动更新网站地图文件。在百度站长平台提交网站地图文件后,百度会自动更新您的网页列表。 5、自动生成优化网页的列表,使优化网页的内链更合理。 6、自动交换友情链接,自动形成友情链接的联盟。您的网址将会被成千上万个网站做反向链接。网站权重迅速上升。 用以上的方法,可以迅速提升您的网站排名,根据难度不同,理论上,排到第一页只是时间问题。 注意:生成的优化网页与您制作的网页是分开的,不会影响您正常网页的内容,优化的目的是提升网站在搜索引擎中的权重,权重高了,您的正常内容自然就会排到前面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值