spring 实现简单AOP

tech2024-06-06  70

什么是AOP?

AOP就是在不修改原代码的情况下,动态的添加功能

上篇文章中 我们实现了简单的IOC,这次在其基础上实现AOP,先理清一下思路,spring的AOP是基于动态代理实现的,如果被代理类实现了接口,就采用jdk动态代理的方式,如果没有就采用cglib的方式,我们实现自己的aop也是基于这两种动态代理,首先需要添加新的注解,必须要有的是切面类的注解@Aspect,再添加@Before和@After,看目录结构 首先定义切面的注解 @Aspect,(@Before,@After)定义类似,唯一注意的就是Target里面的值。

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Aspect { String value() default ""; }

修改核心类,创建几个map用来存放对象,因为实现ioc的时候map里面存放的是类定义对象(具体可以参考上篇实现ioc),如果用代理的类定义对象去覆盖原本的对象,在get的时候如果是原型模式因为会重新new,所以就不再是代理对象,这里需要重新创建一个map存放真正的代理对象,再创建一个map存放切面类的类定义对象,因为在代理类里面我们需要去获取所有的切面类,并且获取里面所有的方法,如果方法上面的切入点的方法和本次执行的方法一致,就执行对应的@Before或者@After

//存储代理对象 private Map<String,Object> proxyFactory = new ConcurrentHashMap<>(); //存储切面对象 private Map<String,Class<?>> aspectFactory = new ConcurrentHashMap<>();

在扫描包的时候,应该判断是否有@Aspect的注解,如果有就应该将类定义对象存入切面类的map,并且将jdk动态代理类和cglib动态代理类的map设置切面类的map,为了方便在代理类里面去做判断。

//通过反射创建对象 Class<?> clazz = Class.forName(pkgCls); //判断是否需要注入bean的注解 if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Component.class) || clazz.isAnnotationPresent(Service.class) || clazz.isAnnotationPresent(Aspect.class)){ String key = getKey(clazz); if (key == null){ //将名字首字母小写 作为map中的key key = String.valueOf(fileName.charAt(0)).toLowerCase() + fileName.substring(1); } if (clazz.isAnnotationPresent(Aspect.class)){ //如果这个类是一个切面类 调用setAspect方法 setAspect(clazz); aspectFactory.put(key,clazz); //将代理类的map设置为当前切面类的map proxyInvocation.setAspectMap(aspectFactory); absMethodAdvance.setAspectMap(aspectFactory); } beanDefinationFactory.put(key, clazz); }

实现setAspect方法,通过传入的切面类对象,去处理value值,获取被代理类的类路径,通过反射创建对象,传入代理类,获取代理对象,存入代理对象的map,注意,这里存入的是真正的对象,而不是类定义对象。

/** * 通过传入的对象获取切入点,切入的方法等 * @param clazz */ private void setAspect(Class<?> clazz) { //找到里面所有的方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method:methods) { method.setAccessible(true); //判断方法上面的注解是before还是after String[] value = null; if (method.isAnnotationPresent(Before.class)){ //获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型 value = method.getDeclaredAnnotation(Before.class).value().split(" "); }else if(method.isAnnotationPresent(After.class)){ value = method.getDeclaredAnnotation(After.class).value().split(" "); } if (value != null){ //被代理的类名 String className = value[0]; //被代理的方法名 String methodName = value[1].substring(0,value[1].indexOf("(")); //获取参数类型 String[] paramsType = value[1].substring(value[1].indexOf("(")+1,value[1].lastIndexOf(")")).split("\\,"); try { //创建被代理对象 Class<?> proxyed = Class.forName(className); //获取被代理对象在map中的key 也就是value值 String key = getKey(proxyed); if (key == null){ key = className.substring(className.lastIndexOf(".")+1).substring(0,1).toLowerCase() + className.substring(className.lastIndexOf(".")+2); } Object obj = null; //判断有无接口 如果实现了接口 默认用jdk动态代理 没有 则用cglib if (proxyed.getInterfaces().length == 0){ //没有使用接口 obj = absMethodAdvance.createObject(proxyed.newInstance()); }else{ obj = proxyInvocation.createObject(proxyed.newInstance()); } //将代理对象放入map proxyFactory.put(key,obj); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } } }

修改getBean,在最开始判断代理对象的map有无这个对象,因为里面的key和在存放的时候和被代理类的key是相同的,所以也能获取到对应的代理对象

//先判断代理对象容器中是否有这个属性 如果有 返回代理对象 if (proxyFactory.get(beanId) != null){ return proxyFactory.get(beanId); }

实现jdk动态代理的代理类

/** * jdk动态代理 */ public class ProxyInvocation implements InvocationHandler { //被代理对象 private Object target; //存储切面类对象的map private Map<String, Class<?>> aspectMap; public void setAspectMap(Map<String, Class<?>> aspectMap) { this.aspectMap = aspectMap; } public Object createObject(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } /** * 代理执行时候的方法 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; //循环map for (Map.Entry<String,Class<?>> entry:aspectMap.entrySet()) { Class<?> clazz = entry.getValue(); //获取类下所有的方法 Method[] methods = clazz.getDeclaredMethods(); if (methods.length == 0){ obj = method.invoke(target,args); }else { //循环遍历方法 for (Method method1:methods) { //获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型 String[] value = null; //被代理的方法名 String methodName = null; //判断方法上面是否有@Before的注解 if (method1.isAnnotationPresent(Before.class)){ //如果有 判断方法名是否相同 value = method1.getDeclaredAnnotation(Before.class).value().split(" "); methodName = value[1].substring(0,value[1].indexOf("(")); if (methodName.equals(method.getName())){ method1.invoke(clazz.newInstance(),null); } } } obj = method.invoke(target,args); for (Method method1:methods) { //获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型 String[] value = null; //被代理的方法名 String methodName = null; //判断方法上面是否有@After的注解 if (method1.isAnnotationPresent(After.class)){ //如果有 判断方法名是否相同 value = method1.getDeclaredAnnotation(After.class).value().split(" "); methodName = value[1].substring(0,value[1].indexOf("(")); if (methodName.equals(method.getName())){ method1.invoke(clazz.newInstance(),null); } } } } } return obj; } }

实现cglib动态代理的代理类

/** * cglib代理模式 */ public class AbsMethodAdvance implements MethodInterceptor { private Object target; //存储切面类对象的map private Map<String, Class<?>> aspectMap; public void setAspectMap(Map<String, Class<?>> aspectMap) { this.aspectMap = aspectMap; } public Object createObject(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object obj = null; //循环map for (Map.Entry<String,Class<?>> entry:aspectMap.entrySet()) { Class<?> clazz = entry.getValue(); //获取类下所有的方法 Method[] methods = clazz.getDeclaredMethods(); if (methods.length == 0){ obj = methodProxy.invokeSuper(o, objects); }else { //循环遍历方法 for (Method method1:methods) { //获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型 String[] value = null; //被代理的方法名 String methodName = null; //判断方法上面是否有@Before的注解 if (method1.isAnnotationPresent(Before.class)){ //如果有 判断方法名是否相同 value = method1.getDeclaredAnnotation(Before.class).value().split(" "); methodName = value[1].substring(0,value[1].indexOf("(")); if (methodName.equals(method.getName())){ method1.invoke(clazz.newInstance(),null); } } } obj = methodProxy.invokeSuper(o, objects); for (Method method1:methods) { //获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型 String[] value = null; //被代理的方法名 String methodName = null; //判断方法上面是否有@After的注解 if (method1.isAnnotationPresent(After.class)){ //如果有 判断方法名是否相同 value = method1.getDeclaredAnnotation(After.class).value().split(" "); methodName = value[1].substring(0,value[1].indexOf("(")); if (methodName.equals(method.getName())){ method1.invoke(clazz.newInstance(),null); } } } } } return obj; } }

接下来就是测试,创建切面类

/** * 切面类 */ @Aspect public class MyAspect { @Before("com.study.service.impl.UserServiceImpl eat()") public void before(){ System.out.println("before"); } @After("com.study.service.impl.UserShow run()") public void after(){ System.out.println("after"); } }

创建测试类,注意一个是实现了接口,一个是没有实现接口。

@Service public class UserServiceImpl implements UserService { @Override public void eat() { System.out.println("eat"); } } @Service public class UserShow { public void run(){ System.out.println("run"); } }

编写测试代码

AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext("com.study"); System.out.println("=======测试aop======="); System.out.println("测试jdk动态代理实现"); UserService userService = (UserService) acac.getBean("userServiceImpl"); userService.eat(); System.out.println(); System.out.println("测试cglib动态代理 实现"); UserShow userShow = acac.getBean("userShow",UserShow.class); userShow.run(); System.out.println();

测试结果

小结:

主要流程就是获取切面类里面的切入点,然后生成代理对象,通过jdk动态代理或者cglib动态代理在方法执行的前后,去添加我们想要的Before方法或者After方法,因为这个实在上篇博客自己写的ioc上完成的,所以代码也是在之上的基础上改的,建议可以先看一下上篇博客。

最新回复(0)