纯手写springaop,深入了解springaop核心思想
上节利用反射及注解技术我们是现在springmvc的核心功能,本节我们继续完成springaop的代理过程。
1.首先我们定义aop需要用到的注解
Aspect:aop的起始点,注解在类上
PointCut:注解包名,aop扫描该包
Before:controller方法执行前
AfterReturning:controller方法执行结束后
2.扫描包,找到aspect类,并找到pointcut注解,获取包名
private ConcurrentHashMap<String, Object> iocMap; public AopLoader(ConcurrentHashMap<String, Object> iocMap){ this.iocMap=iocMap; } private ClassLoader classLoader = this.getClass().getClassLoader(); /** * 扫描注解aspect * * @param packageName :需要扫描的基础包 */ public void scanAspect(String packageName) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException { URL url = classLoader.getResource("/" + packageName.replaceAll("\\.", "/")); assert url != null; File classDir = new File(url.getFile()); List<File> dirList = Arrays.asList(Objects.requireNonNull(classDir.listFiles())); for (File dir : dirList) { if (dir.isDirectory()) { scanAspect(packageName + "." + dir.getName()); continue; } System.out.println("aop==" + packageName + "." + dir.getName()); String realPackageName = (packageName + "." + dir.getName()).replaceAll(".class", ""); Class<?> cls = Class.forName(realPackageName); if (!cls.isAnnotationPresent(Aspect.class)) continue; Method[] methods = cls.getMethods(); for (Method method : methods) { if(method.isAnnotationPresent(PointCut.class)){ PointCut pointCut=method.getAnnotation(PointCut.class); //扫描到了切入点包,下一步进行动态代理 addProxy(pointCut.value(),cls.newInstance()); } } //包含Aspect标签,需要扫描切入包 } }
3.扫描到包,对切入点的包下的controller进行动态代理
本文用到的iocmap,请参照上文手写springmvc public void addProxy(String proxyPackage,Object aspectClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { URL url = classLoader.getResource("/" + proxyPackage.replaceAll("\\.", "/")); assert url != null; File classDir = new File(url.getFile()); List<File> dirList = Arrays.asList(Objects.requireNonNull(classDir.listFiles())); for (File dir : dirList) { if (dir.isDirectory()) { scanAspect(proxyPackage + "." + dir.getName()); continue; } String realPackageName = (proxyPackage + "." + dir.getName()).replaceAll(".class", ""); Class<?> cls = Class.forName(realPackageName); if(!iocMap.containsKey(StringUtil.lowerFirstCapse(cls.getSimpleName()))) continue; Object iocBean= iocMap.get(StringUtil.lowerFirstCapse(cls.getSimpleName())); AdviceProxy adviceProxy=new AdviceProxy(); iocBean= adviceProxy.createProxyObject(iocBean); adviceProxy.setAspect(aspectClass); iocMap.put(StringUtil.lowerFirstCapse(cls.getSimpleName()),iocBean); } }
下面贴出动态代理的方法
package com.gfh.mvc.framework.aop.proxy; import com.gfh.mvc.framework.aop.annotation.AfterReturning; import com.gfh.mvc.framework.aop.annotation.Before; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class AdviceProxy implements MethodInterceptor { private Object aspect; /** * 要被代理的对象 */ private Object targetObject; public Object createProxyObject(Object target) { this.targetObject = target; //该类用于生成代理对象 Enhancer enhancer = new Enhancer(); //设置目标类为代理对象的父类 enhancer.setSuperclass(this.targetObject.getClass()); //设置回调用对象为本身 enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result; //执行前 before(); //执行拦截的方法 result = methodProxy.invoke(targetObject, args); after(); return result; } /** * 执行方法前 */ private void before() { Method[] methods = aspect.getClass().getMethods(); for (Method method : methods) { //查询出带有Before注解的方法 if (!method.isAnnotationPresent(Before.class)) continue; try { method.invoke(aspect); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } /** * 执行方法后 */ private void after() { Method[] methods = aspect.getClass().getMethods(); for (Method method : methods) { //查询出带有Before注解的方法 if (!method.isAnnotationPresent(AfterReturning.class)) continue; try { method.invoke(aspect); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } public Object getAspect() { return aspect; } public void setAspect(Object aspect) { this.aspect = aspect; } }
4.我们只需要在ioc容器注入完成以后初始化aop即可
AopLoader aopLoader=new AopLoader(iocMap); try { aopLoader.scanAspect("com.gfh.mvc.framework"); } catch (IOException | ClassNotFoundException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); }
5.我们来写一下aop的切入方法
package com.gfh.mvc.framework.aop; import com.gfh.mvc.framework.aop.annotation.AfterReturning; import com.gfh.mvc.framework.aop.annotation.Aspect; import com.gfh.mvc.framework.aop.annotation.Before; import com.gfh.mvc.framework.aop.annotation.PointCut; @Aspect public class AopAdvice { private Long time; @PointCut("com.gfh.mvc.framework.controller") public void cut(){ } @Before public void before(){ System.out.println("我来了before"); time=System.currentTimeMillis(); } @AfterReturning public void AfterReturning(){ System.out.println("我来了AfterReturning:"+(System.currentTimeMillis()-time)); } }
6.执行结果