Spring —— AOP原理

在我另一篇博客SSM之Spring 02 —— 动态代理、AOP、Spring-MyBatis、声明式事务中有简单描述代理模式和AOP原理,当时自己也还是有些没明白,后面在面试过程中被问到,这里详细说明一下。

AOP是什么就不再说明了,主要解释下AOP的原理——动态代理。

动态代理

和静态代理不同,动态代理是动态生成代理对象,克服了静态代理的问题——不同目标对象都需要写一个代理类。

动态代理主要有两种方式:JDK的动态代理和cglib代理。

JDK的动态代理

它是对对象进行代理,底层利用反射机制实现,同时需要我们自定义一个实现了InvocationHandler接口的类,这个接口中的invoke方法就是AOP的本质。

下面我们通过代码来进行理解。

1、定义好UserDao以及实现类,这是我们常见的方式。

//为了方便就不写参数了
public interface UserDao {
    
    
    boolean addUser();
    boolean deleteUser();
}

public class UserDaoImpl implements UserDao {
    
    
    @Override
    public boolean addUser() {
    
    
        System.out.println("addUser();");
        return false;
    }

    @Override
    public boolean deleteUser() {
    
    
        System.out.println("deleteUser();");
        return true;
    }
}

2、写一个测试,具体解释看注解。可以看到Proxy和InvocationHandler都是reflect包下的,说明底层肯定是反射。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {
    
    
    public static void main(String[] args) {
    
    
      	//创建好目标对象
        UserDao ud = new UserDaoImpl();
      
      	//这个就是我们自定义的一个实现类,里面invoke方法就是AOP的原理。
      	InvocationHandler ih = new MyInvocationHandler(ud);//将ud作为目标对象传进去
      
        /*
        		这个方法返回一个代理对象,我们会在代理对象中做一些其他操作
            newProxyInstance(ClassLoader, interfaces, InvocationHandler)
            1、ClassLoader:目标对象的类加载器,代码是固定的。
            2、interfaces:目标对象的接口数组,代码也是固定的,都是通过反射获得。
            3、InvocationHandler:这是关键点,AOP能实现方法的增强就在这个接口里。需要自己定义。
         */
        Object newProxyInstance = Proxy.newProxyInstance(ud.getClass().getClassLoader(),
                ud.getClass().getInterfaces(), ih);
      
        //执行代理对象被增强后的方法(增强的意思是在原有方法的基础上添加了其他效果,同时不影响原有业务,同Spring AOP的事务)
        UserDao proxyUserDao = (UserDao)newProxyInstance;
        proxyUserDao.addUser();
        System.out.println("--------------");
        proxyUserDao.deleteUser();
    }
}

3、自定义的实现类,这是实现AOP增强方法的关键。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
    
    
    private Object object;

    public MyInvocationHandler(Object object) {
    
    
        this.object = object;
    }

    /**
     *
     * @param proxy 代理对象,就是我们从外部传进来的,但一般不用这个,会用我们自定义的object成员。
     * @param method 目标对象的方法对象,是JDK传过来的。
     * @param args 目标对象方法的参数对象。
     * @return 对应于我们目标对象的方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    

        String before = "";
        String after = "";
        //这里通过反射来判断UserDao的两个方法,如果不判断,那么就会针对目标对象的所有方法进行增强。
        if(method.getName().equals("addUser")){
    
    
            before = "前置通知:addUser()方法执行 之前";
            after = "后置通知:addUser()方法执行 之后";
        }
        if(method.getName().equals("deleteUser")){
    
    
            before = "前置通知:deleteUser()方法执行 之前";
            after = "后置通知:deleteUser()方法执行 之后";
        }
        //下面这样就实现了和Spring AOP 一样的效果:方法的增强,即在方法执行前做一些其他事,且不影响到原有业务。
        System.out.println(before);
        Object result = method.invoke(object, args);//通过反射调用目标对象的方法
        System.out.println(after);
        return result;
    }
}

4、运行结果:
在这里插入图片描述

cglib

和JDK动态代理不同,cglib(code generator library 代码生成库)代理的是字节码对象(即Class),而JDK动态代理代理的是对象

在这里插入图片描述

步骤:

  1. cglib生成一个字节码对象(即Class对象),内容是空的。
  2. 设置字节码对象的父类为目标对象,即UserDaoImpl。
  3. 通过字节码对象中继承了目标对象的方法,即addUser,进行回调,调用父类的addUser,并在执行其方法前完成方法增强。
  4. 创建代理对象,通过代理对象执行Class的方法。

注意:cglib是Spring中的,所以需要导入Spring核心包才能使用。

在这里插入图片描述

导入进来
在这里插入图片描述

下面进行代码说明:

这里面CallBack就是AOP的原理,它通过子类调用父类UserDaoImpl的addUser方法,并在调用时完成增强。

package test02_AOP.cglib;

import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Enhancer enhancer = new Enhancer();//空的字节码对象
        enhancer.setSuperclass(UserDaoImpl.class);//设置父类
        Callback callback = new MyCallback();
        enhancer.setCallback(callback);//设置回调

        UserDao userDao = (UserDao) enhancer.create();//用代理类Enhancer 创建代理对象
        userDao.addUser();
    }
}
package test02_AOP.cglib;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//本来是继承Callback接口,表示回调
//但Callback接口里什么都没有,只是起到一个标志作用,所以继承其子类MethodInterceptor
//MethodInterceptor接口仅有一个方法,拦截我们待增强的方法
public class MyCallback implements MethodInterceptor {
    
    
    /**
     *
     * @param proxy 代理对象,即前面图里面的 class XX extends UserDaoImpl的XX对象
     * @param method 目标对象的方法
     * @param args 目标对象中的 方法对象 的参数对象(相当于addUser方法的参数)
     * @param methodProxy 代理对象中的 方法代理对象(相当于父类UserDaoImpl的addUser方法)
     * @return 返回代理对象方法的结果
     * @throws Throwable
     */
    @Override
    public Object intercept(
            Object proxy, Method method, Object[] args, MethodProxy methodProxy
                           ) throws Throwable {
    
    
        System.out.println("前置通知");
        //执行原来的方法
        Object object = methodProxy.invokeSuper(proxy, args);//这里不能用invoke,相当于前面图里super.addUser()
        System.out.println("后置通知");

        return object;
    }
}

结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_39763246/article/details/115015812