中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

購(gòu)物網(wǎng)站建設(shè)教程關(guān)鍵詞推廣效果

購(gòu)物網(wǎng)站建設(shè)教程,關(guān)鍵詞推廣效果,來(lái)個(gè)網(wǎng)站,網(wǎng)站建設(shè)中頁(yè)面模板在之前的博客中 【JavaEE進(jìn)階】Spring AOP使用篇_aop多個(gè)切點(diǎn)-CSDN博客 我們主要學(xué)習(xí)了SpringAOP的應(yīng)用, 接下來(lái)我們來(lái)學(xué)習(xí)SpringAOP的原理, 也就是Spring是如何實(shí)現(xiàn)AOP的. SpringAOP 是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)AOP的,咱們學(xué)習(xí)內(nèi)容主要分以下兩部分 1.代理模式 2.Spring AOP源碼剖…

在之前的博客中?【JavaEE進(jìn)階】Spring AOP使用篇_aop多個(gè)切點(diǎn)-CSDN博客

我們主要學(xué)習(xí)了SpringAOP的應(yīng)用, 接下來(lái)我們來(lái)學(xué)習(xí)SpringAOP的原理, 也就是Spring是如何實(shí)現(xiàn)AOP的.

SpringAOP 是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)AOP的,咱們學(xué)習(xí)內(nèi)容主要分以下兩部分
1.代理模式
2.Spring AOP源碼剖析

1.代理模式

定義: 為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn), 它的作用就是通過(guò)提供一個(gè)代理類(lèi), 讓我們在調(diào)用目標(biāo)方法的時(shí)候, 不再是直接對(duì)目標(biāo)方法進(jìn)行調(diào)用, 而是通過(guò)代理類(lèi)間接調(diào)用.

在某些情況下, 一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象, 而代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用.

使用代理前:

使用代碼后:

比如房屋租賃:

Subject: 就是提前定義了房東做的事情,交給中介代理,也是中介要做的事情
RealSubiect: 房東
Proxy: 中介

UML 類(lèi)圖如下:

代理模式可以在不修改被代理對(duì)象的基礎(chǔ)上, 通過(guò)擴(kuò)展代理類(lèi), 進(jìn)行一些功能的附加與增強(qiáng)
根據(jù)代理的創(chuàng)建時(shí)期, 代理模式分為靜態(tài)代理動(dòng)態(tài)代理:

靜態(tài)代理: 由程序員創(chuàng)建代理類(lèi)或特定工具自動(dòng)生成源代碼再對(duì)其編譯,在程序運(yùn)行前代理類(lèi)的
class 文件就已經(jīng)存在了

動(dòng)態(tài)代理: 在程序運(yùn)行時(shí),運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成

靜態(tài)代理:? 由程序員創(chuàng)建代理類(lèi)或特定工具自動(dòng)生成源代碼再對(duì)其編譯, 在程序運(yùn)行前代理類(lèi)的.class 文件就已經(jīng)存在了
動(dòng)態(tài)代理:? 在程序運(yùn)行時(shí), 運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成

以房東和中介的的關(guān)系舉例:

1.1 靜態(tài)代理

以房東和中介的例子模擬靜態(tài)代理:

1.定義接口(定義房東要做的事情,也是中介需要做的事情)

package com.example.aop.proxy;/*** 業(yè)務(wù)接口類(lèi)*/
public interface HouseSubject {void rentHouse();void saleHouse();
}

?2.實(shí)現(xiàn)接口(房東出租房子)目標(biāo)對(duì)象:

package com.example.aop.proxy;/*** 業(yè)務(wù)實(shí)現(xiàn)類(lèi)*/
public class RealHouseSubject implements HouseSubject {@Overridepublic void rentHouse() {System.out.println("我是房東, 我出租房子");}@Overridepublic void saleHouse() {System.out.println("我是房東, 我出售房子");}
}

3.代理(中介,幫房東出租房子)

package com.example.aop.proxy;/*** 靜態(tài)代理的代理類(lèi) (中介)*/public class HouseProxy implements HouseSubject {//房東對(duì)象private HouseSubject target;public HouseProxy(HouseSubject target) {this.target = target;}@Overridepublic void rentHouse() {//代理前System.out.println("我是中介, 開(kāi)始代理");//出租房子target.rentHouse();//出租后System.out.println("我是中介, 結(jié)束代理");}@Overridepublic void saleHouse() {//代理前System.out.println("我是中介, 開(kāi)始代理");//出售房子target.saleHouse();//出租后System.out.println("我是中介, 結(jié)束代理");}
}

?測(cè)試:

package com.example.aop.proxy;public class Main {public static void main(String[] args) {
//        靜態(tài)代理HouseProxy proxy = new HouseProxy(new RealHouseSubject());proxy.rentHouse();proxy.saleHouse();System.out.println("========================");HouseSubject houseSubject = new RealHouseSubject();houseSubject.rentHouse();houseSubject.saleHouse();}
}

上面這個(gè)代理實(shí)現(xiàn)方式就是靜態(tài)代理(仿佛啥也沒(méi)干).

從上述程序可以看出, 雖然靜態(tài)代理也完成了對(duì)目標(biāo)對(duì)象的代理, 但是由于代碼都寫(xiě)死了, 對(duì)目標(biāo)對(duì)象的每個(gè)方法的增強(qiáng)都是手動(dòng)完成的,非常不靈活. 所以日常開(kāi)發(fā)幾乎看不到靜態(tài)代理的場(chǎng)景.?

我們修改接口(Subject)和業(yè)務(wù)實(shí)現(xiàn)類(lèi)(RealSubject)時(shí), 還需要修改代理類(lèi)(Proxy).

同樣的, 如果有新增接口(Subiect)和業(yè)務(wù)實(shí)現(xiàn)類(lèi)(RealSubiect), 也需要對(duì)每一個(gè)業(yè)務(wù)實(shí)現(xiàn)類(lèi)新增代理類(lèi)(Proxy). 既然代理的流程是一樣的, 有沒(méi)有一種辦法, 讓他們通過(guò)一個(gè)代理類(lèi)來(lái)實(shí)現(xiàn)呢?

這就需要用到動(dòng)態(tài)代理技術(shù)了?

1.2 動(dòng)態(tài)代理

相比于靜態(tài)代理來(lái)說(shuō),動(dòng)態(tài)代理更加靈活.
我們不需要針對(duì)每個(gè)目標(biāo)對(duì)象都單獨(dú)創(chuàng)建一個(gè)代理對(duì)象, 而是把這個(gè)創(chuàng)建代理對(duì)象的工作推遲到程序運(yùn)行時(shí)由JVM來(lái)實(shí)現(xiàn), 也就是說(shuō)動(dòng)態(tài)代理在程序運(yùn)行時(shí), 根據(jù)需要?jiǎng)討B(tài)創(chuàng)建生成

比如房屋中介,我不需要提前預(yù)測(cè)都有哪些業(yè)務(wù), 而是業(yè)務(wù)來(lái)了我再根據(jù)情況創(chuàng)建

我們還是先看代碼再來(lái)理解,Java也對(duì)動(dòng)態(tài)代理進(jìn)行了實(shí)現(xiàn), 并給我們提供了一些AP1, 常見(jiàn)的實(shí)現(xiàn)方式有兩種:


動(dòng)態(tài)代理在我們?nèi)粘i_(kāi)發(fā)中使用的相對(duì)較少,但是在框架中幾乎是必用的一門(mén)技術(shù), 學(xué)會(huì)了動(dòng)態(tài)代理之后, 對(duì)于我們理解和學(xué)習(xí)各種框架的原理也非常有幫助.

JDK動(dòng)態(tài)代理?

JDK動(dòng)態(tài)代理實(shí)現(xiàn)步驟:

定義JDK動(dòng)態(tài)代理類(lèi)

實(shí)現(xiàn) InvocationHandler?接口

package com.example.aop.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class JDKInvocationHandler implements InvocationHandler   {private Object target; //目標(biāo)對(duì)象public JDKInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是代理, 開(kāi)始代理");//通過(guò)反射, 調(diào)用目標(biāo)對(duì)象的方法Object result = method.invoke(target, args);System.out.println("我是代理, 結(jié)束代理");return result;}
}

創(chuàng)建一個(gè)代理對(duì)象并使用:


import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {//JDK動(dòng)態(tài)代理/***     public static Object newProxyInstance(ClassLoader loader,*                                           Class<?>[] interfaces,*                                           InvocationHandler h) {*     loader: 加載我們代理類(lèi)的classload*     interfaces: 要實(shí)現(xiàn)的接口*     h: 代理要做的事情, 需要實(shí)現(xiàn) InvocationHandler 這個(gè)接口*///目標(biāo)對(duì)象RealHouseSubject target = new RealHouseSubject();//動(dòng)態(tài)生成代理對(duì)象HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[] {HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();proxy.saleHouse();}
}

JDK動(dòng)態(tài)代理只能代理接口, 不能代理類(lèi):

運(yùn)行成功

?運(yùn)行失敗

CGLIB動(dòng)態(tài)代理

JDK 動(dòng)態(tài)代理有一個(gè)最致命的問(wèn)題是其只能代理實(shí)現(xiàn)了接口的類(lèi)
有些場(chǎng)景下,我們的業(yè)務(wù)代碼是直接實(shí)現(xiàn)的,并沒(méi)有接口定義,為了解決這個(gè)問(wèn)題,我們可以用 CGLIB 動(dòng)態(tài)代理機(jī)制來(lái)解決.

CGLIB(Code Generation Library)是一個(gè)基于ASM的字節(jié)碼生成庫(kù),它允許我們?cè)谶\(yùn)行時(shí)對(duì)字節(jié)碼進(jìn)行修改和動(dòng)態(tài)生成.? CGLIB 通過(guò)繼承方式實(shí)現(xiàn)代理,很多知名的開(kāi)源框架都使用到了CGLIB.

例如 Spring中的 AOP 模塊中: 如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,則默認(rèn)采用 JDK動(dòng)態(tài)代理, 否則采用 CGLIB 動(dòng)態(tài)代理.

CGLIB 動(dòng)態(tài)代理類(lèi)實(shí)現(xiàn)步驟

?添加依賴(lài)

和JDK動(dòng)態(tài)代理不同, CGLlB(Code Generation Library) 實(shí)際是屬于一個(gè)開(kāi)源項(xiàng)目,如果你要使用它的話(huà),需要手動(dòng)添加相關(guān)依賴(lài)

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

自定義 MethodInterceptor(方法攔截器)
實(shí)現(xiàn)MethodInterceptor接

package com.example.aop.proxy;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CGLibMethodInterceptor implements MethodInterceptor {private Object target;public CGLibMethodInterceptor(Object target) {this.target = target;}/*** 調(diào)用代理對(duì)象*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("我是中介, 開(kāi)始代理");Object result = method.invoke(target, args);System.out.println("我是中介, 結(jié)束代理");return result;}
}

創(chuàng)建代理類(lèi),并使用

package com.example.aop.proxy;import org.springframework.cglib.proxy.Enhancer;public class Main {public static void main(String[] args) {//使用CGLib完成代理//代理接口, 運(yùn)行成功HouseSubject target = new RealHouseSubject();HouseSubject houseSubject = (HouseSubject) Enhancer.create(target.getClass(), new CGLibMethodInterceptor(target));houseSubject.saleHouse();houseSubject.rentHouse();System.out.println("====================");//代理類(lèi), 運(yùn)行成功RealHouseSubject realHouseSubject =  (RealHouseSubject) Enhancer.create(target.getClass(), new CGLibMethodInterceptor(target));realHouseSubject.saleHouse();realHouseSubject.rentHouse();}
}

CGLIB既可以代理接口, 又可以代理類(lèi):?

代碼簡(jiǎn)單講解
1. Methodinterceptor

MethodInterceptor 和 JDK動(dòng)態(tài)代理中的 InvocationHandler?類(lèi)似,它只定義了一個(gè)方法 intercept(),用于增強(qiáng)目標(biāo)方法,

public interface MethodInterceptor extends Callback {/*** 參數(shù)說(shuō)明: * o: 被代理的對(duì)象 * method: ?標(biāo)?法(被攔截的?法, 也就是需要增強(qiáng)的?法) * objects: ?法?參 * methodProxy: ?于調(diào)?原始?法 */Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable;
}

2. Enhancer.create()
Enhancer.create()用來(lái)生成一個(gè)代理對(duì)象

public static Object create(Class type, Callback callback) {//...代碼省略 
}

參數(shù)說(shuō)明:

type: 被代理類(lèi)的類(lèi)型(類(lèi)或接口)
callback: 自定義方法攔截器 MethodInterceptor

JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理的區(qū)別

JDK 可以代理接口, 不可以代理類(lèi)

CGLib 既可以代理接口, 又可以代理類(lèi)

JDK 動(dòng)態(tài)代理是 Java 標(biāo)準(zhǔn)庫(kù)的一部分,不需要額外的依賴(lài)。只要使用的是 Java 開(kāi)發(fā)環(huán)境,就可以直接使用 JDK 動(dòng)態(tài)代理

CGLIB 是一個(gè)第三方庫(kù),需要在項(xiàng)目中添加相應(yīng)的依賴(lài)才能使用。

2. SpringAOP 源碼閱讀

SpringAOP 主要基于兩種方式實(shí)現(xiàn)的: JDK及 CGLIB 的方式

生成代理對(duì)象的邏輯在父類(lèi) AbstractAutoProxyCreator 中
Spring 對(duì)于 AOP 的實(shí)現(xiàn),基本上都是靠? AnnotationAwareAspectJAutoProxyCreator 去完成


/*** {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation* that wraps each eligible bean with an AOP proxy, delegating to specified interceptors* before invoking the bean itself.** <p>This class distinguishes between "common" interceptors: shared for all proxies it* creates, and "specific" interceptors: unique per bean instance. There need not be any* common interceptors. If there are, they are set using the interceptorNames property.* As with {@link org.springframework.aop.framework.ProxyFactoryBean}, interceptors names* in the current factory are used rather than bean references to allow correct handling* of prototype advisors and interceptors: for example, to support stateful mixins.* Any advice type is supported for {@link #setInterceptorNames "interceptorNames"} entries.** <p>Such auto-proxying is particularly useful if there's a large number of beans that* need to be wrapped with similar proxies, i.e. delegating to the same interceptors.* Instead of x repetitive proxy definitions for x target beans, you can register* one single such post processor with the bean factory to achieve the same effect.** <p>Subclasses can apply any strategy to decide if a bean is to be proxied, e.g. by type,* by name, by definition details, etc. They can also return additional interceptors that* should just be applied to the specific bean instance. A simple concrete implementation is* {@link BeanNameAutoProxyCreator}, identifying the beans to be proxied via given names.** <p>Any number of {@link TargetSourceCreator} implementations can be used to create* a custom target source: for example, to pool prototype objects. Auto-proxying will* occur even if there is no advice, as long as a TargetSourceCreator specifies a custom* {@link org.springframework.aop.TargetSource}. If there are no TargetSourceCreators set,* or if none matches, a {@link org.springframework.aop.target.SingletonTargetSource}* will be used by default to wrap the target bean instance.** @author Juergen Hoeller* @author Rod Johnson* @author Rob Harrop* @author Sam Brannen* @since 13.10.2003* @see #setInterceptorNames* @see #getAdvicesAndAdvisorsForBean* @see BeanNameAutoProxyCreator* @see DefaultAdvisorAutoProxyCreator*/
@SuppressWarnings("serial")
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupportimplements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {/*** Convenience constant for subclasses: Return value for "do not proxy".* @see #getAdvicesAndAdvisorsForBean*/@Nullableprotected static final Object[] DO_NOT_PROXY = null;/*** Convenience constant for subclasses: Return value for* "proxy without additional interceptors, just the common ones".* @see #getAdvicesAndAdvisorsForBean*/protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0];/** Logger available to subclasses. */protected final Log logger = LogFactory.getLog(getClass());/** Default is global AdvisorAdapterRegistry. */private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();/*** Indicates whether the proxy should be frozen. Overridden from super* to prevent the configuration from becoming frozen too early.*/private boolean freezeProxy = false;/** Default is no common interceptors. */private String[] interceptorNames = new String[0];private boolean applyCommonInterceptorsFirst = true;@Nullableprivate TargetSourceCreator[] customTargetSourceCreators;@Nullableprivate BeanFactory beanFactory;private final Set<String> targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap<>(16));private final Map<Object, Object> earlyBeanReferences = new ConcurrentHashMap<>(16);private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<>(16);private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);/*** Set whether the proxy should be frozen, preventing advice* from being added to it once it is created.* <p>Overridden from the superclass to prevent the proxy configuration* from being frozen before the proxy is created.*/@Overridepublic void setFrozen(boolean frozen) {this.freezeProxy = frozen;}@Overridepublic boolean isFrozen() {return this.freezeProxy;}/*** Specify the {@link AdvisorAdapterRegistry} to use.* <p>Default is the global {@link AdvisorAdapterRegistry}.* @see org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry*/public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) {this.advisorAdapterRegistry = advisorAdapterRegistry;}/*** Set custom {@code TargetSourceCreators} to be applied in this order.* If the list is empty, or they all return null, a {@link SingletonTargetSource}* will be created for each bean.* <p>Note that TargetSourceCreators will kick in even for target beans* where no advices or advisors have been found. If a {@code TargetSourceCreator}* returns a {@link TargetSource} for a specific bean, that bean will be proxied* in any case.* <p>{@code TargetSourceCreators} can only be invoked if this post processor is used* in a {@link BeanFactory} and its {@link BeanFactoryAware} callback is triggered.* @param targetSourceCreators the list of {@code TargetSourceCreators}.* Ordering is significant: The {@code TargetSource} returned from the first matching* {@code TargetSourceCreator} (that is, the first that returns non-null) will be used.*/public void setCustomTargetSourceCreators(TargetSourceCreator... targetSourceCreators) {this.customTargetSourceCreators = targetSourceCreators;}/*** Set the common interceptors. These must be bean names in the current factory.* They can be of any advice or advisor type Spring supports.* <p>If this property isn't set, there will be zero common interceptors.* This is perfectly valid, if "specific" interceptors such as matching* Advisors are all we want.*/public void setInterceptorNames(String... interceptorNames) {this.interceptorNames = interceptorNames;}/*** Set whether the common interceptors should be applied before bean-specific ones.* Default is "true"; else, bean-specific interceptors will get applied first.*/public void setApplyCommonInterceptorsFirst(boolean applyCommonInterceptorsFirst) {this.applyCommonInterceptorsFirst = applyCommonInterceptorsFirst;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = beanFactory;}/*** Return the owning {@link BeanFactory}.* May be {@code null}, as this post-processor doesn't need to belong to a bean factory.*/@Nullableprotected BeanFactory getBeanFactory() {return this.beanFactory;}@Override@Nullablepublic Class<?> predictBeanType(Class<?> beanClass, String beanName) {if (this.proxyTypes.isEmpty()) {return null;}Object cacheKey = getCacheKey(beanClass, beanName);return this.proxyTypes.get(cacheKey);}@Overridepublic Class<?> determineBeanType(Class<?> beanClass, String beanName) {Object cacheKey = getCacheKey(beanClass, beanName);Class<?> proxyType = this.proxyTypes.get(cacheKey);if (proxyType == null) {TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}}else {targetSource = EmptyTargetSource.forClass(beanClass);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);proxyType = createProxyClass(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxyType);}}return (proxyType != null ? proxyType : beanClass);}@Override@Nullablepublic Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) {return null;}@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);this.earlyBeanReferences.put(cacheKey, bean);return wrapIfNecessary(bean, beanName, cacheKey);}@Override@Nullablepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {Object cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}// Create proxy here if we have a custom TargetSource.// Suppresses unnecessary default instantiation of the target bean:// The TargetSource will handle target instances in a custom fashion.TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {return pvs;  // skip postProcessPropertyValues}/*** Create a proxy with the configured interceptors if the bean is* identified as one to proxy by the subclass.* @see #getAdvicesAndAdvisorsForBean*/@Override@Nullablepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyBeanReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}/*** Build a cache key for the given bean class and bean name.* <p>Note: As of 4.2.3, this implementation does not return a concatenated* class/name String anymore but rather the most efficient cache key possible:* a plain bean name, prepended with {@link BeanFactory#FACTORY_BEAN_PREFIX}* in case of a {@code FactoryBean}; or if no bean name specified, then the* given bean {@code Class} as-is.* @param beanClass the bean class* @param beanName the bean name* @return the cache key for the given class and name*/protected Object getCacheKey(Class<?> beanClass, @Nullable String beanName) {if (StringUtils.hasLength(beanName)) {return (FactoryBean.class.isAssignableFrom(beanClass) ?BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName);}else {return beanClass;}}/*** Wrap the given bean if necessary, i.e. if it is eligible for being proxied.* @param bean the raw bean instance* @param beanName the name of the bean* @param cacheKey the cache key for metadata access* @return a proxy wrapping the bean, or the raw bean instance as-is*/protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}/*** Return whether the given bean class represents an infrastructure class* that should never be proxied.* <p>The default implementation considers Advices, Advisors and* AopInfrastructureBeans as infrastructure classes.* @param beanClass the class of the bean* @return whether the bean represents an infrastructure class* @see org.aopalliance.aop.Advice* @see org.springframework.aop.Advisor* @see org.springframework.aop.framework.AopInfrastructureBean* @see #shouldSkip*/protected boolean isInfrastructureClass(Class<?> beanClass) {boolean retVal = Advice.class.isAssignableFrom(beanClass) ||Pointcut.class.isAssignableFrom(beanClass) ||Advisor.class.isAssignableFrom(beanClass) ||AopInfrastructureBean.class.isAssignableFrom(beanClass);if (retVal && logger.isTraceEnabled()) {logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");}return retVal;}/*** Subclasses should override this method to return {@code true} if the* given bean should not be considered for auto-proxying by this post-processor.* <p>Sometimes we need to be able to avoid this happening, e.g. if it will lead to* a circular reference or if the existing target instance needs to be preserved.* This implementation returns {@code false} unless the bean name indicates an* "original instance" according to {@code AutowireCapableBeanFactory} conventions.* @param beanClass the class of the bean* @param beanName the name of the bean* @return whether to skip the given bean* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#ORIGINAL_INSTANCE_SUFFIX*/protected boolean shouldSkip(Class<?> beanClass, String beanName) {return AutoProxyUtils.isOriginalInstance(beanName, beanClass);}/*** Create a target source for bean instances. Uses any TargetSourceCreators if set.* Returns {@code null} if no custom TargetSource should be used.* <p>This implementation uses the "customTargetSourceCreators" property.* Subclasses can override this method to use a different mechanism.* @param beanClass the class of the bean to create a TargetSource for* @param beanName the name of the bean* @return a TargetSource for this bean* @see #setCustomTargetSourceCreators*/@Nullableprotected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {// We can't create fancy target sources for directly registered singletons.if (this.customTargetSourceCreators != null &&this.beanFactory != null && this.beanFactory.containsBean(beanName)) {for (TargetSourceCreator tsc : this.customTargetSourceCreators) {TargetSource ts = tsc.getTargetSource(beanClass, beanName);if (ts != null) {// Found a matching TargetSource.if (logger.isTraceEnabled()) {logger.trace("TargetSourceCreator [" + tsc +"] found custom TargetSource for bean with name '" + beanName + "'");}return ts;}}}// No custom TargetSource found.return null;}/*** Create an AOP proxy for the given bean.* @param beanClass the class of the bean* @param beanName the name of the bean* @param specificInterceptors the set of interceptors that is* specific to this bean (may be empty, but not null)* @param targetSource the TargetSource for the proxy,* already pre-configured to access the bean* @return the AOP proxy for the bean* @see #buildAdvisors*/protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {return buildProxy(beanClass, beanName, specificInterceptors, targetSource, false);}private Class<?> createProxyClass(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {return (Class<?>) buildProxy(beanClass, beanName, specificInterceptors, targetSource, true);}private Object buildProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (proxyFactory.isProxyTargetClass()) {// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.for (Class<?> ifc : beanClass.getInterfaces()) {proxyFactory.addInterface(ifc);}}}else {// No proxyTargetClass flag enforced, let's apply our default checks...if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}// Use original ClassLoader if bean class not locally loaded in overriding class loaderClassLoader classLoader = getProxyClassLoader();if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) {classLoader = smartClassLoader.getOriginalClassLoader();}return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));}/*** Determine whether the given bean should be proxied with its target class rather than its interfaces.* <p>Checks the {@link AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE "preserveTargetClass" attribute}* of the corresponding bean definition.* @param beanClass the class of the bean* @param beanName the name of the bean* @return whether the given bean should be proxied with its target class* @see AutoProxyUtils#shouldProxyTargetClass*/protected boolean shouldProxyTargetClass(Class<?> beanClass, @Nullable String beanName) {return (this.beanFactory instanceof ConfigurableListableBeanFactory clbf &&AutoProxyUtils.shouldProxyTargetClass(clbf, beanName));}/*** Return whether the Advisors returned by the subclass are pre-filtered* to match the bean's target class already, allowing the ClassFilter check* to be skipped when building advisors chains for AOP invocations.* <p>Default is {@code false}. Subclasses may override this if they* will always return pre-filtered Advisors.* @return whether the Advisors are pre-filtered* @see #getAdvicesAndAdvisorsForBean* @see org.springframework.aop.framework.Advised#setPreFiltered*/protected boolean advisorsPreFiltered() {return false;}/*** Determine the advisors for the given bean, including the specific interceptors* as well as the common interceptor, all adapted to the Advisor interface.* @param beanName the name of the bean* @param specificInterceptors the set of interceptors that is* specific to this bean (may be empty, but not null)* @return the list of Advisors for the given bean*/protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {// Handle prototypes correctly...Advisor[] commonInterceptors = resolveInterceptorNames();List<Object> allInterceptors = new ArrayList<>();if (specificInterceptors != null) {if (specificInterceptors.length > 0) {// specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORSallInterceptors.addAll(Arrays.asList(specificInterceptors));}if (commonInterceptors.length > 0) {if (this.applyCommonInterceptorsFirst) {allInterceptors.addAll(0, Arrays.asList(commonInterceptors));}else {allInterceptors.addAll(Arrays.asList(commonInterceptors));}}}if (logger.isTraceEnabled()) {int nrOfCommonInterceptors = commonInterceptors.length;int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +" common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");}Advisor[] advisors = new Advisor[allInterceptors.size()];for (int i = 0; i < allInterceptors.size(); i++) {advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));}return advisors;}/*** Resolves the specified interceptor names to Advisor objects.* @see #setInterceptorNames*/private Advisor[] resolveInterceptorNames() {BeanFactory bf = this.beanFactory;ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory _cbf ? _cbf : null);List<Advisor> advisors = new ArrayList<>();for (String beanName : this.interceptorNames) {if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {Assert.state(bf != null, "BeanFactory required for resolving interceptor names");Object next = bf.getBean(beanName);advisors.add(this.advisorAdapterRegistry.wrap(next));}}return advisors.toArray(new Advisor[0]);}/*** Subclasses may choose to implement this: for example,* to change the interfaces exposed.* <p>The default implementation is empty.* @param proxyFactory a ProxyFactory that is already configured with* TargetSource and interfaces and will be used to create the proxy* immediately after this method returns*/protected void customizeProxyFactory(ProxyFactory proxyFactory) {}/*** Return whether the given bean is to be proxied, what additional* advices (e.g. AOP Alliance interceptors) and advisors to apply.* @param beanClass the class of the bean to advise* @param beanName the name of the bean* @param customTargetSource the TargetSource returned by the* {@link #getCustomTargetSource} method: may be ignored.* Will be {@code null} if no custom target source is in use.* @return an array of additional interceptors for the particular bean;* or an empty array if no additional interceptors but just the common ones;* or {@code null} if no proxy at all, not even with the common interceptors.* See constants DO_NOT_PROXY and PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS.* @throws BeansException in case of errors* @see #DO_NOT_PROXY* @see #PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS*/@Nullableprotected abstract Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,@Nullable TargetSource customTargetSource) throws BeansException;}

代理工廠有一個(gè)重要的屬性: proxyTargetClass,默認(rèn)值為false.也可以通過(guò)程序設(shè)置

Spring Boot 2.X開(kāi)始,默認(rèn)使用CGLIB代理

可以通過(guò)配置項(xiàng) spring.aop.proxy-target-class=false 來(lái)進(jìn)行修改, 設(shè)置默認(rèn)為jdk代理
SpringBoot設(shè)置 @EnableAspectJAutoProxy 無(wú)效, 因?yàn)镾pring Boot 默認(rèn)使用
AopAutoConfiguration 進(jìn)行裝配

我看點(diǎn)進(jìn)去看代理??的代碼


/*** Factory for AOP proxies for programmatic use, rather than via declarative* setup in a bean factory. This class provides a simple way of obtaining* and configuring AOP proxy instances in custom user code.** @author Rod Johnson* @author Juergen Hoeller* @author Rob Harrop* @since 14.03.2003*/
@SuppressWarnings("serial")
public class ProxyFactory extends ProxyCreatorSupport {/*** Create a new ProxyFactory.*/public ProxyFactory() {}/*** Create a new ProxyFactory.* <p>Will proxy all interfaces that the given target implements.* @param target the target object to be proxied*/public ProxyFactory(Object target) {setTarget(target);setInterfaces(ClassUtils.getAllInterfaces(target));}/*** Create a new ProxyFactory.* <p>No target, only interfaces. Must add interceptors.* @param proxyInterfaces the interfaces that the proxy should implement*/public ProxyFactory(Class<?>... proxyInterfaces) {setInterfaces(proxyInterfaces);}/*** Create a new ProxyFactory for the given interface and interceptor.* <p>Convenience method for creating a proxy for a single interceptor,* assuming that the interceptor handles all calls itself rather than* delegating to a target, like in the case of remoting proxies.* @param proxyInterface the interface that the proxy should implement* @param interceptor the interceptor that the proxy should invoke*/public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {addInterface(proxyInterface);addAdvice(interceptor);}/*** Create a ProxyFactory for the specified {@code TargetSource},* making the proxy implement the specified interface.* @param proxyInterface the interface that the proxy should implement* @param targetSource the TargetSource that the proxy should invoke*/public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {addInterface(proxyInterface);setTargetSource(targetSource);}/*** Create a new proxy according to the settings in this factory.* <p>Can be called repeatedly. Effect will vary if we've added* or removed interfaces. Can add and remove interceptors.* <p>Uses a default class loader: Usually, the thread context class loader* (if necessary for proxy creation).* @return the proxy object*/public Object getProxy() {return createAopProxy().getProxy();}/*** Create a new proxy according to the settings in this factory.* <p>Can be called repeatedly. Effect will vary if we've added* or removed interfaces. Can add and remove interceptors.* <p>Uses the given class loader (if necessary for proxy creation).* @param classLoader the class loader to create the proxy with* (or {@code null} for the low-level proxy facility's default)* @return the proxy object*/public Object getProxy(@Nullable ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);}/*** Determine the proxy class according to the settings in this factory.* @param classLoader the class loader to create the proxy class with* (or {@code null} for the low-level proxy facility's default)* @return the proxy class* @since 6.0*/public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {return createAopProxy().getProxyClass(classLoader);}/*** Create a new proxy for the given interface and interceptor.* <p>Convenience method for creating a proxy for a single interceptor,* assuming that the interceptor handles all calls itself rather than* delegating to a target, like in the case of remoting proxies.* @param proxyInterface the interface that the proxy should implement* @param interceptor the interceptor that the proxy should invoke* @return the proxy object* @see #ProxyFactory(Class, org.aopalliance.intercept.Interceptor)*/@SuppressWarnings("unchecked")public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();}/*** Create a proxy for the specified {@code TargetSource},* implementing the specified interface.* @param proxyInterface the interface that the proxy should implement* @param targetSource the TargetSource that the proxy should invoke* @return the proxy object* @see #ProxyFactory(Class, org.springframework.aop.TargetSource)*/@SuppressWarnings("unchecked")public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {return (T) new ProxyFactory(proxyInterface, targetSource).getProxy();}/*** Create a proxy for the specified {@code TargetSource} that extends* the target class of the {@code TargetSource}.* @param targetSource the TargetSource that the proxy should invoke* @return the proxy object*/public static Object getProxy(TargetSource targetSource) {if (targetSource.getTargetClass() == null) {throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTargetSource(targetSource);proxyFactory.setProxyTargetClass(true);return proxyFactory.getProxy();}}

createAopProxy的實(shí)現(xiàn)在 DefaultAopProxyFactory中


/*** Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy* or a JDK dynamic proxy.** <p>Creates a CGLIB proxy if one the following is true for a given* {@link AdvisedSupport} instance:* <ul>* <li>the {@code optimize} flag is set* <li>the {@code proxyTargetClass} flag is set* <li>no proxy interfaces have been specified* </ul>** <p>In general, specify {@code proxyTargetClass} to enforce a CGLIB proxy,* or specify one or more interfaces to use a JDK dynamic proxy.** @author Rod Johnson* @author Juergen Hoeller* @author Sebastien Deleuze* @author Sam Brannen* @since 12.03.2004* @see AdvisedSupport#setOptimize* @see AdvisedSupport#setProxyTargetClass* @see AdvisedSupport#setInterfaces*/
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {/*** Singleton instance of this class.* @since 6.0.10*/public static final DefaultAopProxyFactory INSTANCE = new DefaultAopProxyFactory();private static final long serialVersionUID = 7930414337282325166L;@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}/*** Determine whether the supplied {@link AdvisedSupport} has only the* {@link org.springframework.aop.SpringProxy} interface specified* (or no proxy interfaces specified at all).*/private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class<?>[] ifcs = config.getProxiedInterfaces();return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));}}

接下來(lái)就是創(chuàng)建代理了
JDK動(dòng)態(tài)代理

CGLIB動(dòng)態(tài)代理

CglibAopProxy類(lèi)中:

@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {return buildProxy(classLoader, false);}@Overridepublic Class<?> getProxyClass(@Nullable ClassLoader classLoader) {return (Class<?>) buildProxy(classLoader, true);}private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());}try {Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");Class<?> proxySuperClass = rootClass;if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {proxySuperClass = rootClass.getSuperclass();Class<?>[] additionalInterfaces = rootClass.getInterfaces();for (Class<?> additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);}}// Validate the class, writing log messages as necessary.validateClassIfNecessary(proxySuperClass, classLoader);// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader smartClassLoader &&smartClassLoader.isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setAttemptLoad(true);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveProxyCallbackFilter filter = new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset);enhancer.setCallbackFilter(filter);enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.// ProxyCallbackFilter has method introspection capability with Advisor access.try {return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));}finally {// Reduce ProxyCallbackFilter to key-only state for its class cache role// in the CGLIB$CALLBACK_FILTER field, not leaking any Advisor state...filter.advised.reduceToAdvisorKey();}}catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +": Common causes of this problem include using a final class or a non-visible class",ex);}catch (Throwable ex) {// TargetSource.getTarget() failedthrow new AopConfigException("Unexpected AOP exception", ex);}}

回顧:

1.什么是AOP

2.SpringAOP 的實(shí)現(xiàn)方式有哪些?

3.SpringAOP 的實(shí)現(xiàn)原理 (基于動(dòng)態(tài)代理 1.JDK 2.CGLib)

4. Spring 使用的是那種代理方式?

Spring?默認(rèn) proxyTargetClass 值為false, 如果實(shí)現(xiàn)了接口, 使用JDK代理, 如果是普通類(lèi)則使用CGLib代理

SpringBoot從2.x之后, proxyTargetClass值為true, 默認(rèn)是使用CGLib代理

5. JDK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理的區(qū)別?

總結(jié)?

http://www.risenshineclean.com/news/39723.html

相關(guān)文章:

  • 天津個(gè)人網(wǎng)站建設(shè)廊坊seo優(yōu)化排名
  • 志愿者管理網(wǎng)站開(kāi)發(fā)的需求分析 基于 java廣告接單平臺(tái)有哪些
  • 南沙網(wǎng)站建設(shè)小說(shuō)搜索風(fēng)云榜
  • 懷集建設(shè)房管部門(mén)網(wǎng)站淘寶美工培訓(xùn)
  • 江西景德鎮(zhèn)建設(shè)廳網(wǎng)站seo行業(yè)崗位
  • 江西會(huì)昌建設(shè)局網(wǎng)站建站軟件可以不通過(guò)網(wǎng)絡(luò)建設(shè)嗎
  • 網(wǎng)站管理系統(tǒng)后臺(tái)不能發(fā)布文章了網(wǎng)絡(luò)營(yíng)銷(xiāo)顧問(wèn)
  • 石家莊網(wǎng)站制作公司最大的中文搜索引擎
  • 網(wǎng)站設(shè)計(jì)制作系統(tǒng)哪個(gè)好搜索引擎優(yōu)化的根本目的
  • 制作網(wǎng)站哪里好廣告投放方案
  • 網(wǎng)站 切圖中國(guó)疫情最新情況
  • 工業(yè)設(shè)計(jì)和產(chǎn)品設(shè)計(jì)哪個(gè)好seo關(guān)鍵詞優(yōu)化軟件合作
  • 如何查公司的工商注冊(cè)信息網(wǎng)站推廣與優(yōu)化方案
  • 深圳企業(yè)網(wǎng)站制作哪家好百度搜索引擎收錄
  • 做網(wǎng)站人太原網(wǎng)站建設(shè)制作
  • 網(wǎng)站哪個(gè)公司好南寧seo外包要求
  • vue做網(wǎng)站的實(shí)例網(wǎng)絡(luò)培訓(xùn)心得
  • 北關(guān)網(wǎng)站制作百度推廣官方
  • 打字做任務(wù)賺錢(qián)的網(wǎng)站什么都不懂能去干運(yùn)營(yíng)嗎
  • 模板網(wǎng)站和定制網(wǎng)站影響排名seo推廣代理
  • 有什么網(wǎng)站可以做初中試題怎么做app推廣和宣傳
  • 做時(shí)時(shí)彩網(wǎng)站百度搜索關(guān)鍵詞排名優(yōu)化技術(shù)
  • 精品課程網(wǎng)站建設(shè)方案安卓aso優(yōu)化工具
  • 有做貨 物的網(wǎng)站嗎今天新聞?lì)^條新聞
  • 建設(shè)什么網(wǎng)站新品推廣活動(dòng)方案
  • 國(guó)內(nèi)做的好的電商網(wǎng)站有哪些方面百度在線(xiàn)使用網(wǎng)頁(yè)版
  • 自己做的網(wǎng)站可以用于百度推廣嗎線(xiàn)上營(yíng)銷(xiāo)推廣方案有哪些
  • nginx wordpress ssl網(wǎng)站排名優(yōu)化外包公司
  • 怎么做監(jiān)測(cè)網(wǎng)站的瀏覽量app推廣地推接單網(wǎng)
  • 溫州網(wǎng)站建設(shè)培訓(xùn)seoul是什么品牌