spring 实现简单AOP

什么是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上完成的,所以代码也是在之上的基础上改的,建议可以先看一下上篇博客。

安装Docker安装插件,可以按照以下步骤进行操作: 1. 首先,安装Docker。可以按照官方文档提供的步骤进行安装,或者使用适合您操作系统的包管理器进行安装。 2. 安装Docker Compose插件。可以使用以下方法安装: 2.1 下载指定版本的docker-compose文件: curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 2.2 赋予docker-compose文件执行权限: chmod +x /usr/local/bin/docker-compose 2.3 验证安装是否成功: docker-compose --version 3. 在安装插件之前,可以测试端口是否已被占用,以避免编排过程中出错。可以使用以下命令安装netstat并查看端口号是否被占用: yum -y install net-tools netstat -npl | grep 3306 现在,您已经安装Docker安装Docker Compose插件,可以继续进行其他操作,例如上传docker-compose.yml文件到服务器,并在服务器上安装MySQL容器。可以参考Docker的官方文档或其他资源来了解如何使用DockerDocker Compose进行容器的安装和配置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Docker安装docker-compose插件](https://blog.youkuaiyun.com/qq_50661854/article/details/124453329)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Docker安装MySQL docker安装mysql 完整详细教程](https://blog.youkuaiyun.com/qq_40739917/article/details/130891879)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值