做者:小傅哥
博客:https://bugstack.cnhtml
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
爲何,你的代碼老是糊到豬圈上?
java
🎙怎麼辦,知道你在互聯網,不知道你在哪一個大廠。知道你在加班,不知道你在和哪一個產品爭辯。知道你在偷懶,不知道你要摸魚到幾點。知道你在搬磚,不知道你在蓋哪一個豬圈。spring
當你特別辛苦夜以繼日的完成着,天天、每週、每個月重複性的工做時,你能得到的成長是最小,獲得的回報也是少的。留着最多的汗、拿着最少的錢數據庫
可能你一激動開始看源碼,但不知道看完的源碼能用到什麼地方。看設計模式,看的時候懂,但改本身的代碼又下不去手。其實一方面是自己技術棧的知識面不足,另一方面是本身儲備的代碼也不夠。最終也就致使根本無法把一些列的知識串聯起來,就像你看了 HashMap,但也聯想不到分庫分表組件中的數據散列也會用到了 HashMap 中的擾動函數思想和泊松分佈驗證
、看了Spring 源碼,也讀不出來 Mybatis 是如何解決只定義 Dao 接口就能使用配置或者註解對數據庫進行 CRUD 操做
、看來 JDK 的動態代理,也想不到 AOP 是如何設計的
。因此成體系學習,增強技術棧知識的完整性,才能更好的用上這些學習到的編碼能力。express
到本章節咱們將要從 IOC 的實現,轉入到關於 AOP(Aspect Oriented Programming
) 內容的開發。在軟件行業,AOP 意爲:面向切面編程,經過預編譯的方式和運行期間動態代理實現程序功能功能的統一維護。其實 AOP 也是 OOP 的延續,在 Spring 框架中是一個很是重要的內容,使用 AOP 能夠對業務邏輯的各個部分進行隔離,從而使各模塊間的業務邏輯耦合度下降,提升代碼的可複用性,同時也能提升開發效率。編程
關於 AOP 的核心技術實現主要是動態代理的使用,就像你能夠給一個接口的實現類,使用代理的方式替換掉這個實現類,使用代理類來處理你須要的邏輯。好比:segmentfault
@Test public void test_proxy_class() { IUserService userService = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserService.class}, (proxy, method, args) -> "你被代理了!"); String result = userService.queryUserInfo(); System.out.println("測試結果:" + result); }
代理類的實現基本都你們都見過,那麼有了一個基本的思路後,接下來就須要考慮下怎麼給方法作代理呢,而不是代理類。另外怎麼去代理全部符合某些規則的全部類中方法呢。若是能夠代理掉全部類的方法,就能夠作一個方法攔截器,給全部被代理的方法添加上一些自定義處理,好比打印日誌、記錄耗時、監控異常等。設計模式
在把 AOP 整個切面設計融合到 Spring 前,咱們須要解決兩個問題,包括:如何給符合規則的方法作代理
,以及作完代理方法的案例後,把類的職責拆分出來
。而這兩個功能點的實現,都是以切面的思想進行設計和開發。若是不是很清楚 AOP 是啥,你能夠把切面理解爲用刀切韭菜,一根一根切老是有點慢,那麼用手(代理
)把韭菜捏成一把,用菜刀或者斧頭這樣不一樣的攔截操做來處理。而程序中其實也是同樣,只不過韭菜變成了方法,菜刀變成了攔截方法。總體設計結構以下圖:架構
MethodInterceptor#invoke
,而不是直接使用 invoke 方法中的入參 Method method 進行 method.invoke(targetObj, args)
這塊是整個使用時的差別。org.aspectj.weaver.tools.PointcutParser
處理攔截表達式 "execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))"
,有了方法代理和處理攔截,咱們就能夠完成設計出一個 AOP 的雛形了。![spring-12-02](https://bugstack.cn/assets/images/spring/spring-12-02.png)![spring-12-02](https://bugstack.cn/assets/images/spring/spring-12-02.png)small-spring-step-11 └── src ├── main │ └── java │ └── cn.bugstack.springframework │ ├── aop │ │ ├── aspectj │ │ │ └── AspectJExpressionPointcut.java │ │ ├── framework │ │ │ ├── AopProxy.java │ │ │ ├── Cglib2AopProxy.java │ │ │ ├── JdkDynamicAopProxy.java │ │ │ └── ReflectiveMethodInvocation.java │ │ ├── AdvisedSupport.java │ │ ├── ClassFilter.java │ │ ├── MethodMatcher.java │ │ ├── Pointcut.java │ │ └── TargetSource.java │ ├── beans │ │ ├── factory │ │ │ ├── config │ │ │ │ ├── AutowireCapableBeanFactory.java │ │ │ │ ├── BeanDefinition.java │ │ │ │ ├── BeanFactoryPostProcessor.java │ │ │ │ ├── BeanPostProcessor.java │ │ │ │ ├── BeanReference.java │ │ │ │ ├── ConfigurableBeanFactory.java │ │ │ │ └── SingletonBeanRegistry.java │ │ │ ├── support │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java │ │ │ │ ├── AbstractBeanDefinitionReader.java │ │ │ │ ├── AbstractBeanFactory.java │ │ │ │ ├── BeanDefinitionReader.java │ │ │ │ ├── BeanDefinitionRegistry.java │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java │ │ │ │ ├── DefaultListableBeanFactory.java │ │ │ │ ├── DefaultSingletonBeanRegistry.java │ │ │ │ ├── DisposableBeanAdapter.java │ │ │ │ ├── FactoryBeanRegistrySupport.java │ │ │ │ ├── InstantiationStrategy.java │ │ │ │ └── SimpleInstantiationStrategy.java │ │ │ ├── support │ │ │ │ └── XmlBeanDefinitionReader.java │ │ │ ├── Aware.java │ │ │ ├── BeanClassLoaderAware.java │ │ │ ├── BeanFactory.java │ │ │ ├── BeanFactoryAware.java │ │ │ ├── BeanNameAware.java │ │ │ ├── ConfigurableListableBeanFactory.java │ │ │ ├── DisposableBean.java │ │ │ ├── FactoryBean.java │ │ │ ├── HierarchicalBeanFactory.java │ │ │ ├── InitializingBean.java │ │ │ └── ListableBeanFactory.java │ │ ├── BeansException.java │ │ ├── PropertyValue.java │ │ └── PropertyValues.java │ ├── context │ │ ├── event │ │ │ ├── AbstractApplicationEventMulticaster.java │ │ │ ├── ApplicationContextEvent.java │ │ │ ├── ApplicationEventMulticaster.java │ │ │ ├── ContextClosedEvent.java │ │ │ ├── ContextRefreshedEvent.java │ │ │ └── SimpleApplicationEventMulticaster.java │ │ ├── support │ │ │ ├── AbstractApplicationContext.java │ │ │ ├── AbstractRefreshableApplicationContext.java │ │ │ ├── AbstractXmlApplicationContext.java │ │ │ ├── ApplicationContextAwareProcessor.java │ │ │ └── ClassPathXmlApplicationContext.java │ │ ├── ApplicationContext.java │ │ ├── ApplicationContextAware.java │ │ ├── ApplicationEvent.java │ │ ├── ApplicationEventPublisher.java │ │ ├── ApplicationListener.java │ │ └── ConfigurableApplicationContext.java │ ├── core.io │ │ ├── ClassPathResource.java │ │ ├── DefaultResourceLoader.java │ │ ├── FileSystemResource.java │ │ ├── Resource.java │ │ ├── ResourceLoader.java │ │ └── UrlResource.java │ └── utils │ └── ClassUtils.java └── test └── java └── cn.bugstack.springframework.test ├── bean │ ├── IUserService.java │ ├── UserService.java │ └── UserServiceInterceptor.java └── ApiTest.java
工程源碼:公衆號「bugstack蟲洞棧」,回覆:Spring 專欄,獲取完整源碼
app
AOP 切點表達式和使用以及基於 JDK 和 CGLIB 的動態代理類關係,如圖 12-2
在實現 AOP 的核心功能以前,咱們先作一個代理方法的案例,經過這樣一個能夠歸納代理方法的核心全貌,可讓你們更好的理解後續拆解各個方法,設計成解耦功能的 AOP 實現過程。
單元測試
@Test public void test_proxy_method() { // 目標對象(能夠替換成任何的目標對象) Object targetObj = new UserService(); // AOP 代理 IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), new InvocationHandler() { // 方法匹配器 MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))"); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (methodMatcher.matches(method, targetObj.getClass())) { // 方法攔截器 MethodInterceptor methodInterceptor = invocation -> { long start = System.currentTimeMillis(); try { return invocation.proceed(); } finally { System.out.println("監控 - Begin By AOP"); System.out.println("方法名稱:" + invocation.getMethod().getName()); System.out.println("方法耗時:" + (System.currentTimeMillis() - start) + "ms"); System.out.println("監控 - End\r\n"); } }; // 反射調用 return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args)); } return method.invoke(targetObj, args); } }); String result = proxy.queryUserInfo(); System.out.println("測試結果:" + result); }
測試結果
監控 - Begin By AOP 方法名稱:queryUserInfo 方法耗時:86ms 監控 - End 測試結果:小傅哥,100001,深圳 Process finished with exit code 0
拆解案例
ReflectiveMethodInvocation
的使用,它目前已是實現 MethodInvocation
接口的一個包裝後的類,參數信息包括:調用的對象、調用的方法、調用的入參。定義接口
cn.bugstack.springframework.aop.Pointcut
public interface Pointcut { /** * Return the ClassFilter for this pointcut. * @return the ClassFilter (never <code>null</code>) */ ClassFilter getClassFilter(); /** * Return the MethodMatcher for this pointcut. * @return the MethodMatcher (never <code>null</code>) */ MethodMatcher getMethodMatcher(); }
cn.bugstack.springframework.aop.ClassFilter
public interface ClassFilter { /** * Should the pointcut apply to the given interface or target class? * @param clazz the candidate target class * @return whether the advice should apply to the given target class */ boolean matches(Class<?> clazz); }
cn.bugstack.springframework.aop.MethodMatcher
public interface MethodMatcher { /** * Perform static checking whether the given method matches. If this * @return whether or not this method matches statically */ boolean matches(Method method, Class<?> targetClass); }
methodMatcher.matches(method, targetObj.getClass())
實現切點表達式類
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher { private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>(); static { SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); } private final PointcutExpression pointcutExpression; public AspectJExpressionPointcut(String expression) { PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader()); pointcutExpression = pointcutParser.parsePointcutExpression(expression); } @Override public boolean matches(Class<?> clazz) { return pointcutExpression.couldMatchJoinPointsInType(clazz); } @Override public boolean matches(Method method, Class<?> targetClass) { return pointcutExpression.matchesMethodExecution(method).alwaysMatches(); } @Override public ClassFilter getClassFilter() { return this; } @Override public MethodMatcher getMethodMatcher() { return this; } }
pointcutExpression.couldMatchJoinPointsInType(clazz)
、pointcutExpression.matchesMethodExecution(method).alwaysMatches()
,這部份內容能夠單獨測試驗證。匹配驗證
@Test public void test_aop() throws NoSuchMethodException { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.UserService.*(..))"); Class<UserService> clazz = UserService.class; Method method = clazz.getDeclaredMethod("queryUserInfo"); System.out.println(pointcut.matches(clazz)); System.out.println(pointcut.matches(method, clazz)); // true、true }
cn.bugstack.springframework.aop.AdvisedSupport
public class AdvisedSupport { // 被代理的目標對象 private TargetSource targetSource; // 方法攔截器 private MethodInterceptor methodInterceptor; // 方法匹配器(檢查目標方法是否符合通知條件) private MethodMatcher methodMatcher; // ...get/set }
定義接口
cn.bugstack.springframework.aop.framework
public interface AopProxy { Object getProxy(); }
cn.bugstack.springframework.aop.framework.JdkDynamicAopProxy
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { private final AdvisedSupport advised; public JdkDynamicAopProxy(AdvisedSupport advised) { this.advised = advised; } @Override public Object getProxy() { return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); } return method.invoke(advised.getTargetSource().getTarget(), args); } }
cn.bugstack.springframework.aop.framework.Cglib2AopProxy
public class Cglib2AopProxy implements AopProxy { private final AdvisedSupport advised; public Cglib2AopProxy(AdvisedSupport advised) { this.advised = advised; } @Override public Object getProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass()); enhancer.setInterfaces(advised.getTargetSource().getTargetClass()); enhancer.setCallback(new DynamicAdvisedInterceptor(advised)); return enhancer.create(); } private static class DynamicAdvisedInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy); if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { return advised.getMethodInterceptor().invoke(methodInvocation); } return methodInvocation.proceed(); } } private static class CglibMethodInvocation extends ReflectiveMethodInvocation { @Override public Object proceed() throws Throwable { return this.methodProxy.invoke(this.target, this.arguments); } } }
public class UserService implements IUserService { public String queryUserInfo() { try { Thread.sleep(new Random(1).nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } return "小傅哥,100001,深圳"; } public String register(String userName) { try { Thread.sleep(new Random(1).nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } return "註冊用戶:" + userName + " success!"; } }
public class UserServiceInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { long start = System.currentTimeMillis(); try { return invocation.proceed(); } finally { System.out.println("監控 - Begin By AOP"); System.out.println("方法名稱:" + invocation.getMethod()); System.out.println("方法耗時:" + (System.currentTimeMillis() - start) + "ms"); System.out.println("監控 - End\r\n"); } } }
@Test public void test_dynamic() { // 目標對象 IUserService userService = new UserService(); // 組裝代理信息 AdvisedSupport advisedSupport = new AdvisedSupport(); advisedSupport.setTargetSource(new TargetSource(userService)); advisedSupport.setMethodInterceptor(new UserServiceInterceptor()); advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))")); // 代理對象(JdkDynamicAopProxy) IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy(); // 測試調用 System.out.println("測試結果:" + proxy_jdk.queryUserInfo()); // 代理對象(Cglib2AopProxy) IUserService proxy_cglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy(); // 測試調用 System.out.println("測試結果:" + proxy_cglib.register("花花")); }
測試結果
監控 - Begin By AOP 方法名稱:public abstract java.lang.String cn.bugstack.springframework.test.bean.IUserService.queryUserInfo() 方法耗時:86ms 監控 - End 測試結果:小傅哥,100001,深圳 監控 - Begin By AOP 方法名稱:public java.lang.String cn.bugstack.springframework.test.bean.UserService.register(java.lang.String) 方法耗時:97ms 監控 - End 測試結果:註冊用戶:花花 success! Process finished with exit code 0