Spring AOP的核心是基於代理實現的,代理有jdk基於接口的動態代理和基於asm實現的容許類沒有接口的cglib代理,上一小結,已經分析過Spring封裝了使用了@Aspect
註解的類,並將切面方法封裝成實現了AbstractAspectJAdvice
的類放入緩存中,等待建立代理對象時使用,AbstractAspectJAdvice
有三個參數:java
//切面方法 諸如使用了@Before這類的方法
Method aspectJAdviceMethod
//封裝了expression表達式,給定一個類和方法名,能返回是否匹配
AspectJExpressionPointcut pointcut
//能夠建立一個切面類,而後經過反射執行切面方法
AspectInstanceFactory aspectInstanceFactory
複製代碼
經過這個對象就能夠攔截全部的類的建立找出符合條件的bean建立代理執行加強操做,這也是spring的實現原理。git
jdk代理是基於接口的代理,因此被代理的對象必須是有接口實現的類,代理建立時經過Proxy.newProxyInstance
實現的,這個方法有三個參數:github
//指定要使用的類加載器
ClassLoader loader,
//被代理的類所實現的接口,加強接口的方法
Class<?>[] interfaces,
//方法處理器,會攔截全部方法,而後執行加強參數。
InvocationHandler inoker
複製代碼
簡單實例redis
訂單操做接口spring
public interface OrderUpdateService {
/** * 訂單付款 * @param orderAmt */
void payOrder(String orderAmt);
}
複製代碼
訂單操做實現類express
@Slf4j
public class OrderUpdateServiceImpl implements OrderUpdateService {
public void payOrder(String orderAmt) {
log.info("訂單付款中.....");
}
}
複製代碼
訂單代理類數組
@Slf4j
public class OrderUpdateServiceImplProxy {
//目標類 要加強的類
private OrderUpdateService updateService;
public OrderUpdateServiceImplProxy(OrderUpdateService updateService) {
this.updateService = updateService;
}
public OrderUpdateService getProxy() {
return (OrderUpdateService) Proxy.newProxyInstance(OrderUpdateServiceImplProxy.class.getClassLoader(), new Class[]{OrderUpdateService.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("payOrder".equals(method.getName())) {
log.info("付款前處理");
method.invoke(updateService, args);
log.info("付款後處理");
}
return proxy;
}
});
}
}
複製代碼
demo驗證緩存
public static void main(String[] args) {
OrderUpdateService orderUpdateService = new OrderUpdateServiceImpl();
OrderUpdateServiceImplProxy proxy = new OrderUpdateServiceImplProxy(orderUpdateService);
orderUpdateService = proxy.getProxy();
orderUpdateService.payOrder("100");
}
複製代碼
驗證結果架構
11:18:07.859 [main] INFO s.a.e.p.OrderUpdateServiceImplProxy - 付款前處理
11:18:07.890 [main] INFO s.a.e.p.s.OrderUpdateServiceImpl - 訂單付款中.....
11:18:07.890 [main] INFO s.a.e.p.OrderUpdateServiceImplProxy - 付款後處理
複製代碼
payOrder方法的確時被加強了。app
Cglib是一個強大的、高性能的代碼生成包,它普遍被許多AOP框架使用,爲他們提供方法的攔截。以下圖所示Cglib與Spring等應用的關係。
Bytecode
,字節碼是Java爲了保證「一次編譯、處處運行」而產生的一種虛擬指令格式,例如iload_0、iconst_一、if_icmpne、dup等ASM
,這是一種直接操做字節碼的框架,應用ASM須要對Java字節碼、Class結構比較熟悉ASM
之上的是CGLIB
、Groovy
、BeanShell
,後兩種並非Java體系中的內容而是腳本語言,它們經過ASM框架生成字節碼變相執行Java代碼,這說明在JVM中執行程序並不必定非要寫Java代碼----只要你能生成Java字節碼,JVM並不關心字節碼的來源,固然經過Java代碼生成的JVM字節碼是經過編譯器直接生成的,算是最「正統」的JVM字節碼CGLIB
、Groovy
、BeanShell
之上的就是Hibernate
、Spring AOP
這些框架了,這一層你們都比較熟悉因此,Cglib的實現是在字節碼的基礎上的,而且使用了開源的ASM讀取字節碼,對類實現加強功能的。
Cglib能夠經過Callback
回調函數完成對方法的加強,經過CallbackFilter
函數過濾符合條件的函數,Spring也是基於這兩個接口完成bean的加強功能的,因此能夠猜想前面Advice
參數的pointCut
方法匹配器就是在CallbackFilter
中起做用的。下來看看cglib的簡單使用:
下面的驗證是攔截GameService
的playGames
方法,在playGames
方法以前,執行TransactionManager
類的start
和commit
方法。
@Test
public void testProxyFilter() throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(GameService.class);
// NoOp.INSTANCE 表示沒有被匹配的方法不執行任何操做 若是CallbackFilter返回0
//表明使用callbacks集合中的第一個元素執行方法攔截策略 1 則使用第二個。
Callback[] callbacks = {new TransactionInterceptor(), NoOp.INSTANCE};
//設置回調函數集合
enhancer.setCallbacks(callbacks);
//根據回調過濾器返回指定回調函數索引
enhancer.setCallbackFilter(new TransactionFilter());
GameService gameService = (GameService) enhancer.create();
Person person = new Person("lijinpeng", 26, false, new UserDao());
gameService.setPerson(person);
gameService.playGames();
}
複製代碼
執行結果:playGames
獲得了加強。
[2019-06-03 15:13:59] [INFO ][s.r.beans.models.aop.TransactionManager][15][start][]start tx
[2019-06-03 15:13:59] [INFO ][s.road.beans.models.scan.GameService][29][playGames][]person-name:lijinpeng play games
[2019-06-03 15:13:59] [INFO ][s.r.beans.models.aop.TransactionManager][20][commit][]commit tx
複製代碼
方法加強器
@Slf4j
public class TransactionManager {
public void start() {
log.info("start tx");
MessageTracerUtils.addMessage("start tx");
}
public void commit() {
log.info("commit tx");
MessageTracerUtils.addMessage("commit tx");
}
public void rollback() {
log.info("rollback tx");
MessageTracerUtils.addMessage("rollback tx");
}
public Object getAspectInstance() {
return new TransactionManager();
}
}
複製代碼
方法攔截器
//方法攔截器 會攔截 全部方法 ,因此須要加判斷 cglib 還提供了filter過濾器 能夠用於過濾指定方法
public class TransactionInterceptor implements MethodInterceptor {
TransactionManager tx = new TransactionManager();
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
tx.start();
Object value = methodProxy.invokeSuper(o, objects);
tx.commit();
return value;
}
}
複製代碼
經過CallbackFilter
接口,能夠自定義攔截加強策略,返回的是前面設置的Callbacks
集合中的回調函數索引,這裏的含義是,若是方法名是playGames
則使用Callbacks集合中索引爲0的回調函數即TransactionInterceptor
,該回調函數就是要在playGames
前執行TransactionManager
的start()
方法,playGames
方法後執行commit()
,不然使用NoOp.INSTANCE
表示什麼也不作。
public class TransactionFilter implements CallbackFilter {
//返回回調函數在集合中的索引
public int accept(Method method) {
if ("playGames".equals(method.getName())) {
return 0;
} else {
return 1;
}
}
}
複製代碼
cglib代理經過Callback方法控制目標方法的加強邏輯,經過CallbackFilter用來指定適配的方法使用不通的回調函數完成不通的功能加強,cglib還能夠爲類動態得建立新得方法,不過知道cglib如何實現代理對Spring AOP的源碼學習來講已經足夠了。
在Spring中,並非全部的bean都須要建立代理,在Spring Aop中,只有被咱們配置的Aspect切面類的PointCut表達式匹配的類纔會建立代理,因此在建立代理以前須要對要建立bean進行校驗以判斷該類是否須要被建立爲代理對象。上一篇的結尾處分析過,當解析完@Aspect註解的切面類後,就會調用BeanPostProcessor
的postProcessAfterInitialization
的方法去建立一個代理對象,這個方法會調用wrapIfNecessary
方法,這個方法會完成bean是否須要被建立爲代理的校驗,在這個方法裏面,有一個核心的方法getAdvicesAndAdvisorsForBean
,這個方法會返回一個Advisor
數組集合,這個數組集合的Advisor即表示要建立的bean是否能被Advisor的Advice中的PointCut匹配到,若是能夠匹配到,則爲bean建立代理。AbstractAdvisorAutoProxyCreator
完成這個方法的功能實現:
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
//查找全部須要做用於beanClass的Advisor
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
//直接返回null
return DO_NOT_PROXY;
}
return advisors.toArray();
}
複製代碼
核心方法是 findEligibleAdvisors(beanClass, beanName)
:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//核心方法1:查找容器中全部已經生成了的Advisor,這是第二次執行
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//核心方法2:從容器中全部的Advisro找出可以做用於beanClass的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//爲Advisor提供鉤子方法 方便擴展Advisor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//根據Order排序Advisor
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
複製代碼
這個方法首先又再次調用了findCandidateAdvisors
方法,這個方法在解析切面類的時候也調用了一次,目的是爲切面類的切面方法生成Advisor
,並根據註解爲每一個Advisor
再生成一個Advice
對象。這裏第二次調用的目的是獲取容器中全部生成過的Advisor
,而後調用findAdvisorsThatCanApply方法找出全部可以做用於目標bean的Advisor
,extendAdvisors
方法是爲了方便擴展Advisor
,sortAdvisors
是基於Order
接口排序Advisor
的。接下來主要來看下前兩個方法: AnnotationAwareAspectJAutoProxyCreator
重寫了findCandidateAdvisors
方法,在重寫了的方法裏面,先調用了父類的,而後調用了基於註解的獲取Advisor
的方法。
@Override
protected List<Advisor> findCandidateAdvisors() {
//查找構建全部基於XML配置的切面類的切面方法
List<Advisor> advisors = super.findCandidateAdvisors();
//查找構建全部基於註解的切面類的切面方法
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
複製代碼
此次咱們來看看基於xml的切面類是如何獲取容器中的Advisor
的。
public List<Advisor> findAdvisorBeans() {
String[] advisorNames = null;
synchronized (this) {
//這個階段若是xml配置了切面類 cachedAdvisorBeanNames 應該已經包含了切面方法生成的切面類的 //beanName了
advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
//Aspect 多是基於FactroyBean建立的,這裏不實例化Aspect,仍是應該交給代理實現
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
}
if (advisorNames.length == 0) {
return new LinkedList<Advisor>();
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
//直接經過切面方法生成的BeanDefinition 獲取bean實例
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
//異常處理
......
}
return advisors;
}
複製代碼
從上面的代碼看獲取xml配置的切面方法生成的Advisor很簡單,其實否則,真正複雜的邏輯是讀取xml生成BeanDefinition
和getBean()
的過程,這裏就不展開分析了。 這個方法過程就是找出由XMl配置的切面類生成的全部切面方法的Advisor
,而後遍歷beanName
,生成實例對象,然會返回生成後的Advisor
實例對象。
接下來再來看看基於註解的Advisor
是如何獲取的。
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
//此時的aspectNames已經不爲空了,省略以前生成Advisor的部分
.......
return advisors;
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String aspectName : aspectNames) {
//獲取全部基於註解生成Advisor
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
//若是當時生成的Advisor是由工廠生成的,這個時候從工廠獲取
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
複製代碼
這個方法有兩部分,第一部分省略了,內容是上一篇分析過的生成基於註解的Advisor
的過程,第二部分是獲取已經生成過的Advisor
的過程,因爲基於註解生成的Advisor已經在建立的同時完成了bean的初始化,因此這裏直接從緩存中獲取便可,若是Advisor
是由工廠類生成,則此時須要經過工廠類獲取Advisor
。
上面分析過程已經從xml和註解的方式獲取到了容器中的全部Advisor
,接下來會執行第二步操做,校驗容器中的Advisor
是否可以做用與要建立的bean上,回頭看核心方法2:findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
for (Advisor candidate : candidateAdvisors) {
//核心點1:經過Introduction實現的advice
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
//核心點2:匹配可以做用於clazz的Advice
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
複製代碼
這個方法的核心點1作了一次匹配,這個匹配是經過IntroductionAdvisor
實現的,首先簡單瞭解下這個問題的背景:
若是須要對一些組件新增一些功能,好比在付款接口完成後,須要記錄這筆交易耗時時間,可是此時根據業務不一樣,可能會有不少個付款實現類,這個時候的作法就是新增一個統計耗時的接口,這樣作雖然能夠解決問題,可是污染了業務代碼,並且每一個類中都要實現一遍,這就違反了面向對象的開放封閉原則,這個問題經過裝飾者模式解決,Spring爲了可以完成這個功能,使用Introduction+advice
實現的,Spring經過一個特殊的攔截器IntroductionInterceptor
實現。與PointCut
做用於接口層次上不一樣,這種方式做用於類的層次,可是很不建議在生產環境使用這種粗粒度的實現,只須要知道Spring中有這種實現的方式就能夠了,這裏也不做爲重點分析。
咱們來看核心點2,canApply(candidate, clazz, hasIntroductions)
從方法參數能夠看出參數candidate是一個Advisor,claszz是目標類對象,因爲Advisor
實現類裏面有PointCut
,因此就能夠匹配clazz類的方法是否屬於切入方法,須要被加強。
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
//由於做用於類 直接匹配類就好了。
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
//轉換成成PointCut接口
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
//若是advisor沒有一個pointcut,默認它對全部的bean都是生效的
return true;
}
}
複製代碼
上面的方法首先校驗了advisor
是不是IntroductionAdvisor
,上面分析過,IntroductionAdvisor
是基於類型的,因此這裏直接針對這種bean直接調用了ClassFilter
接口去匹配,而後若是advisor
是一個PointcutAdvisor
,則轉換成PointcutAdvisor
再調用canApply
方法,最後若是Advisor
並無配置成一個PointcutAdvisor
,就默認對全部的bean都是生效的,進入canApply
方法繼續看
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
//前面說過 PointCut接口getMethodMatcher的返回一個方法匹配器
MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
//獲取目標類得全部接口接口
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
//獲取目標類及其接口的全部方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
複製代碼
這個方法其實很明確了,就是經過PointCut
獲取到一個方法匹配器MethodMatcher
,這個匹配器經過match
方法能夠判斷當前的方法是否可以被匹配到,PointCut
的封裝其實就是aspectj的,但咱們關注點並不在這,就不進入裏面分析了, 而後獲取目標類的全部接口,循環全部的接口而後獲取各個接口的全部方法,調用MethodMatcher
的match
方法用來判斷當前方法是否可以被匹配,只要有一個方法被匹配到,則但會true,代表當前的advisor
可以做用於當前bean。
Spring在bean的建立過程當中,就是經過上面的過程查找可以做用於bean的Advisor
的,若是可以找到,就爲bean建立代理,不然就當普通的bean建立,通過上面的判斷,咱們案例中的bean確定是有符合條件的Advisor
,因此接下來看看Spring是如何建立代理的。
再來回憶下這個方法:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//若是bean是經過TargetSource接口獲取 則直接返回
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//若是bean是切面類 直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//若是bean是Aspect 並且容許跳過建立代理, 加入advise緩存 返回
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//若是前面生成的advisor緩存中存在可以匹配到目標類方法的Advisor 則建立代理
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;
}
複製代碼
通過前面的分析,經過getAdvicesAndAdvisorsForBean
方法能夠獲取到容器中全部能被做用於當前bean的Advisor
,此時的specificInterceptors
確定是不爲null,因此就會執行createProxy
建立代理的方法了。
protected Object createProxy( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
//若是是基於IntroductionAdvisor,直接指定代理的目標類便可
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
//建立代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
//校驗當前代理工廠是否能夠直接代理目標類和目標接口
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
//將beanClass的接口設置到代理工廠上
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//構建可以做用於beanClass類的Advisor集合
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//建立代理對象
return proxyFactory.getProxy(getProxyClassLoader());
}
複製代碼
因爲IntroductionAdvisor
這樣特殊的攔截器已經指定了加強的類對象,也就是說該類實現了IntroductionInterceptor
攔截器,定義過了類的方法須要如何加強,因此上面的方法第一步就是給他指定了加強類,而後建立ProxyFactory
代理工廠,先校驗代理工廠是否可以直接代理目標類及其接口,若是不能則evaluateProxyInterfaces
方法會將beanClass
的接口設置到代理工廠上,接下來經過buildAdvisors
方法構建一個可以做用於beanClass
類的Advisor
集合放入到代理工廠中,以便後續建立代理時使用,最後調用proxyFactory.getProxy
方法一個代理對象,在這裏buildAdvisors
方法沒可看的,就是將Advisor
封裝到一個集合裏等待備用:
protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
Advisor[] commonInterceptors = resolveInterceptorNames();
List<Object> allInterceptors = new ArrayList<Object>();
if (specificInterceptors != null) {
//將全部Advisor放入到allInterceptors集合中
allInterceptors.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.isDebugEnabled()) {
int nrOfCommonInterceptors = commonInterceptors.length;
int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
Advisor[] advisors = new Advisor[allInterceptors.size()];
for (int i = 0; i < allInterceptors.size(); i++) {
//根據Advice類型轉換成不用的Advisor實現
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
return advisors;
}
複製代碼
下面看看Spring時如何建立代理的
public Object getProxy(ClassLoader classLoader) {
//createAopProxy方法建立一個代理對象
return createAopProxy().getProxy(classLoader);
}
複製代碼
createAopProxy
方法建立一個代理對象,這個方法由DefaultAopProxyFactory
類實現:
@Override
public 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)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
複製代碼
這個方法決定了是使用jdk代理仍是cglib代理,上面的判斷由三個校驗組成,只要有任何一個結果爲true都會進入else使用jdk代理,來看下這個三個判斷條件:
//自定義指定配置
config.isOptimize()
//是否使用了proxy-target-class="true"
config.isProxyTargetClass()
//目標類是否實現了接口 並且接口不能與SpringProxy類相同
hasNoUserSuppliedProxyInterfaces(config))
複製代碼
這三個方法中的config.isOptimize()
,config.isProxyTargetClass()
默認都會返回false,針對最後一個方法是判斷目標類是否沒有接口,當目標類有接口實現的時候就會走到最後一個else,使用JdkDynamicAopProxy
建立代理,也就是jdk代理。
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
//若是目標類是實現了接口 會直接返回false
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
複製代碼
而後再來看config.isOptimize()
,這個屬性的初始值是false,我並無在源碼中發現有哪裏能夠修改這個值,可是若是咱們自定義一個實現了AbstractAdvisorAutoProxyCreator
類的處理器,像下面這樣配置,就能夠設置這個值爲true了。這個應該是基於Sping的擴展的,但這裏對普通註解bean都是false的,因此不須要太糾結。
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="optimize" value="true"/>
</bean>
複製代碼
而後再看一個比較關鍵的判斷config.isProxyTargetClass()
,這個配置實際上就是aop基於自動註解配置裏面的一個屬性
<aop:aspectj-autoproxy proxy-target-class="true">
複製代碼
這個值就是proxy-target-class
的值,這個值的注入是註冊AnnotationAwareAspectJAutoProxyCreator
時候設置的,來看下面的代碼,這部分是註冊AnnotationAwareAspectJAutoProxyCreator
時候,設置proxyTargetClass
屬性的
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if (sourceElement != null) {
// PROXY_TARGET_CLASS_ATTRIBUTE就是proxy-target-class屬性
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
//主要執行這個方法
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
複製代碼
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//若是proxy-target-class屬性配置的是true 直接設置這個值的類型爲true
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
複製代碼
因此說若是咱們經過<aop:aspectj-autoproxy proxy-target-class="true">
方式若是配置了proxy-target-class
爲true,則就會直接使用cglib代理了,固然下面還會有一些校驗,不過最終仍是會走到cglib代理,此次先來看看cglib代理吧
cglib代理就是由ObjenesisCglibAopProxy
這個類實現的,獲取代理對象直接調用其父類CglibAopProxy的getProxy
方法獲取。
@Override
public Object getProxy(ClassLoader classLoader) {
try {
Class<?> rootClass = this.advised.getTargetClass();
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
//校驗類
validateClassIfNecessary(proxySuperClass, classLoader);
//開始建立cglib代理
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
//核心點1:獲取回調集合
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
//核心點2:設置過濾器
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 建立代理對象
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (Exception ex) {
//異常捕獲拋出
.........
}
}
複製代碼
cglib代理的使用方式和咱們以前作的簡單案例的使用是同樣的:
首先建立一個Enhancer,setSuperclass設置要代理的對象,
setInterfaces能夠不設置,由於是基於cglib代理的,不須要接口實現,這裏的含義是指定建立的代理繼承AopProxy的一些接口。
setNamingPolicy設置cglib的命名策略,這裏是以BySpringCGLIB爲前綴:
***
getCallbacks
***方法用於獲取加強器,會詳細解析。
***
enhancer.setCallbackFilter()
***用於過濾器用於過濾不須要加強的方法的,也會詳細解析
最後經過***
createProxyClassAndInstance
***建立一個代理實例對象。
從上面單獨對cglib單例的使用講解能夠知道,cglib代理是基於asm在字節碼上的實現,能夠動態的建立一個繼承於目標類的代理對象,而後執行加強操做,此外cglib還提供了Callback
接口用於執行對方法的攔截,由MethodInterceptor
接口的intercept
方法攔截,而後cglib還能夠經過CallbackFilter
類來指定對不一樣的類或者方法執行不一樣的加強操做,accept
方法會返回一個索引,這個索引是設置的CallbackFilter
集合中集合的索引,經過accpet
方法若是返回0就會使用Callback
集合中第一個加強攔截器,下面會針對Spring設置的Callback
集合和CallbackFilter
分別分析。
首先來看下Spring是如何獲取CallbackFilter
集合。
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimisation choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
Callback targetInterceptor;
if (exposeProxy) {
//這個方法是表示處理嵌套的,也就是在代理方法中調用本次的其餘方法 那麼針對該方法的加強效果將會失效
........
}
else {
targetInterceptor = isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
}
Callback targetDispatcher = isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
//這個事核心點,從這裏能夠看出Spring裏面總共設置了6個方法加強器
//其中第一個就是咱們咱們配置的加強器
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimized
new SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
if (isStatic && isFrozen) {
.........
}
else {
callbacks = mainCallbacks;
}
return callbacks;
}
複製代碼
首先Spring將建立的的ProxyFactroy
對象封裝到DynamicAdvisedInterceptor
,這個代理類裏面有以前匹配到的Advisor
集合,而後判斷exposeProxy
是不是true,這個屬性也是在xml配置文件中配置的,背景就是若是在事務A中使用了代理,事務A調用了目標類的的方法a,在方法a中又調用目標類的方法b,方法a,b同時都是要被加強的方法,若是不配置exposeProxy
屬性,方法b的加強將會失效,若是配置exposeProxy
,方法b在方法a的執行中也會被加強了。接下來再看mainCallbacks
這個集合,這個集合裏面封裝了6個是實現了Callback
或MethodInterceptor
接口的類,其中第一個aopInterceptor
就是封裝了目標類的要加強的邏輯也就是Advisor集合,第二個是針對以前配置的optimize
屬性使用的,後面的四個基本上不會作太多業務邏輯上的攔截,Spring最終將這6個加強器集合返回做爲cglib的攔截器鏈,以後經過CallbackFilter
的accpet
方法返回的索引從這個集合中返回對應的攔截加強器執行加強操做。
Spring實現的是經過ProxyCallbackFilter
實現CallbackFilter
接口,而後賦值給代理對象,上面分析過,這個接口主要是經過accpet
接口實現的,來看看是如何匹配的:
@Override
public int accept(Method method) {
if (AopUtils.isFinalizeMethod(method)) {
logger.debug("Found finalize() method - using NO_OVERRIDE");
//不使用代理
return NO_OVERRIDE;
}
if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
if (logger.isDebugEnabled()) {
logger.debug("Method is declared on Advised interface: " + method);
}
return DISPATCH_ADVISED;
}
// We must always proxy equals, to direct calls to this.
if (AopUtils.isEqualsMethod(method)) {
logger.debug("Found 'equals' method: " + method);
return INVOKE_EQUALS;
}
// We must always calculate hashCode based on the proxy.
if (AopUtils.isHashCodeMethod(method)) {
logger.debug("Found 'hashCode' method: " + method);
return INVOKE_HASHCODE;
}
Class<?> targetClass = this.advised.getTargetClass();
// Proxy is not yet available, but that shouldn't matter.
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
if (haveAdvice || !isFrozen) {
// If exposing the proxy, then AOP_PROXY must be used.
if (exposeProxy) {
if (logger.isDebugEnabled()) {
logger.debug("Must expose proxy on advised method: " + method);
}
//使用封裝了Advisor的攔截加強器
return AOP_PROXY;
}
String key = method.toString();
// Check to see if we have fixed interceptor to serve this method.
// Else use the AOP_PROXY.
if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
// We know that we are optimising so we can use the FixedStaticChainInterceptors.
int index = this.fixedInterceptorMap.get(key);
return (index + this.fixedInterceptorOffset);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Unable to apply any optimisations to advised method: " + method);
}
//使用封裝了Advisor的攔截加強器
return AOP_PROXY;
}
}
else {
if (exposeProxy || !isStatic) {
return INVOKE_TARGET;
}
Class<?> returnType = method.getReturnType();
if (targetClass == returnType) {
return INVOKE_TARGET;
}
else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
return DISPATCH_TARGET;
}
else {
return INVOKE_TARGET;
}
}
}
複製代碼
這個方法裏面,咱們只須要關心AOP_PROXY
這個值就能夠了,這個值是0
,也就是對應上面生成的Callback
集合中的第一個攔截加強器,也就是aopInterceptor
,這個攔截器裏面封裝了全部可以做用於目標類的Advisor
加強類,因此就會調用到切面類定義的切面方法來進行加強操做了。
上面分析過,Spring使用的是aopInterceptor
做爲攔截加強器,這個加強器被封裝進了DynamicAdvisedInterceptor
類中,這個類實現了MethodInterceptor
方法,因此被攔截到的方法會進入intercept
方法中,因爲這個類裏面封裝了前面咱們匹配到的全部可以做用於這個類的Advisor
集合,可是可能針對這些Advisor
可能執行順序有些疑問,雖然Spring容許經過繼承Order
接口實現排序,可是好比@Before
,@After
,@Round
等是須要在某個方法的不一樣時機執行的,因此這須要構造一個調用鏈,廢話很少,仍是先來看aopInterceptor的intercept方法是如何作的:
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
try {
if()
{
//省略不重要代碼
........
}
else {
//建立一個方法執行器 主要邏輯在這裏
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
複製代碼
在這個方法裏面,咱們只須要關心CglibMethodInvocation
的proceed
方法,調用鏈就是在這裏構建的
@Override
public Object proceed() throws Throwable {
//首先獲取到全部的Advisor 而後去除一個下標-1
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//獲取當前Advisor
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//若是是動態方法匹配器
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
//匹配失敗直接跳過,再次執行proceed方法
return proceed();
}
}
else {
//調用攔截器加強集合
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
複製代碼
這個方法是構造調用鏈的實現原理,首先獲取到Advisor集合,而後用currentInterceptorIndex
表示當前集合剩餘未執行的Advisor
,currentInterceptorIndex=0
的時候,集合中全部的Advisor
已經執行完畢了,這種方法仍是很巧妙的,咱們從總體上看下是如何造成調用鏈的,該方法最終調用最後一行代碼,也就是MethodInterceptor
類的invoke
方法,把當前對象做爲參數傳進去,當前對象也就是aopInterceptor
,這裏面封裝了全部符合條件的Advisor
(每次都要強調這個類裏面的數據就是讓咱們始終能保持在主線上),該類實現了ProxyMethodInvocation
接口間接實現了MethodInvocation
接口,最後一行的代碼調用invoke
方法,也就是MethodInterceptor
接口的方法,來看下這個方法的定義:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
複製代碼
invoke
的參數是MethodInvocation
,因爲aopInterceptor
是MethodInvocation
的實現類,因此能夠將該類做爲參數往下傳遞,前面分析過了切面的註解方法註解對應的類爲:
@Before | MethodBeforeAdviceInterceptor |
---|---|
@After | AspectJAfterReturningAdvice |
@Around | AspectJAroundAdvice |
@AfterThrowing | AspectJAfterThrowingAdvice |
這些生成的Advice
切面類毫無疑問都實現了MethodInterceptor
方法,而MethodInterceptor
的invoke
方法須要接收一個類型爲MethodInvocation
的對象,正好aopInterceptor
實現了MethodInvocation,因此能夠做爲參數在多個Advisor間流轉,每執行完一個
Advisor就進入
aopInterceptor的
process方法再次校驗,直至執行完全部的
Advisor`。
假設此時的aopInterceptor
裏面包含了兩個切面方法MethodBeforeAdviceInterceptor
和AspectJAfterReturningAdvice
,此時先取出AspectJAfterReturningAdvice
,而後執行invoke
方法,看下AspectJAfterReturningAdvice
的invoke
方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
//此時的mi對象就是 aopInterceptor ,又回到了proceed方法
return mi.proceed();
}
finally {
//自身的方法最後執行
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
複製代碼
AspectJAfterReturningAdvice
的invoke
方法會首先調用參數 mi 的proceed
方法,此時mi就是aopInterceptor
對象(這裏面封裝了全部適用於目標類對象的切面方法類),因此會再次返回到攔截器集合裏,取出MethodBeforeAdviceInterceptor
對象,調用它的invoke
方法,最後自調用AspectJAfterReturningAdvice
封裝的切面方法也就是經過@After
註解修飾的方法。再來看看AspectJAfterReturningAdvice
的invoke方法
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
複製代碼
這裏會先調用經過@Before
註解修飾的方法,而後再次調用aopInterceptor
攔截器鏈,雖然這兩個註解是最簡單的,可是確時邏輯最清晰的,也是最容易理解的,其餘的切面註解方法的調用過程也是這樣,最終完成這個攔截器鏈的調用。
通過上面的步驟,就能夠建立一個代理對象了, 而後Spring將代理對象放入緩存中,咱們使用時獲得的是一個代理對象,調用目標方法,就會執行到加強器方法,進而找到全部的Advisor
,構造一個調用鏈,這樣就完成業務的加強了。我相信若是對照着源碼看,多調試幾遍代碼,應該會大體瞭解Spring實現Cglib的代理的一個流程。關於Spring時如何實現cglib代理的暫時就分析到這裏。
jdk模式的代理是經過JdkDynamicAopProxy這個類實現的,因爲jdk的代理的實現比cglib代理稍微簡單一點,我這裏暫時就先不分析了,關於jdk代理模式的解析估計有不少資料,之後有時間我補上這部份內容
Spring源碼很複雜,若是想把每一個細節都搞明白,幾乎是不可能的,並且無疑會浪費不少時間,咱們只須要搞懂經常使用的功能的實現方式,遇到問題能夠從源碼的角度解決問題就好了,說的更高一點,若是咱們能學到做者的一些架構思路以及對代碼的功能抽象,之後能夠對Spring進行擴展,那就再好不過了,廢話很少了,總結下一些這部份內容究竟在說些什麼吧。
這一部份內容是Spring AOP的第二部份內容,是基於上一步解析@Aspect
註解,將注入@Before
等註解方法生成Advice
切面方法類的過程,這一部分則是使用這些切面方法類,而後找出可以做用於目標類的Advisor
,而後建立代理對象,具體流程是:
一、從Advisor
緩存中獲取第一步生成的全部Advisor
(包括經過xml配置和註解配置的)
二、遍歷Advisor
集合,從Advisor
裏面獲取PointCut
,獲取方法匹配器MethodMatcher
,經過match方法匹配一個當前Advisor
是否可以做用於目標類,若是能,則放入集合中
三、若是第二部獲取的集合爲空,則直接返回,不須要建立代理,若是不爲空,也就是Advisor
緩存中有可以做用於目標類的Advisor
,則根據配置選擇不一樣的代理的方法(cglib 仍是 jdk 代理)
四、假如是cglib
代理,須要構建Callback
攔截加強器集合,而後建立一個CallbackFilter
過濾器,選擇合適的攔截加強器對bean實現加強處理
五、返回建立的代理對象。
寫完這一篇,我終於把這兩個月看Spring的源碼對其的理解記錄下來了, 即使如此,我感受我對Spring仍是知之甚少,還須要鞏固,我認爲若是能經過Spring封裝本身寫的框架,就像Sping-redis
,Spring-kafka
那樣直接經過註解或者配置就能夠拿來使用了,確定對Spring有一個更高層次的認識,爲了加深對Spring的理解,經過劉欣老師的講解,我也跟着寫了一遍稍微簡單的Spring實現,那段代碼花了我將近一個月的時間,可是寫完再看Spring的源碼就會容易理解的多,源碼地址是:
感興趣的能夠拉下來看看。