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,更多时候用于测试场景,能够帮助咱们检查生成的代理对象是否如所指望的那样。
欢迎关注微信公众号哦~ ~