专栏首页java干货Small Spring系列十一:aop (四)

Small Spring系列十一:aop (四)

回眸一笑百媚生,六宫粉黛无颜色。

概述

在前四篇,我们已经实现了使用Cglib实现了aop动态代理。但是在spring中如果代理对象实现了接口,则默认使用jdk动态代理,也可以通过配置强制使用cglib代理。本篇,我们使用jdk动态代理来完善aop

准备工作

INioCoderService

新增接口类,因为使用jdk动态代理,代理对象必须实现接口。

package com.niocoder.service.v6;

/**
 * 测试 JDK 动态代理
 */
public interface INioCoderService {

    void placeOrder();
}

NioCoderService

实现INioCoderService接口。

@Component(value = "nioCoder")
public class NioCoderService implements INioCoderService {

    public NioCoderService() {

    }


    public void placeOrder() {
        System.out.println("place order");
        MessageTracker.addMsg("place order");
    }

    public void placeOrderV2() {
        System.out.println("no interception");
    }
}

bean-v6.XML

设置Pointcutv6包下面的placeOrder方法。

<?xml version="1.0" encoding="UTF-8"?>
<!-- 增加namespace-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop">

    <!-- 扫描哪个包下面的文件 -->
    <context:component-scan base-package="com.niocoder.service.v6">

    </context:component-scan>

    <!-- 模拟 TransactionManager-->
    <bean id="tx" class="com.niocoder.tx.TransactionManager"/>

    <!-- aop 配置-->
    <aop:config>
        <!-- aop 核心配置 依赖tx-->
        <aop:aspect ref="tx">
            <!-- 切入点配置 包下面的placeOrder 方法-->
            <aop:pointcut id="placeOrder"
                          expression="execution(* com.niocoder.service.v6.*.placeOrder(..))"/>
            <!-- 通知配置,-->
            <aop:before pointcut-ref="placeOrder" method="start"/>
            <aop:after-returning pointcut-ref="placeOrder" method="commit"/>
            <aop:after-throwing pointcut-ref="placeOrder" method="rollback"/>
        </aop:aspect>

    </aop:config>
</beans>

创建jdk代理工厂

根据类图aop 动态获取代理实例 创建JdkAopProxyFactory代理工厂。

JdkAopProxyFactory

package com.niocoder.aop.framework;

import com.niocoder.aop.Advice;
import com.niocoder.util.Assert;
import com.niocoder.util.ClassUtils;
import lombok.extern.java.Log;
import org.aopalliance.intercept.MethodInterceptor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zhenglongfei
 */
@Log
public class JdkAopProxyFactory implements AopProxyFactory, InvocationHandler {

    private final AopConfig config;

    public JdkAopProxyFactory(AopConfig config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if (config.getAdvices().size() == 0) {
            throw new AopConfigException("No advices specified");
        }
        this.config = config;
    }

    @Override
    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {

        log.info("Creating JDK dynamic proxy: target source is " + this.config.getTargetObject());

        Class<?>[] proxiedInterfaces = config.getProxiedInterfaces();

        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

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

        Object target = this.config.getTargetObject();
        Object retVal;
        List<Advice> chain = this.config.getAdvices(method);
        if (chain.isEmpty()) {
            retVal = method.invoke(target, args);
        } else {
            List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>();
            interceptors.addAll(chain);
            // We need to create a method invocation...
            retVal = new ReflectiveMethodInvocation(target, method, args, interceptors).proceed();
        }

        return retVal;
    }
}

AspectJAutoProxyCreator

AspectJAutoProxyCreatorcreateProxy方法中添加JDK代理支持。

public class AspectJAutoProxyCreator implements BeanPostProcessor {
  ......
  protected Object createProxy(List<Advice> advices, Object bean) {
       AopConfigSupport config = new AopConfigSupport();
       for (Advice advice : advices) {
           config.addAdvice(advice);
       }

       Set<Class> targetInterfaces = ClassUtils.getAllInterfacesForClassAsSet(bean.getClass());
       for (Class<?> targetInterface : targetInterfaces) {
           config.addInterface(targetInterface);
       }

       config.setTargetObject(bean);

       AopProxyFactory proxyFactory = null;
       if (config.getProxiedInterfaces().length == 0) {
           proxyFactory = new CglibProxyFactory(config);
       } else {
           // JDK 代理
           proxyFactory = new JdkAopProxyFactory(config);
       }

       return proxyFactory.getProxy();
   }
  ......
}

ApplicationContextTest

测试jdk动态代理。

public class ApplicationContextTest {
    @Before
    public void setUp() {
        MessageTracker.clearMsgs();
    }

    @Test
    public void testGetBeanProperty() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-v6.xml");
        INioCoderService nioCoderService = (INioCoderService) ctx.getBean("nioCoder");

        nioCoderService.placeOrder();

        List<String> msgs = MessageTracker.getMsgs();

        Assert.assertEquals(3, msgs.size());
        Assert.assertEquals("start tx", msgs.get(0));
        Assert.assertEquals("place order", msgs.get(1));
        Assert.assertEquals("commit tx", msgs.get(2));
    }
}

输出:

start tx
place order
commit tx

代码下载

代码下载

参考资料


从零开始造Spring

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Small Spring系列八:aop (一)

    我们终于不辱使命完成了Spring的注解注入,接下来我们要实现更为关键aop部分,在这开始之前你需要了解什么事aop以及aop的常用术语,参考链接

    java干货
  • Small Spring系列九:aop (二)

    在Small Spring系列八:aop (一)中,我们实现了Pointcut和MethodLocatingFactory,Pointcut根据给定一个类的方法...

    java干货
  • Small Spring系列一:BeanFactory(一)

    Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。

    java干货
  • 《Spring 手撸专栏》| 开篇介绍,我要带新人撸 Spring 啦!

    是的,在写了4篇关于Spring核心源码的面经内容后,我决定要去手撸一个Spring了。为啥这么干呢?因为所有我想写的内容,都希望它是以理科思维理解为目的的学会...

    小傅哥
  • 使用aop统一处理controller中的异常及日志

    在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技...

    呼延十
  • 《Spring 手撸专栏》第 13 章:行云流水,把AOP动态代理,融入到Bean的生命周期

    在电视剧《楚汉传奇》中有这么一段刘邦与韩信的饮酒对话,刘邦问韩信我那个曹参读过书见过世面能带多少兵,韩信说能带一万五,又补充说一万五都吃力。刘邦又一一说出樊哙、...

    小傅哥
  • Small Spring系列二:BeanFactory(二)

    在Small Spring系列一:BeanFactory(一)中,我们用DefaultBeanFactory读取bean.xlm中的bean信息,并且也实现了B...

    java干货
  • 手写Spring框架,是时候撸个AOP与Bean生命周期融合了!

    在电视剧《楚汉传奇》中有这么一段刘邦与韩信的饮酒对话,刘邦问韩信我那个曹参读过书见过世面能带多少兵,韩信说能带一万五,又补充说一万五都吃力。刘邦又一一说出樊哙、...

    小傅哥
  • small-spring 代码贡献者3个月,敢说精通Spring了,分享我的总结!

    这个与我们码农朝夕相处的 Spring,就像睡在你身边的媳妇,你知道找她要吃、要喝、要零花钱、要买皮肤。但你不知道她的仓库共有多少存粮、也不知道她是买了理财还是...

    小傅哥
  • 目录:SpringBoot 核心技术

    恒宇少年
  • 32天高效突击:开源框架+性能优化+微服务架构+分布式,面阿里获P7(脑图、笔记、面试考点全都有)

    今年似乎因为疫情影响,时间过得特别快,对于需要跳槽换工作的人来,更觉得有些突然,似乎金三银四和金九银四还没开始准备好,就匆匆过去。加上今年的大环境不佳,所以大部...

    Java程序猿阿谷
  • Spring系列十四: Spring @Component, @Controller, @Repository and @Service

    在spring自动装配中,@Autowired注解只处理连接部分。我们仍然需要定义bean,以便容器能够识别它们并为我们注入它们。

    java干货
  • spirng底层实现原理

    用户2146856
  • spirng底层实现原理

    用户2146856
  • Spring Boot2 系列教程(四十四)Spring Session 中的 Bug

    真是郁闷,不过这事又一次提醒我解决问题还是要根治,不能囫囵吞枣,否则相同的问题可能会以不同的形式出现,每次都得花时间去搞。刨根问底,一步到位,再遇到类似问题就可...

    江南一点雨
  • Spring系列之初识Spring Spring概述

    Spring当然不是上面那个Spring,Spring之所以命名为Spring是因为这个开源的轻量级的开源框架的出现给软件行业带来了春天,促进了软件行业的发...

    一只胡说八道的猴子
  • Spring_总结_01_Spring概述

    从今天开始,重新总结一下Spring的相关知识,主要是结合《Spring实战(第四版)》和 《JavaEE开发的颠覆者——SpringBoot实战》这两本书以及...

    shirayner
  • SpringBoot入门建站全系列(三十六)AspectJ做AOP日志管理

    Spring的两大特性,AOP和IOC,AOP面向切面编程,可以对当前代码无侵入的情况下,使用AspectJ对切点数据进行分析存储。常常被用来做日志/流水的存储...

    品茗IT
  • Spring通过XML配置文件以及通过注解形式来AOP 来实现前置,后置,环绕,异常通知

    AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,AOP实际是GoF设计模式的延续

    yaphetsfang

扫码关注云+社区

领取腾讯云代金券