菜瓜:你以爲AOP是啥html
水稻:我以爲吧,AOP是對OOP的補充。一般狀況下,OOP代碼專一功能的實現,所謂面向切面編程,大多數時候是對某一類對象的方法或者功能進行加強或者抽象java
菜瓜:我看你這個理解就挺抽象的web
水稻:舉個栗子🌰!我要在知足開閉原則的基礎下對已有功能進行擴展spring
菜瓜:你說的這個low一點的方法怎麼好像是在說我???編程
水稻:建議看一下動態代理設計模式【DP-動態代理】JDK&Cglib,我固然知道你不會看,因此我還準備了自定義註解的栗子設計模式
package com.hb.merchant.config.aop; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * @author QuCheng on 2020/6/23. */ @Configuration @EnableAspectJAutoProxy public class AopConfig { } package com.hb.merchant.config.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author QuCheng on 2020/6/23. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface OperatorLog { } package com.hb.merchant.config.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; /** * * @author QuCheng on 2020/6/23. */ @Aspect @Component @Slf4j public class OperatorAspect { @Around("@annotation(OperatorLog)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { //獲取要執行的方法 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); //記錄方法執行前日誌 log.info("startLog: {} 開始了。。。" , methodSignature.getName()); //獲取方法信息 String[] argNames = methodSignature.getParameterNames(); // 參數值: final Object[] argValues = joinPoint.getArgs(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < argNames.length; i++) { String value = argValues[i] == null ? "null" : argValues[i].toString(); sb.append(argNames[i]).append("=").append(value).append(","); } String paramStr = sb.length() > 0 ? sb.toString().substring(0, sb.length() - 1) + "]" : ""; log.info("參數信息爲:[{}", paramStr); //執行方法 Object result; try { result = joinPoint.proceed(); } catch (Exception e) { log.error("errorLog", e); return null; } //記錄方法執行後日志 log.info("endLog: {} 結束了。。。" , methodSignature.getName()); return result; } } package com.hb.merchant.controller.icbc.item.oc; import com.hb.merchant.config.aop.OperatorLog; import lombok.extern.slf4j.Slf4j; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author QuCheng on 2020-06-23. */ @RestController @RequestMapping("/item") @Slf4j public class ItemOcController { @OperatorLog @GetMapping("/delete") public String delete(Long itemId) { Assert.notNull(itemId,"itemId不能爲空"); return "delete finished ..."; } }
// 後臺打印
startLog: delete 開始了。。。
參數信息爲:[itemId=1]
endLog: delete 結束了。。。
菜瓜:這個自定義註解又是怎麼實現的呢?app
水稻:不愧是你,沒有源碼看來是知足不了你的好奇心了!!不知道你是否還記得咱們以前有聊到過bean建立完畢後會調用一些PostProcessor對其進一步操做ide
菜瓜:有印象,@PostConstruct註解就是InitDestroyAnnotationBeanPostProcessor在這裏調用的,還自定義過BeanPostProcessorT對象打印輸出過bean信息post
水稻:你猜Spring是怎麼操做的ui
菜瓜:let me try try。結合剛剛的栗子和提示,大膽猜想應該是用PostProcessor在bean建立完成以後生成代理對象。實際調用代理的invoke方法實現對被代理bean的加強
水稻:思路正確。看脈絡
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { 。。。 // BeanNameAware BeanFactoryAware ... invokeAwareMethods(beanName, bean); 。。。 // BeanPostProcessorBefore @PostConstruct wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); 。。。 // initMethod InitializingBean接口 invokeInitMethods(beanName, wrappedBean, mbd); 。。。 if (mbd == null || !mbd.isSynthetic()) { // aop wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { 。。。 // 收集切面信息匹配被代理對象 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; }
@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)) { // jdk動態代理類 return new JdkDynamicAopProxy(config); } // cglib return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
菜瓜:而後代理類中都有invoke方法,那些advice(@Around,@Before...)在invoke中找到適當時機調用對吧
水稻:是的,這裏我想結合@Transactional註解會更容易理解,你確定用過這個註解吧,它其實。。。
菜瓜:停。。。今天獲取的知識量已經夠了,我下去本身斷點走一趟再熟悉熟悉。下次請結合Transactional註解再敲打我吧
水稻:也好,我下去再給你準備幾個栗子🌰
總結: