动态代理与SpringAOP

  • 1. 动态代理与SpringAOP的关系
  • 2. java中的动态代理
  • 3. SpingAOP

1. 动态代理与SpringAOP的关系

动态代理是用InvocationHandler接口和Proxy类来实现的一种代理模式

SpringAOP即面向切面编程,它对 AOP 进行了封装,使用面向对象的思想来实现,所以AOP的底层是用动态代理实现的
动态代理与SpringAOP_第1张图片

2. java中的动态代理

java中提供了一个InvocationHandler接口,用来继承实现动态代理

public interface InvocationHandler {
	 public Object invoke(Object proxy, Method method, Object[] args)
     throws Throwable;
}

从源码可以看出,该接口只定义了一个方法,invoke()

invoke()方法3个参数的说明:

Object proxy: 代理对象的实例(Proxy的一个动态实例) 可以通过反射机制获取代理类的信息

method: 真实对象要实现的业务方法(由Proxy实例的静态代码块得到)

args: 第二个参数 method 方法的参数

接下来以一个例子理解动态代理

(1) 创建一个类,继承InvocationHandler接口

public class MyProxyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}

(2) 添加方法,得到生成的代理类

 类中除了重写的invoke方法,其余基本是固定写法,目的是为了通过传入真实对象(要使用代理的对象)得到代理类
 具体解释见代码注释
//用这个类,自动生成代理类
public class MyProxyInvocationHandler implements InvocationHandler {
    
    //被代理的类
    public Object object;

    //得到一个代理实例(代理类)
    public Object getProxy(Object object){
        this.object = object;
        //通过Proxy.newProxyInstance方法创建一个代理对象 即得到代理类
        // 3个参数为 要代理的对象的类加载器、接口、及实现了InvocationHandler接口的类,一般即自身
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}

(3) 重写invoke方法

① 在invoke方法中,可以通过 method 执行要代理的类的自身的方法

 @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 将代理的对象和执行参数代入执行方法 也就可以执行要代理的对象自身的方法
        method.invoke(object,args);

        return null;
    }

② 在method.invoke()函数前后,可以添加额外的执行方法,也就是面向切面编程的思想(原来是在要代理的类中添加,但是这样就可以在这个方法里面添加)

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("在"+method.getName()+"前添加了一个方法");
        
        // 将代理的对象和执行参数代入执行方法 也就可以执行代理类自身的方法
        method.invoke(object,args);

        System.out.println("在"+method.getName()+"后添加了一个方法");

        return null;
    }

3. SpingAOP

Spring 框架对 AOP 进⾏了封装,Spring 使用面向对象的思想来实现 AOP

Spring 框架中不需要创建 InvocationHandler,只需要创建⼀个切面对象,将所有的非业务代码在切⾯对象中完成,Spring 框架底层会自动根据切面类以及目标类生成⼀个代理对象

上述概念有点繁琐,下面通过使用SpringAOP的一个例子来理解

思路:编写一个计算器类 拥有加减乘除方法,然后通过SpringAOP给计算器添加额外的打印日志方法

(1) 编写MyCalculator 接口及其实现类 并添加上@Component注解(代表交给springIOC容器管理)

public interface MyCalculator {
    int add(int num1,int num2);
    int sub(int num1,int num2);
    int mul(int num1,int num2);
    int div(int num1,int num2);
}
public class MyCalculatorImpl implements MyCalculator{

    public int add(int num1, int num2) {
        return num1+num2;
    }

    public int sub(int num1, int num2) {
        return num1-num2;
    }

    public int mul(int num1, int num2) {
        return num1*num2;
    }

    public int div(int num1, int num2) {
        return num1/num2;
    }
}

(2) 编写切面日志类,使用SpringAOP,实现额外添加打印日志方法的功能

@Aspect 代表该类是一个切面类

//切面日志类 通过SpringAOP来实现功能 @Aspect注解表示该类为一个切面类
@Aspect
@Component
public class AspectLogger {
}

(3)在AspectLogger类中添加方法,并使用SpringAOP注解

//切面日志类 通过SpringAOP来实现功能 @Aspect注解表示该类为一个切面类
@Aspect
@Component
public class AspectLogger {

    //Before 表示在value对应方法执行前执行 .*代表该类下的所有方法 (..)表示参数通配符 也可以使用.方法名()来指定一个方法
    @Before(value="execution(public int MyCalculatorImpl.*(..))")
    // JoinPoint:切入点
    public void executeBeforeMethod(JoinPoint joinPoint){
        //获取⽅法名
        String name = joinPoint.getSignature().getName();
        //获取参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"⽅法的参数是:"+ args);
    }

    @After(value = "execution(public int MyCalculatorImpl.*(..))")
    public void after(JoinPoint joinPoint){
        //获取⽅法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"⽅法执⾏完毕");
    }

    //返回后执行 returning赋予的String要和方法的第二个参数Object result <- 即这个result名字对应
    @AfterReturning(value = "execution(public int MyCalculatorImpl.*(..))",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        //获取⽅法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"⽅法的结果是"+result);
    }

    //抛出异常后执行 参数对应和上一个一样
    @AfterThrowing(value = "execution(public int MyCalculatorImpl.*(..))",throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint,Exception exception){
        //获取⽅法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"⽅法抛出异常:"+exception);
    }
}

代码说明:

@Before @After @AfterReturning @AfterThrowing :表示方法执行的位置和时机

JoinPoint对象:JoinPoint即切入点,它封装了SpringAop中切面方法的信息
在切面方法通过JoinPoint参数,可以获取到封装了该方法信息的JoinPoint对象

JionPoint的常用方法 :

方法名 功能
Signature getSignature() 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class信息等
Object[] getArgs() 获取传入目标方法的参数对象
Object getTarget() 获取被代理的对象
Object getThis() 获取代理对象

(4) 在Test类中编写main方法进行测试

    public static void main(String[] args) {
//        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.ruoxi");
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
        MyCalculator myCalculator = (MyCalculator) applicationContext.getBean("myCalculatorImpl");
        myCalculator.add(1,2);
        myCalculator.sub(1,5);
        myCalculator.mul(1,3);
        myCalculator.div(4,2);
    }
}

动态代理与SpringAOP_第2张图片

通过输出结果可以看出,已成功通过SpringAOP的方式给MyCalulator的各个函数添加了打印日志的方法

动态代理与SpringAOP_第3张图片

你可能感兴趣的:(java,Spring,java,spring,aop)