代理模式与Spring AOP:从设计模式到手写源码

代理模式

给一个对象提供一种代理对象以控制对该对象的访问。代理对象在不改变原对象代码的基础上对原对象的功能进行扩展。

使用代理降低了系统的耦合度,扩展性也会更好,还可以起到保护目标对象的作用。代理模式在 Java 开发中是广泛应用的,特别是在框架中底层原理经常涉及到代理模式(尤其是动态代理)。

静态代理和动态代理,实际使用时还是动态代理使用的比较多。原因就是静态代理需要自行手写代码,维护、修改非常繁琐,会额外引入很多工作量。也不能很好的使用配置完成逻辑的指定,所以使用较少。

  • 静态代理维护复杂,一旦接口或父类发生改变,所有相关的类或接口就都得进行维护。
  • 动态代理无需手写代理类,也不会存在代码编译的过程。运用在内存中生产代理类的技术在 JVM 的运行区造一个代理对象,只需对需要修改的部分进行编辑。

基于 JDK 和基于 CGLIB,实际使用时两个都会用到。

在 Spring 中,默认情况下它就支持了两种动态代理方式。如果你指定的目标类实现了接口,Spring 就会自动选择 JDK 的动态代理。而如果目标类没有实现接口,则 Spring 会使用 CGLIB。而在 SpringBoot 中,默认使用的是 CGLIB。

我们在开发时,由于基于 JDK 的动态代理要求比较多,更不容易实现,所以很多人习惯于统一配置为使用 CGLIB 进行代理。也就是 CGLIB 更通用。

【静态代理】基于接口实现

让目标对象和代理对象都实现一个共同接口。那么这两个类就有了公共的方法,就可以在代理对象中实现对目标对象功能的扩展。

public class StaticProxyInterfaceDemo {

    /**
     * 公共接口
     */
    public interface Dao {
        void save();
    }

    /**
     * 原始对象
     */
    static class UserDao implements Dao {
        @Override
        public void save() {
            System.out.println("保存数据");
        }
    }

    /**
     * 代理对象
     */
    static class UserDaoProxy implements Dao {

        private UserDao userDao;

        @Override
        public void save() {
            System.out.println("开启事务");
            userDao.save();
            System.out.println("提交事务");
        }
    }
}

【静态代理】基于继承实现

让代理对象继承目标对象,那么代理对象就拥有目标对象的方法,同时又可以对其功能进行扩展。

public class StaticProxyExtendsDemo {

    /**
     * 原始对象
     */
    static class UserDao {
        public void save() {
            System.out.println("保存数据");
        }
    }

    /**
     * 代理对象
     */
    static class UserDaoProxy extends UserDao {

        @Override
        public void save() {
            System.out.println("开启事务");
            super.save();
            System.out.println("提交事务");
        }
    }
}

【动态代理】基于接口的 JDK 动态代理

JDK 动态代理基于接口实现,要求原始对象必须实现接口。

public class JdkProxyDemo {

    public static void main(String[] args) {
        // 创建目标对象(如果是 JDK 8 之前的版本,匿名内部类访问外面的变量需要声明 final)
        UserService userService = new UserServiceImpl();
        Class<? extends UserService> userServiceClass = userService.getClass();
        // 使用 JDK 动态代理生成代理对象
        UserService proxyObj = (UserService) Proxy.newProxyInstance(userServiceClass.getClassLoader(), userServiceClass.getInterfaces(), (proxy, method, args1) -> {
            // 在此处编写前置操作
            System.out.println("Before");
            Object result = null;
            try {
                result = method.invoke(userService, args1);
            } catch (Exception e) {
                // 在此处编写异常操作
                System.out.println("Exception");
            }
            // 在此处编写后置操作
            System.out.println("After");
            // 可以在此处编写新的返回值并返回
            return result;
        });
        proxyObj.login();
    }
}

【动态代理】基于继承的 CGLIB 动态代理

CGLIB 动态代理基于继承原始类实现,不需要原始类实现接口,但是原始类不能是 final。

public class CglibProxyDemo {

    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        // 使用 CGlib 动态创建代理对象
        UserService proxyObj = (UserService) Enhancer.create(userService.getClass(), (InvocationHandler) (o, method, objects) -> {
            // 在此处编写前置操作
            System.out.println("Before");
            Object result = null;
            try {
                result = method.invoke(userService, args);
            } catch (Exception e) {
                // 在此处编写异常操作
                System.out.println("Exception");
            }
            // 在此处编写后置操作
            System.out.println("After");
            // 可以在此处编写新的返回值并返回
            return result;
        });
        proxyObj.login();
    }
}

Spring AOP

Spring 的 AOP(面向切面编程)是 Spring 框架中的一个核心特性,它允许开发者在不修改原有代码的情况下,通过添加额外的逻辑来实现横切关注点(cross-cutting concerns)的功能。

Spring 的 AOP 基于代理模式实现。它通过动态代理或者字节码生成技术,在运行时为目标对象生成一个代理对象,并将切面逻辑织入到代理对象的方法调用中。当应用程序调用代理对象的方法时,切面逻辑会在目标方法执行前、执行后或抛出异常时被触发执行。

在 Spring 中,默认情况下它就支持了两种动态代理方式。如果你指定的目标类实现了接口,Spring 就会自动选择 JDK 的动态代理。而如果目标类没有实现接口,则 Spring 会使用 CGLIB。而在 SpringBoot 中,默认使用的是 CGLIB。

关于 Spring AOP 的详细介绍,可以查看这篇文章:《Spring AOP:解锁切面编程的威力与实践》。

我们已经了解到,Spring 的 AOP 本质上就是动态代理,那么 Spring 是通过什么来应用这个动态代理技术的呢?回想之前学习的内容,我们知道 BeanPostProcesser 可以对创建的 Bean 进行加工,而 Spring 的 AOP 正好就是用 BeanPostProcesser 来实现的,它就是 AnnotationAwareAspectJAutoProxyCreator。

Spring 基于注解的 AOP 编程,我们需要在配置类中标记 @EnableAspectJAutoProxy 注解,在 SpringBoot 中我们不需要手动指定,因为 SpringBoot 底层已经进行了封装。如果我们要强制 Spring 每次都使用 CGLIB,那么需要在这个注解添加属性 proxyTargetClass = true

为什么我们指定了 @EnableAspectJAutoProxy 就可以了?我们知道 AOP 是通过 AnnotationAwareAspectJAutoProxyCreator 这个 BeanPostProcesser 来实现的,那么 @EnableAspectJAutoProxy 底层肯定也是通过 @Import 技术引入了这个类。这样就不需要我们自己引入,屏蔽了 Spring 的 AOP 实现细节,语义化的处理也更加的通俗。

@EnableAspectJAutoProxy 还有一个属性 exposeProxy,默认为 false,此参数控制是否将代理对象放入 ThreadLocal,以方便后续获取,解决代理对象的方法中调用本来其他方法代理失效的问题。什么时候设置的?可以参考 JdkDynamicAopProxy.invoke 方法

我们需要在代理类中配置:@EnableAspectJAutoProxy(exposeProxy=true),随后在代理的原始方法调用其他方法的时候,通过 AopContext.currentProxy() 获取本类的代理对象,然后调用方法。

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
@ComponentScan("world.xuewei.aopproxy")
public class AopProxyConfig {

}
@Service
public class UserService implements IService {

 @Override
 public void m1() {
     System.out.println("UserService.m1");
     // 注意这里,实现接口默认是 JDK 动态代理,这里获取的代理对象的类型用接口类型接收
     IService service = (IService) AopContext.currentProxy();
     // 如果是 CGLIB 动态代理,则可以使用原始对象类型接收,同时需要指定 proxyTargetClass = true
     // UserService service = (UserService) AopContext.currentProxy();
     service.m2();
 }

 @Override
 public void m2() {
     System.out.println("UserService.m2");
 }
}

手写 Spring AOP

基于前面的了解,我们手写一个简单的实现原理,其实就是创建一个 BeanPostProcesser。这个 BeanPostProcesser 通常是在初始化完成后去执行的。当然也不一定,也有可能在处理循环引用的时候提前就执行了。

定义接口和原始类

public interface IStudentService {
    void test1();
}

@Service
public class StudentServiceImpl implements IStudentService {

    @Override
    public void test1() {
        System.out.println("StudentServiceImpl.test1");
    }
}

定义代理 BeanPostProcessor

public class MyProxyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 基于 CGLIB 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
            // 在此处编写前置操作
            System.out.println("Before");
            Object result = null;
            try {
                result = method.invoke(bean, objects);
            } catch (Exception e) {
                // 在此处编写异常操作
                System.out.println("Exception");
            }
            // 在此处编写后置操作
            System.out.println("After");
            // 可以在此处编写新的返回值并返回
            return result;
        });
        return enhancer.create();
    }
}

定义 AOP 开启注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyProxyBeanPostProcessor.class)
public @interface EnableMyProxy {

}

定义配置类

@EnableMyProxy
@Configuration
@ComponentScan("world.xuewei.proxy")
public class ProxyAppConfig {
    
}

定义测试类

public class ProxyTest {

    private ApplicationContext context;

    @Before
    public void before() {
        // 指定配置类,创建 Spring 工厂
        context = new AnnotationConfigApplicationContext(ProxyAppConfig.class);
    }

    /**
     * 测试 AOP
     */
    @Test
    public void test1() {
        StudentServiceImpl service = context.getBean("studentServiceImpl", StudentServiceImpl.class);
        service.test1();
    }
}

[外链图片转存中…(img-Ly5hHRy3-1722864552441)]

通过上面的编码,我们已经实现了最简单的 AOP,但是会存在一些问题:

  1. 我们通过 MyProxyBeanPostProcessor 为所有类的所有方法都进行了代理,这里显然不够灵活。
  2. 我们通过 MyProxyBeanPostProcessor 在增加额外功能的时候,增强位置和增强逻辑都是写死在代理中的,这里显然不够灵活。

此处我们可以利用 Spring 提供的 PointCut 切入点接口,对我们上面的 AOP 实现进行升级。

升级版 Spring AOP

定义自定义切入点配置

主要解决之前为所有类的所有方法都进行了代理,不够灵活的问题。

@Component
public class MyPointCut implements Pointcut {

    /**
     * 类型过滤器,判定当前的类型是否满足 AOP 要求
     */
    @Override
    public ClassFilter getClassFilter() {
        return clazz -> {
            // 只为 StudentServiceImpl 类做代理
            return StudentServiceImpl.class.isAssignableFrom(clazz);
        };
    }

    /**
     * 方法过滤器,判定当前的方法是否满足 AOP 要求
     */
    @Override
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return matches(method, targetClass, null);
            }

            @Override
            public boolean isRuntime() {
                return true;
            }

            @Override
            public boolean matches(Method method, Class<?> targetClass, Object... args) {
                // 只为 test2 方法进行增强
                return "test2".equals(method.getName());
            }
        };
    }
}

改造代理 BeanPostProcessor

public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 获取自定义的切入点
        Pointcut pointcut = beanFactory.getBean(Pointcut.class);
        ClassFilter classFilter = pointcut.getClassFilter();
        MethodMatcher methodMatcher = pointcut.getMethodMatcher();
        // 基于 CGLIB 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
            Object result = null;
            if (classFilter.matches(o.getClass()) && methodMatcher.matches(method, o.getClass())) {
                // 满足条件,添加增强逻辑
                // 在此处编写前置操作
                System.out.println("Before");
                try {
                    result = method.invoke(bean, objects);
                } catch (Exception e) {
                    // 在此处编写异常操作
                    System.out.println("Exception");
                }
                // 在此处编写后置操作
                System.out.println("After");
                // 可以在此处编写新的返回值并返回
            } else {
                // 不满足条件直接执行原始方法
                result = method.invoke(bean, objects);
            }
            return result;
        });
        return enhancer.create();
    }
}

使用一系列回调接口

MethodBeforeAdvice、AfterReturningAdvice

主要解决在增加额外功能的时候,增强位置和增强逻辑都是写死在代理中的,不够灵活的问题。

@Component
public class MyAdviceBefore implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Before");
    }
}
@Component
public class MyAdviceAfter implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
        System.out.println("After");
    }
}
public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 获取自定义的切入点
        Pointcut pointcut = beanFactory.getBean(Pointcut.class);
        ClassFilter classFilter = pointcut.getClassFilter();
        MethodMatcher methodMatcher = pointcut.getMethodMatcher();
        // 获取一系列回调接口
        MethodBeforeAdvice beforeAdvice = beanFactory.getBean(MethodBeforeAdvice.class);
        AfterReturningAdvice afterReturningAdvice = beanFactory.getBean(AfterReturningAdvice.class);
        // 基于 CGLIB 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
            Object result = null;
            if (classFilter.matches(o.getClass()) && methodMatcher.matches(method, o.getClass())) {
                // 满足条件,添加增强逻辑
                // 回调前置增强方法
                beforeAdvice.before(method, objects, o.getClass());
                try {
                    result = method.invoke(bean, objects);
                } catch (Exception e) {
                    // 在此处编写异常操作
                    System.out.println("Exception");
                }
                // 回调后置增强方法
                afterReturningAdvice.afterReturning(result, method, objects, o.getClass());
            } else {
                // 不满足条件直接执行原始方法
                result = method.invoke(bean, objects);
            }
            return result;
        });
        return enhancer.create();
    }
}

包装自定义切面

在 Spring 中,切入点和额外功能组成了切面(通知),相当于对这两者再次进行了封装,所以上面我们在 BeanPostProcessor 中直接使用切入点和额外功能还是有点生硬,不太合理。

@Component
public class MyPointCutAdvisor implements PointcutAdvisor, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Pointcut getPointcut() {
        return new MyPointCut();
    }

    @Override
    public Advice getAdvice() {
        return beanFactory.getBean(Advice.class);
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }

}
public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 获取自定义的切入点
        PointcutAdvisor pointcutAdvisor = beanFactory.getBean(PointcutAdvisor.class);
        Pointcut pointcut = pointcutAdvisor.getPointcut();
        Advice advice = pointcutAdvisor.getAdvice();
        // 基于 CGLIB 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
            Object result = null;
            if (pointcut.getClassFilter().matches(o.getClass()) && pointcut.getMethodMatcher().matches(method, o.getClass())) {
                // 满足条件,添加增强逻辑
                if (advice instanceof BeforeAdvice) {
                    // 如果当前通知是前置通知,则回调前置增强方法
                    MethodBeforeAdvice beforeAdvice = (MethodBeforeAdvice) advice;
                    beforeAdvice.before(method, objects, o.getClass());
                }
                result = method.invoke(bean, objects);
                if (advice instanceof AfterAdvice) {
                    // 如果当前通知是后置通知,则回调后置增强方法
                    AfterReturningAdvice afterReturningAdvice = (AfterReturningAdvice) advice;
                    afterReturningAdvice.afterReturning(result, method, objects, o.getClass());
                }
            } else {
                // 不满足条件直接执行原始方法
                result = method.invoke(bean, objects);
            }
            return result;
        });
        return enhancer.create();
    }
}

这里我们定义的只是单一切面,单个切入点和单个额外功能,如果要完善的话就可以参考 Spring AOP,将切面组成链路,其实就是遍历执行。

public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

    private ListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 如果当前是一个代理对象,则直接返回
        if (bean instanceof Advisor) {
            return bean;
        }
        // 获取所有的通知增强器
        String[] pointcutAdvisorNames = beanFactory.getBeanNamesForType(PointcutAdvisor.class);
        List<PointcutAdvisor> pointcutAdvisors = Arrays.stream(pointcutAdvisorNames)
                .map(name -> beanFactory.getBean(name, PointcutAdvisor.class))
                .collect(Collectors.toList());
        List<PointcutAdvisor> beforeAdvisors = pointcutAdvisors.stream()
                .filter(pointcutAdvisor -> pointcutAdvisor.getAdvice() instanceof BeforeAdvice)
                .collect(Collectors.toList());
        List<PointcutAdvisor> afterAdvisors = pointcutAdvisors.stream()
                .filter(pointcutAdvisor -> pointcutAdvisor.getAdvice() instanceof AfterAdvice)
                .collect(Collectors.toList());
        // 基于 CGLIB 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
            Object result = null;
            for (PointcutAdvisor beforeAdvisor : beforeAdvisors) {
                Pointcut pointcut = beforeAdvisor.getPointcut();
                if (pointcut.getClassFilter().matches(o.getClass()) && pointcut.getMethodMatcher().matches(method, o.getClass())) {
                    // 满足条件,添加增强逻辑
                    Advice advice = beforeAdvisor.getAdvice();
                    // 如果当前通知是前置通知,则回调前置增强方法
                    MethodBeforeAdvice beforeAdvice = (MethodBeforeAdvice) advice;
                    beforeAdvice.before(method, objects, o.getClass());
                }
            }
            result = method.invoke(bean, objects);
            for (PointcutAdvisor afterAdvisor : afterAdvisors) {
                Pointcut pointcut = afterAdvisor.getPointcut();
                if (pointcut.getClassFilter().matches(o.getClass()) && pointcut.getMethodMatcher().matches(method, o.getClass())) {
                    // 满足条件,添加增强逻辑
                    Advice advice = afterAdvisor.getAdvice();
                    // 如果当前通知是前置通知,则回调前置增强方法
                    AfterReturningAdvice afterReturningAdvice = (AfterReturningAdvice) advice;
                    afterReturningAdvice.afterReturning(result, method, objects, o.getClass());
                }
            }
            return result;
        });
        return enhancer.create();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值