AOP基本概念
面向对象编程 [官网介绍]: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop
白话文:增强/加强一系列的类/方法
定义几个简单的接口
public interface ServiceForKTV { void kty(); }
public interface ServiceForSPA { void spa(); }
具体实现逻辑可以自己写
定义代理逻辑
public interface Advice { //定义一个方法 //用户提供增加逻辑 /** * * @param target * @param method * @param args * @return */ public Object invoke(Object target, Method method,Object[] args) throws Exception; }
public class TimeCsAdvice implements Advice { @Override public Object invoke(Object target, Method method, Object[] args) throws Exception { long startTime = System.currentTimeMillis(); Object ret = method.invoke(target,args); long endTime = System.currentTimeMillis(); System.out.println("类名【"+target.getClass().getName()+"】"); System.out.println("方法名【"+method.getName()+"】"); System.out.println("耗时【"+(endTime - startTime)+"】"); return null; } }
这里是统计接口耗时和打印一些参数
到这里的话,基本上用户的在逻辑都写完了,接下来就要完成一些框架需要干的事了
定义切入点和切入方法
@Data public class Pointcut { private String classPattern; private String methodPattern; }
java是能识别正则表达式的,这里我们用正则。
定义一个类来封装切点和Advice
public class Aspect { private Advice advice; private Pointcut pointcut; }
定义AOP核心处理类InvocationHandler
public class AopInvocationHandler implements InvocationHandler { private Aspect aspect; private Object bean; public AopInvocationHandler(Aspect aspect, Object bean) { this.aspect = aspect; this.bean = bean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (bean.getClass().getName().matches(aspect.getPointcut().getMethodPattern())){ return aspect.getAdvice().invoke(bean,method,args); } return method.invoke(bean,args); } }
增强需要被代理的类
构造简易的Spring上下文对象
public interface IApplicationContext { Object getBean(String beanName) throws Exception; void registerBeanDefinition(String beanName,Class<?> beanClass) throws Exception; void setAspect(Aspect aspect); }
public class MyApplicationContext implements IApplicationContext { private Map<String,Class<?>> beanMap = new ConcurrentHashMap<>(); private Aspect aspect; @Override public Object getBean(String beanName) throws Exception { Object bean = createInstance(beanName); //返回一个增加的方法 bean = proxyEnhance(bean); return bean; } private Object proxyEnhance(Object bean) { if (aspect != null && (bean.getClass().getName()).matches(aspect.getPointcut().getClassPattern()) ){ return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new AopInvocationHandler(aspect,bean)); } return bean; } private Object createInstance(String beanName) throws Exception { return beanMap.get(beanName).newInstance(); } @Override public void registerBeanDefinition(String beanName, Class<?> beanClass) throws Exception{ this.beanMap.put(beanName,beanClass); } @Override public void setAspect(Aspect aspect) { this.aspect = aspect; } }
解释&说明
beanMap 模拟Spring容器,存放实例化的Bean对象
getBean(String beanName) 获取Bean实例。如果对象都被增强,则返回增强过后的类,在实例化出来的时候,已经是被增强过的,可以参考Spring 提供的源码。
proxyEnhance(Object bean) 简易的增强逻辑,通过正则匹配,是否需要被增强。这里有使用到动态代理。java给我们提供了两种便捷的API,分别是JDK提供的动态代理和CGLIB提供的。
registerBeanDefinition(String beanName, Class<?> beanClass) 简易的加入逻辑,将Bean放入到Spring容器中。可以通过XML或是注解的形式将Bean放入到Spring中。这里简化了IOC处理的流程,具体IOC相关的知识,可以参考
- createInstance(String beanName) Bean 的实例化
到这里的话,已经是大功告成。
功成身退
public class AopMain { public static void main(String[] args) throws Exception{ Advice advice = new TimeCsAdvice(); Pointcut pointcut = new Pointcut(); pointcut.setClassPattern("com\\.lynn\\.pay\\.imooc\\.demo\\.aop\\.service\\..*"); pointcut.setMethodPattern(".*impl"); Aspect aspect = new Aspect(); aspect.setAdvice(advice); aspect.setPointcut(pointcut); //创建容器 IApplicationContext applicationContext = new MyApplicationContext(); applicationContext.setAspect(aspect); applicationContext.registerBeanDefinition("ktv", KTVimpl.class); applicationContext.registerBeanDefinition("spa", SPAimpl.class); ServiceForSPA spa = (ServiceForSPA)applicationContext.getBean("spa"); ServiceForKTV ktv = (ServiceForKTV)applicationContext.getBean("ktv"); spa.spa(); ktv.kty(); } }
运行结果如下图,只对KTV服务进行了匹配处理。
image.png
总结
Sping已经为我们封装好了AOP,我们只需要定义好以下几个点就可以使用了
- Pointcut(切入点)
- Advice (通知)
- invoke(增强逻辑)
当然,在实际生产中也有许多作用,比如参数验签,TOKEN校验,统一的脱敏处理,重复请求处理等。方便我们用的同时,不会入侵到原有代码,非常高效。
这里是参考网易云免费直播课Tony老师的课学习的。
网友评论