JAVA中AOP實現方式java
動態代理git
動態字節碼加強github
java代碼生成正則表達式
自定義類加載器spring
AOL擴展apache
一些單詞的含義:設計模式
Joinpoint 切點緩存
Pointcut 切點表達式:服務器
Advice 切面微信
Aspect是對系統中的橫切關注點邏輯進行模塊化封裝的AOP的概念實體。一般狀況下,Aspect能夠包含多個Pointcut以及相關Advice定義。
package org.springframework.mylearntest.aop.staticproxy; public interface IRequestable { void request(); }
package org.springframework.mylearntest.aop.staticproxy; public class RequestableImpl implements IRequestable{ @Override public void request() { System.out.println(" request process in RequestableImpl"); } }
package org.springframework.mylearntest.aop.staticproxy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServiceControlRequestableProxy implements IRequestable{ private static final Logger logger = LoggerFactory.getLogger(ServiceControlRequestableProxy.class); private IRequestable requestable; public ServiceControlRequestableProxy(IRequestable target) { this.requestable = target; } @Override public void request() { System.out.println("request process in ServiceControlRequestableProxy"); requestable.request(); } public static void main(String[] args) { IRequestable target = new RequestableImpl();// 須要被代理的對象 IRequestable proxy = new ServiceControlRequestableProxy(target); // 以構造方法形式將被代理對象傳入代理者中 proxy.request();// 讓代理者去處理請求 } }
package org.springframework.mylearntest.aop.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class RequestCtrlInvocationHandler implements InvocationHandler { private Object target; public RequestCtrlInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("reflect invoke before target method"); if ("request".equals(method.getName())) { System.out.println("dynamic proxy target method"); return method.invoke(target, args); } return null; } }
package org.springframework.mylearntest.aop.dynamicproxy; import org.springframework.mylearntest.aop.staticproxy.IRequestable; import org.springframework.mylearntest.aop.staticproxy.RequestableImpl; import java.lang.reflect.Proxy; @SuppressWarnings("rawtypes") public class Test4DynamicProxy { public static void main(String[] args) { // arg1:類加載器 arg2:接口信息 arg3:實現InvocationHandler的類 並傳入須要代理的對象 IRequestable requestable = (IRequestable) Proxy.newProxyInstance( Test4DynamicProxy.class.getClassLoader(), new Class[]{IRequestable.class}, new RequestCtrlInvocationHandler(new RequestableImpl())); requestable.request(); } }
若是想深刻了解動態代理,請閱讀《java reflect in action》。
package org.springframework.mylearntest.aop.CGLIBClassGenerate; public class Requestable { public void request(){ System.out.println("req in requestable without implement any interface"); } }
package org.springframework.mylearntest.aop.CGLIBClassGenerate; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class RequestCtrlCallback implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if (method.getName().equals("request")) { System.out.println("proxy generated by cglib intercept method request"); return methodProxy.invokeSuper(o, objects); } return null; } }
package org.springframework.mylearntest.aop.CGLIBClassGenerate; import org.springframework.cglib.proxy.Enhancer; public class Test4CGLIB { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Requestable.class); enhancer.setCallback(new RequestCtrlCallback()); Requestable proxy = (Requestable) enhancer.create(); proxy.request(); } }
若是Pointcut類型爲TruePointcut,默認會對系統中的全部對象,以及對象上全部被支持的Joinpoint進行匹配。
package org.springframework.aop; springframework.aop.support.MethodMatchers public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); Pointcut TRUE = TruePointcut.INSTANCE; }
package org.springframework.aop; import java.io.Serializable; @SuppressWarnings("serial") final class TruePointcut implements Pointcut, Serializable { public static final TruePointcut INSTANCE = new TruePointcut(); private TruePointcut() { } @Override public ClassFilter getClassFilter() { return ClassFilter.TRUE; } @Override public MethodMatcher getMethodMatcher() { return MethodMatcher.TRUE; } private Object readResolve() { return INSTANCE; } @Override public String toString() { return "Pointcut.TRUE"; } }
ClassFilter和MethodMatcher分別用於匹配將被執行織入操做的對象以及相應的方法。之因此將類型匹配和方法匹配分開定義,是由於能夠重用不一樣級別的匹配定義,而且能夠在不一樣級別或者相同級別上進行組合操做,或者強制讓某個子類只覆蓋(Override)相應方法定義等。
package org.springframework.aop; @FunctionalInterface public interface ClassFilter { boolean matches(Class<?> clazz); ClassFilter TRUE = TrueClassFilter.INSTANCE; } ```java package org.springframework.aop; import java.lang.reflect.Method; public interface MethodMatcher { boolean matches(Method method, Class<?> targetClass); boolean isRuntime(); boolean matches(Method method, Class<?> targetClass, Object... args); MethodMatcher TRUE = TrueMethodMatcher.INSTANCE; }
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.setMappedName("matches"); // 或者傳入多個方法名 pointcut.setMappedNames(new String[]{"matches", "isRuntime"}); // 簡單模糊匹配 pointcut.setMappedNames(new String[]{"match*", "matches", "mat*es" });
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut(); pointcut.setPattern(".*match.*"); pointcut.setPatterns(new String[]{".*match.", ".*matches"});
注意正則表達式匹配模式必須匹配整個方法簽名(Method signature)的形式指定,而不能像NameMatchMethodPointcut那樣僅給出匹配的方法名稱。
Perl5RegexpMethodPointcut實現使用jakarta ORO提供正則表達式支持,
package org.springframework.mylearntest.aop.annotationmatchingpointcut; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ClassLevelAnnotation { }
package org.springframework.mylearntest.aop.annotationmatchingpointcut; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MethodLevelAnnotation { }
package org.springframework.mylearntest.aop.annotationmatchingpointcut; @ClassLevelAnnotation public class GenericTargetObject { @MethodLevelAnnotation public void getMethod1() { System.out.println("getMethod1"); } public void getMethod2() { System.out.println("getMethod2"); } }
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class); // 也能夠經過靜態方法 AnnotationMatchingPointcut pointcut1 = AnnotationMatchingPointcut.forClassAnnotation(MethodLevelAnnotation.class); // 同時限定 AnnotationMatchingPointcut pointcut2 = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class);
package org.springframework.mylearntest.aop.pointcut.composablePointcut; import org.junit.Assert; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.support.ComposablePointcut; import org.springframework.aop.support.Pointcuts; public class Test4ComposablePointcut { public static void main(String[] args) { ComposablePointcut pointcut1 = new ComposablePointcut(new ClassFilter() { @Override public boolean matches(Class<?> clazz) { return false; } }, MethodMatcher.TRUE); ComposablePointcut pointcut2 = new ComposablePointcut(new ClassFilter() { @Override public boolean matches(Class<?> clazz) { return false; } }, MethodMatcher.TRUE); // union intersection ComposablePointcut union = pointcut1.union(pointcut2); ComposablePointcut intersection = pointcut1.intersection(union); Assert.assertEquals(pointcut1,intersection); // combine classFilter with methodMatcher pointcut2.union(new ClassFilter() { @Override public boolean matches(Class<?> clazz) { return false; } }).intersection(MethodMatcher.TRUE); // just compute between pointcut, use org.springframework.aop.support.Pointcuts Pointcut pointcut3 = new Pointcut() { @Override public ClassFilter getClassFilter() { return null; } @Override public MethodMatcher getMethodMatcher() { return null; } }; Pointcut pointcut4 = new Pointcut() { @Override public ClassFilter getClassFilter() { return null; } @Override public MethodMatcher getMethodMatcher() { return null; } }; Pointcut union1 = Pointcuts.union(pointcut3, pointcut4); Pointcut intersection1 = Pointcuts.intersection(pointcut3, pointcut4); } }
Spring 中各類Advice 和 Aop Alliance標準接口之間的關係。
per-class的Advice是指,該類型的Advice的實例能夠在目標對象類的全部實例之間共享。這種類型的Advice一般只是提供方法的攔截功能,不會對目標對象類保存任何狀態或者添加新的特性。
package org.springframework.mylearntest.aop.advice; import org.apache.commons.io.FileUtils; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.core.io.Resource; import java.lang.reflect.Method; public class ResourceSetupBeforeAdvice implements MethodBeforeAdvice { private Resource resource; public ResourceSetupBeforeAdvice(Resource resource) { this.resource = resource; } @Override public void before(Method method, Object[] args, Object target) throws Throwable { if (!resource.exists()) { FileUtils.forceMkdir(resource.getFile()); } } }
package org.springframework.mylearntest.aop.advice; import org.omg.CORBA.portable.ApplicationException; import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; public class ExceptionBarrierThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Throwable t) { // 普通異常處理 } public void afterThrowing(RuntimeException t) { // 運行時異常處理 } public void afterThrowing(Method m, Object[] args, Object target, ApplicationException e) { // 處理應用程序生成的異常 } }
AfterReturningAdvice
此Advice能夠訪問到當前Joinpoint的方法返回值、方法、方法參數以及所在的目標對象,可是不能更改返回值,可使用Around Advice來更改返回值。
Around Advice
Spring中沒有定義Around Advice ,而是直接使用AOP Alliance的標準接口,實現 MethodInterceptor便可。
per-instance類型的Advice不會在目標類全部對象實例之間共享,而是會爲不一樣的實例對象保存它們各自的狀態以及相關邏輯。在Spring中Introduction就是惟一的一種per-instance型Advice。
DelegatingIntroductionInterceptor不會本身實現將要添加到目標對象上的新邏輯行爲,而是委派給其餘的實現類。
package org.springframework.mylearntest.aop.advice.perinstance.delegatingIntroductionInterceptor; public class Developer implements IDeveloper{ @Override public void developSoftware() { System.out.println(" do some developing ..."); } }
package org.springframework.mylearntest.aop.advice.perinstance.delegatingIntroductionInterceptor; public interface ITester { boolean isBusyAsTester(); void testSoftware(); }
package org.springframework.mylearntest.aop.advice.perinstance.delegatingIntroductionInterceptor; public class Tester implements ITester{ private boolean busyAsTester; public void setBusyAsTester(boolean busyAsTester) { this.busyAsTester = busyAsTester; } @Override public boolean isBusyAsTester() { return busyAsTester; } @Override public void testSoftware() { System.out.println("do some developing and test ..."); } }
ITester delegator = new Tester(); DelegatingIntroductionInterceptor interceptor = new DelegatingIntroductionInterceptor(delegator); // 進行織入 ITester tester = (ITester)weaver.weave(developer).with(interceptor).getProxy(); tester.testSoftware();
與DelegatingIntroductionInterceptor不一樣,DelegatePerTargetObjectIntroductionInterceptor會在內部持有一個目標對象與相應Introduction邏輯實現類之間的映射關係。當每一個對象上的新定義的接口方法被調用的時候,DelegatePerTargetObjectIntroductionInterceptor會攔截這些調用,而後以目標對象實例做爲鍵,到它持有的那個映射關係中取得對應當前目標對象實例的Introduction實現實例。
DelegatePerTargetObjectIntroductionInterceptor interceptor1 = new DelegatePerTargetObjectIntroductionInterceptor(Tester.class,ITester.class);
package org.springframework.mylearntest.aop.advice.perinstance; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.lang3.StringUtils; import org.springframework.aop.support.DelegatingIntroductionInterceptor; public class TesterFeatureIntroductionInterceptor extends DelegatingIntroductionInterceptor implements ITester { public static final long serialVersionUID = -3387097489523045796L; private boolean busyAsTester; @Override public Object invoke(MethodInvocation mi) throws Throwable { if (isBusyAsTester() && StringUtils.contains(mi.getMethod().getName(), "developSoftware")) { throw new RuntimeException("I'am so tired"); } return super.invoke(mi); } @Override public boolean isBusyAsTester() { return busyAsTester; } public void setBusyAsTester(boolean busyAsTester) { this.busyAsTester = busyAsTester; } @Override public void testSoftware() { System.out.println("I will ensure the quality"); } }
<bean id="pointcut" class="org.springframework.mylearntest.aop.pointcut.selfdefinepointcut.QueryMethodPointcut"/> <bean id="advice" class="org.springframework.mylearntest.aop.advice.perclass.DiscountMethodInterceptor"/> <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="pointcut"/> <property name="advice" ref="advice"/> </bean>
Advice advice = new DiscountMethodInterceptor(); NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(advice); advisor.setMappedName("invoke");
RegexpMethodPointcutAdvisor
只能經過正則表達式爲其設置相應的Pointcut,內部持有一個AbstractRegexpMethodPointcut的實例。AbstractRegexpMethodPointcut有兩個實現類,Perl5RegexpMethodPointcutAdvisor和JdkRegexpMethodPointcut。默認使用JdkRegexpMethodPointcut,若是強制使用Perl5RegexpMethodPointcutAdvisor,那麼能夠經過RegexpMethodPointcutAdvisor的setPerl5(boolean)實現。
DefaultBeanFactoryPointcutAdvisor
DefaultBeanFactoryPointcutAdvisor自身綁定到了BeanFactory,要使用DefaultBeanFactoryPointcutAdvisor,要綁定到Spring IoC容器。經過容器中的Advice註冊的beanName來關聯對應的Advice。只有當對應的Pointcut匹配成功以後,採起實例化對應的Advice,減小了容器啓動初期Advisor和Advice之間的耦合性。
IntroductionAdvisor只能應用於類級別的攔截,只能使用Introduction型的Advice,而不能像PointcutAdvisor那樣,可使用任意類型的Pointcut,以及差很少任何類型的Advice。
<bean id="permissionAuthAdvisor" class="...PermissionAuthAdvisor"> <property name="order" value="1"> ... <bean> <bean id="exceptionBarrierAdvisor" class="...ExceptionBarrierAdvisor"> <property name="order" value="0"> ... <bean>
AspectJ採用ajc編譯器做爲它的織入器;JBoss AOP使用自定義的ClassLoader做爲它的織入器;而在Spring AOP中,使用類org.springframework.aop.framework.ProxyFactory做爲織入器。
ProxyFactory weaver = new ProxyFactory(target);
weaver.addAdvisor(advisor);
Object proxyObject = weaver.getProxy();
package org.springframework.mylearntest.aop.weaver; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.NameMatchMethodPointcutAdvisor; import java.util.Date; /** * @Author: whalefall * @Date: 2020/7/15 22:53 */ @SuppressWarnings({"rawtypes", "Deprecated"}) public class Test4ProxyFactory { public static void main(String[] args) { /*// 1. 傳入須要織入的對象 ProxyFactory weaver = new ProxyFactory(new Tester()); // weaver.setTarget(new Tester()); // 2. 將要應用到目標對象的Advisor綁定到織入器上 ApplicationContext context = new ClassPathXmlApplicationContext("advisor/defaultadvisor/defaultadvisor.xml"); Advisor advisor = (Advisor) context.getBean("advisor"); weaver.addAdvisor(advisor); Object proxyObject = weaver.getProxy(); System.out.println(proxyObject.getClass()); // out: class org.springframework.mylearntest.aop.advice.perinstance.Tester$$EnhancerBySpringCGLIB$$8e739b5b */ // 目標類有實現接口的用法 // 只要不將ProxyFactory的optimize和proxyTargetClass設置爲true // 那麼ProxyFactory都會按照面向接口進行代理 MockTask task = new MockTask(); ProxyFactory weaver = new ProxyFactory(task); // weaver.setInterfaces(new Class[]{ITask.class}); NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(); advisor.setMappedNames("execute"); advisor.setAdvice(new PerformanceMethodInterceptor()); weaver.addAdvisor(advisor); ITask proxyObj = (ITask)weaver.getProxy(); // com.sun.proxy.$Proxy0 // System.out.println(proxyObj.getClass()); // 只能強制轉化爲接口類型,不能轉化爲實現類類型 不然會拋出ClassCastException // ITask proxyObj = (MockTask)weaver.getProxy(); proxyObj.execute(new Date()); // 目標類沒有實現接口的用法 } }
package org.springframework.mylearntest.aop.weaver.baseonclass; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.NameMatchMethodPointcutAdvisor; import org.springframework.mylearntest.aop.advice.perclass.PerformanceMethodInterceptor; /** * @Author: whalefall * @Date: 2020/7/17 23:31 */ public class Test4CGLib { public static void main(String[] args) { ProxyFactory weaver = new ProxyFactory(new Executable()); NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(); advisor.addMethodName("execute"); advisor.setAdvice(new PerformanceMethodInterceptor()); weaver.addAdvisor(advisor); Executable proxyObject = (Executable)weaver.getProxy(); proxyObject.execute(); // org.springframework.mylearntest.aop.weaver.baseonclass.Executable$$EnhancerBySpringCGLIB$$37e40619 System.out.println("proxyObject class: " + proxyObject.getClass()); } }
package org.springframework.mylearntest.aop.weaver.introduction; import org.springframework.aop.framework.ProxyFactory; import org.springframework.mylearntest.aop.advice.perinstance.Developer; import org.springframework.mylearntest.aop.advice.perinstance.IDeveloper; import org.springframework.mylearntest.aop.advice.perinstance.ITester; import org.springframework.mylearntest.aop.advice.perinstance.TesterFeatureIntroductionInterceptor; /** * @Author: whalefall * @Date: 2020/7/19 0:02 */ @SuppressWarnings("rawtypes") public class Test4Introduction { public static void main(String[] args) { ProxyFactory weaver = new ProxyFactory(new Developer()); weaver.setInterfaces(new Class[]{IDeveloper.class, ITester.class}); TesterFeatureIntroductionInterceptor advice = new TesterFeatureIntroductionInterceptor(); weaver.addAdvice(advice); // DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(advice,advice); // weaver.addAdvisor(advisor); Object proxy = weaver.getProxy(); ((ITester)proxy).testSoftware(); ((IDeveloper)proxy).developSoftware(); System.out.println("proxy = " + proxy); } }
pulic interface AopProxyFactory { AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException; }
package org.springframework.aop.framework; import java.io.Serializable; import java.lang.reflect.Proxy; import org.springframework.aop.SpringProxy; @SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // 若是傳入的AdvisedSupport實例的isOptimize或者isProxyTargetClass方法返回true, // 或者目標對象沒有實現任何接口,則採用CGLIB生成代理對象,不然使用動態代理。 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); } } private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); } }
AdvisedSupport是一個生成代理對象所須要的信息的載體。一類爲org.springframework.aop.framework.ProxyConfig爲首的,記載生成代理對象的控制信息;一類以org.springframework.aop.framework.Advised爲首,承載生成代理對象的所須要的必要信息,好比相關目標類、Advice、Advisor等。
ProxyConfig就是普通的JavaBean,定義了五個boolean型的屬性,分別控制在生成代理代理對象的時候,應該採起哪些措施。
要生成代理對象,只有ProxyConfig提供的信息還不夠,咱們還須要生成代理對象的一些具體信息,好比,要針對哪些目標類生成代理對象,要爲代理對象加入何種橫切邏輯等,這些信息能夠經過org.springframework.aop.framework.Advised設置或者拆線呢。默認狀況下Spring AOP框架返回的代理對象均可以強制轉型爲Advised,已查詢代理對象的相關信息。
咱們可使用Advised接口訪問相應代理對象全部持有的Advisor,進行添加Advisor、一處Advisor等相關動做。即便代理對象已經生成完畢,也可對其進行操做,直接操做Advised,更多時候用於測試場景,能夠幫助咱們檢查生成的代理對象是否如所指望的那樣。
歡迎關注微信公衆號哦~ ~