Spring AOP原理(java动态代理)

AOP原理

AOP (Aspect Orient Programming), 面向切面编程。AOP是一种思想,相对于OOP(面向对象编程),AOP是对OOP的一种补充。
SpringAOP依靠java的动态代理实现。
(1)对于有接口实现的类,使用JDK动态代理实现AOP
(2)对于没有接口实现的类,使用CGLIB动态代理实现AOP

为什么使用AOP

在很多业务场景下,一个类方法中除了主要的业务逻辑外,通常还会包含一些很重要但是不是主要业务逻辑的次要逻辑(比如说操作日志记录)。这些次要逻辑是往往是不可或缺的,而且是多个主要逻辑下共同需要的。如果在每个主要逻辑方法后都粘贴上这些“次要逻辑代码”就会使我们的代码变得臃肿。所以将主要代码和次要代码分开,在编译时在将两者合并的思想(AOP)就诞生了。
使用java的代理技术,为主要逻辑创建一个代理类,我们在调用主要逻辑的时候将不是直接调用,而是通过代理类调用,代理类帮助我们合并主要逻辑和次要逻辑,简化代码。
在这里插入图片描述

AOP术语

连接点: 类里的可以被增强的方法
切入点: 实际被增强的方法
通知(增强): 实际为目标类的目标方法进行增加的部分
通知类型: (见名知意)
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知
切面(是一个动作): 将通知织入切入点的过程

代理模式

代理模式是一种结构型设计模式,给对象提供一个代理对象,并由代理对象控制对真实对象的访问

Java代理

代理分为静态代理和动态代理,动态代理又有JDK动态代理和CGLIB动态代理之分。

  • 静态代理
    静态代理就是自己手动完成代理类,代理类在编码阶段完成
//接口,规范实现类
public interface UserService {
    public void myMethod();   
}
//被代理的类,主要逻辑实现
public class UserServiceImpl implements UserService {  
    public void myMethod() {  
    	....
        System.out.println("在这里实现了主要业务逻辑");
    }
}
//手动完成的代理类,实现对主要逻辑的增强
public class UserServiceProxy implements UserService {
    private UserService target; // 被代理的对象
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    public void myMethod() {
        before();
        target.myMethod();    // 主要逻辑调用
        after();
    }
    private void before() {     // 在主要逻辑前执行的次要逻辑
        System.out.println("before次要逻辑");
    }
    private void after() {      // 在主要逻辑后执行的次要逻辑
        System.out.println("after次要逻辑");
    }
}
//测试静态代理效果
public class Client {
    public static void main(String[] args) {
        UserService userServiceImpl = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(userServiceImpl);
        proxy.myMethod();
    }
}
//运行结果,通过代理类调用主要逻辑后,代理类为主要逻辑织入了次要逻辑
before次要逻辑
在这里实现了主要业务逻辑
after次要逻辑

静态代理的问题
(1)当需要代理多个类的时候,需要创建多个代理类,或者代理类实现多个接口,造成代码量庞大
(2)需要增加,删除,修改实现的接口时,代理类和真实类都需要修改,不容易维护

  • JDK动态代理

JDK动态代理是基于java的反射实现,在运行是动态的获取类的信息,创建代理类对象,也就是说,我们不再需要手动的完成代理类的编写,而是我们提供好主要逻辑的类和一个次要逻辑的类,由java在运行时帮助我们生成代理类对象
JDK动态代理主要设计两个接口:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler
JDK动态代理必须要求被代理对象是一个接口的实现类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler {
    Object target;  // 被代理的对象,实际的方法执行者
    public LogHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);  // 调用 target 的 method 方法
        after();
        return result;  // 返回方法的执行结果
    }
    // 调用invoke方法之前执行
    private void before() {
        System.out.println("before次要逻辑");
    }
    // 调用invoke方法之后执行
    private void after() {
        System.out.println(String.format("after次要逻辑");
    }
}
import proxy.UserService;
import proxy.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        // 1. 创建被代理的对象,UserService接口的实现类
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        // 2. 获取对应的 ClassLoader
        ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
        // 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService,
        Class[] interfaces = userServiceImpl.getClass().getInterfaces();
        // 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
        InvocationHandler logHandler = new LogHandler(userServiceImpl);
        UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);
        // 调用代理的方法
        proxy.myMethod();
    }
}

问题:为什么JDK动态代理被代理对象一定要是一个接口的实现类呢?
答: 实现代理类需要对两个类进行增强(被代理类和InvocationHandler)。而在java中增强类的方法有两种,一种是继承、另一种是实现接口。动态代理需要jvm去调用InvocationHandler中的invoke方法,而不是我们手动去做,所以JDK选择了继承父类Proxy的方法InvocationHandler存在父类的对象中。通过父类Proxy的构造方法,保存了创建代理对象过程中传进来的InvocationHandler的实例,使用protected修饰保证了它可以在子类中被访问和使用。众所周知,java只支持单继承,所以在想通过继承的方式对被代理类进行扩是不可能的,所以只能通过接口的方式进行扩展。
参考JDK动态代理为什么必须要基于接口?

  • CGLIB动态代理

由于JDK动态代理要求被代理类必须是接口实现类,存在局限性,所以基于继承采用ASM机制实现的CGLIB动态代理技术用来补充JDK动态代理的缺陷。

//LogInterceptor用于方法的拦截回调
import java.lang.reflect.Method;
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(object, objects);   // 注意这里是调用 invokeSuper 而不是 invoke,否则死循环,methodProxy.invokesuper执行的是原始类的方法,method.invoke执行的是子类的方法
        after();
        return result;
    }
    private void before() {
        System.out.println("before次要逻辑");
    }
    private void after() {
        System.out.println("after次要逻辑");
    }
}

//CGLIB测试
public class Client{
    public static void main(String[] args) {
        LogInterceptor uProxy = new LogInterceptor();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);  
        enhancer.setCallback(uProxy );

        UserDao dao = (UserServiceImpl)enhancer.create();   // 创建代理类
        dao.myMethod();
    }
}

JDK与CGLIB的对比?

JDK动态代理:
(1)基于Java的反射机制实现,必须是实现了接口的业务类才能用这个方法生成代理对象;(必须要接口是因为动态生成的代理类已经继承了Proxy类,java不支持多继承,所以只能通多接口的多实现进行代理)
(2)最小化依赖关系,方便维护;
(3)JDK支持,更加可靠;
(4)代码实现简单;
CGLIB动态代理:
(1)基于ASM机制实现,通过生成业务类的子类实现代理;
(2)不需要接口;
(3)性能高;
(4)由于CGLIB是通过动态创建子类的方法进行代理,所以无法对final修饰的方法进行代理;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值