Java框架(四)--Spring AOP(3)--Spring AOP实现原理

第三章、Spring AOP实现原理

一、Spring AOP实现原理

Spring基于代理模式实现功能动态扩展,包含两种形式:
目标类拥有接口,通过JDK动态代理实现功能扩展。
目标类没有接口,通过CGLib组件实现功能扩展。

1、代理模式

代理模式通过代理对象对原对象的实现功能扩展。
在这里插入图片描述

2、静态代理

静态代理是指必须手动创建代理类的代理模式使用方式。

打开IDEA创建新的maven工程,在com.ql.spring.service包下创建UserService接口

package com.ql.spring.service;

public interface UserService {
    public void createUser();
}

然后同包下创建该接口的实现类UserServiceImpl

package com.ql.spring.service;

public class UserServiceImpl implements UserService{
    @Override
    public void createUser() {
        System.out.println("执行创建用户业务逻辑");
    }
}

然后同包下创建代理类UserServiceProxy

package com.ql.spring.service;

import java.text.SimpleDateFormat;
import java.util.Date;

public class UserServiceProxy implements UserService{
    //持有委托类的对象
    private UserService userService;
    public UserServiceProxy(UserService userService){
        this.userService = userService;
    }

    @Override
    public void createUser() {
        System.out.println("======="+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"===========");
        userService.createUser();
    }
}

然后在com.ql.spring包下创建入口类Application ,并运行

package com.ql.spring;

import com.ql.spring.service.UserService;
import com.ql.spring.service.UserServiceImpl;
import com.ql.spring.service.UserServiceProxy;

public class Application {
    public static void main(String[] args) {
        UserService userService = new UserServiceProxy(new UserServiceImpl());
        userService.createUser();
    }
}
/*
=======2022-07-04 23:27:22 421===========
执行创建用户业务逻辑
*/

代理类的构造方法中,参数建议使用持有一个接口的某个实现类,而不是具体实现类。原因是作为代理模式是可以嵌套使用的,每次构造方法里传入实现接口的其他代理类就可以了,从而实现多层嵌套。

在com.ql.spring.service包下再创建一个代理类UserServiceProxy1

package com.ql.spring.service;

import java.text.SimpleDateFormat;
import java.util.Date;

public class UserServiceProxy1 implements UserService{
    //持有委托类的对象
    private UserService userService;
    public UserServiceProxy1(UserService userService){
        this.userService = userService;
    }

    @Override
    public void createUser() {
        userService.createUser();
        System.out.println("=========后置扩展功能=========");
    }
}

然后修改入口类Application

package com.ql.spring;

import com.ql.spring.service.UserService;
import com.ql.spring.service.UserServiceImpl;
import com.ql.spring.service.UserServiceProxy;
import com.ql.spring.service.UserServiceProxy1;

public class Application {
    public static void main(String[] args) {
        UserService userService = new UserServiceProxy1(new UserServiceProxy(new UserServiceImpl()));
        userService.createUser();
    }
}
/*
=======2022-07-04 23:32:05 461===========
执行创建用户业务逻辑
=========后置扩展功能=========
*/

3、JDK动态代理

动态代理,必须实现接口才可以运行。
JDK动态代理具体实现原理:

  • 通过实现 InvocationHandler 接⼝创建⾃⼰的调⽤处理器;
  • 通过为 Proxy 类指定 ClassLoader 对象和⼀组 interface 来创建动态代理;
  • 通过反射机制获取动态代理类的构造函数,其唯⼀参数类型就是调⽤处理器接⼝类型;
  • 通过构造函数创建动态代理类实例,构造时调⽤处理器对象作为参数参⼊;

JDK 动态代理是⾯向接⼝的代理模式,Spring 通过 Java 的反射机制⽣产被代理接⼝的新的匿名实现类,重写了其中 AOP 的增强⽅法。

在com.ql.spring.service包下创建EmployeeService和他的实现类EmployeeServiceImpl

package com.ql.spring.service;

public interface EmployeeService {
    public void createEmployee();
}

package com.ql.spring.service;

public class EmployeeServiceImpl implements EmployeeService{
    @Override
    public void createEmployee() {
        System.out.println("执行创建员工业务逻辑");
    }
}

在同包下创建动态代理实现类ProxyInvocationHandler

package com.ql.spring.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强
 * InvocationHandler实现类与切面类的环绕通知类似
 */
public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;//目标对象
    private ProxyInvocationHandler(Object target){
        this.target = target;
    }
    /**
     * 在invoke()方法对目标方法进行增强
     * @param proxy 代理类对象
     * @param method 目标方法对象
     * @param args 目标方法实参
     * @return 目标方法运行后返回值
     * @throws Throwable 目标方法抛出的异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("========"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"========");
        Object ret = method.invoke(target, args);//调用目标方法,类似环绕通知的ProceedingJoinPoint.proceed()
        return ret;
    }

    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService);
        //动态创建代理类
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                invocationHandler);
        userServiceProxy.createUser();

        EmployeeService employeeService = new EmployeeServiceImpl();
        EmployeeService employeeServiceProxy = (EmployeeService) Proxy.newProxyInstance(employeeService.getClass().getClassLoader(),
                employeeService.getClass().getInterfaces(),
                new ProxyInvocationHandler(employeeService));
        employeeServiceProxy.createEmployee();
    }
}
/*
========2022-07-05 08:52:16 166========
执行创建用户业务逻辑
========2022-07-05 08:52:16 169========
执行创建员工业务逻辑
*/

4、CGLib实现代理类

CGLib是运行时字节码增强技术。(Code Generation Library)
Spring AOP扩展无接口类使用CGLib
AOP会运行时生成目标继承类字节码的方式进行行为扩展。

利⽤ ASM 开源包,对代理对象类的 class ⽂件加载进来,通过修改其字节码⽣成⼦类来处理。
在这里插入图片描述
两者对⽐:
JDK 动态代理是⾯向接⼝的。
CGLib 动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被 final 关键字所修饰,会失败。
如果要被代理的对象是个实现类,那么 Spring 会使⽤ JDK 动态代理来完成操作(Spirng 默认采⽤ JDK 动态代理实现机制);
如果要被代理的对象不是个实现类,那么 Spring 会强制使⽤ CGLib 来实现动态代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值