Spring AOP中动态代理的两种实现方式及其过程_spring动态代理原理

Spring AOP中动态代理的两种实现方式及其过程_spring动态代理原理什么是代理?指为一个目标对象提供一个代理对象,并由代理对象控制对目标对象的引用.使用代理对象,是为了在不修改目标对象的基础上,增强目标对象的业务逻辑.静态代理静态代理的特点是,为每一个业务增强都提供一个代理类,由代理类来创建代理对象.下面我们通过静态代理来实现对转账业务进行身份验证.(1)转账业务publicinterfaceIAccountService{…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

什么是代理?

指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上, 增强目标对象的业务逻辑.
在这里插入图片描述

静态代理

静态代理的特点是, 为每一个业务增强都提供一个代理类, 由代理类来创建代理对象. 下面我们通过静态代理来实现对转账业务进行身份验证.

(1) 转账业务

public interface IAccountService {
    //主业务逻辑: 转账
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 代理类

public class AccountProxy implements IAccountService {
    //目标对象
    private IAccountService target;

    public AccountProxy(IAccountService target) {
        this.target = target;
    }

    /**
     * 代理方法,实现对目标方法的功能增强
     */
    @Override
    public void transfer() {
        before();
        target.transfer();
    }

    /**
     * 前置增强
     */
    private void before() {
        System.out.println("对转账人身份进行验证.");
    }
}

(3) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        IAccountService target = new AccountServiceImpl();
        //创建代理对象
        AccountProxy proxy = new AccountProxy(target);
        proxy.transfer();
    }
}

结果: 
对转账人身份进行验证.
调用dao层,完成转账主业务.

动态代理

静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象, 而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成.

JDK动态代理

JDK动态代理是使用 java.lang.reflect 包下的代理类来实现. JDK动态代理动态代理必须要有接口.

(1) 转账业务

public interface IAccountService {
    //主业务逻辑: 转账
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 增强

因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.

public class AccountAdvice implements InvocationHandler {
    //目标对象
    private IAccountService target;

    public AccountAdvice(IAccountService target) {
        this.target = target;
    }

    /**
     * 代理方法, 每次调用目标方法时都会进到这里
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        return method.invoke(target, args);
    }

    /**
     * 前置增强
     */
    private void before() {
        System.out.println("对转账人身份进行验证.");
    }
}

(3) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        IAccountService target = new AccountServiceImpl();
        //创建代理对象
        IAccountService proxy = (IAccountService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new AccountAdvice(target)
        );
        proxy.transfer();
    }
}
结果: 
对转账人身份进行验证.
调用dao层,完成转账主业务.

CGLIB动态代理

JDK动态代理必须要有接口, 但如果要代理一个没有接口的类该怎么办呢? 这时我们可以使用CGLIB动态代理. CGLIB动态代理的原理是生成目标类的子类, 这个子类对象就是代理对象, 代理对象是被增强过的.

注意: 不管有没有接口都可以使用CGLIB动态代理, 而不是只有在无接口的情况下才能使用.

(1) 转账业务

public class AccountService {
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 增强

因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.

public class AccountAdvice implements MethodInterceptor {
    /**
     * 代理方法, 每次调用目标方法时都会进到这里
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        return methodProxy.invokeSuper(obj, args);
        //        return method.invoke(obj, args);  这种也行
    }

    /**
     * 前置增强
     */
    private void before() {
        System.out.println("对转账人身份进行验证.");
    }
}

(3) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        AccountService target = new AccountService();
        //
        //创建代理对象
        AccountService proxy = (AccountService) Enhancer.create(target.getClass(),
                new AccountAdvice());
        proxy.transfer();
    }
}
结果: 
对转账人身份进行验证.
调用dao层,完成转账主业务.

模拟Spring AOP场景

了解了动态代理后, 我们就可以自己来实现Spring AOP功能了, 所以下面我们来模拟下Spring AOP场景.

(1) 转账业务

public interface IAccountService {
    //主业务逻辑: 转账
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 切面抽象类

定义一个切面抽象类, 该类使用了模板方法的设计模式, 为开始, 结束, 异常, 前置增强, 后置增强提供了默认实现, 当我们定义切面类时只需要按需重写它们就行. isIntercept() 方法用来判断切入点是否正确, 切面类需要重写这个方法.

public abstract class BaseAspect implements MethodInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(BaseAspect.class);

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;

        begin();
        try {
            if (isIntercept(method, args)) {
                before();
                result = methodProxy.invokeSuper(obj, args);
                after();
            } else {
                result = methodProxy.invokeSuper(obj,args);
            }
        } catch (Exception e) {
            logger.error("proxy failure", e);
            error(e);
            throw e;
        } finally {
            end();
        }
        return result;
    }

    /**
     * 开始增强
     */
    public void begin() {
    }

    /**
     * 切入点判断
     */
    public boolean isIntercept(Method method, Object[] args) throws Throwable {
        return true;
    }

    /**
     * 前置增强
     */
    public void before() throws Throwable {
    }

    /**
     * 后置增强
     */
    public void after() throws Throwable {
    }

    /**
     * 异常增强
     */
    public void error(Throwable e) {
    }

    /**
     * 最终增强
     */
    public void end() {
    }
}

(3) 切面类

创建一个切面类, 类中配置切入点和增强.

public class AccountAspect extends BaseAspect {

    /**
     * 切入点
     */
    public boolean isIntercept(Method method, Object[] args) throws Throwable {
        return method.getName().equals("transfer");
    }

    /**
     * 前置增强
     */
    public void before() throws Throwable {
        System.out.println("对转账人身份进行验证.");
    }
}

(4) 代理工厂类

定义一个工厂类来创建代理, 其实不创建这个类也行, 但为了模仿Spring还是创建了. @SuppressWarnings是为了抑制警告, 就是编译器上面的黄线.

public class ProxyFactory {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) {
        return (T) Enhancer.create(targetClass,methodInterceptor);
    }
}

(5) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        IAccountService target = new AccountServiceImpl();
        //切面
        BaseAspect accountAspect = new AccountAspect();
        //创建代理对象
        IAccountService proxy = (IAccountService) ProxyFactory.createProxy(target.getClass(), accountAspect);
        proxy.transfer();
    }
}
结果:
对转账人身份进行验证.
调用dao层,完成转账主业务.
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/169601.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux网卡驱动怎么看_电脑网卡驱动在哪里查看

    linux网卡驱动怎么看_电脑网卡驱动在哪里查看Linux下查看网卡驱动和版本信息查看网卡生产厂商和信号查看基本信息:lspci查看详细信息:lspci-vvv#3个小写的v查看网卡信息:lspci|grepEthernet查看网卡驱动查看网卡驱动信息:lspci-vvv#找到网卡设备的详细信息,包括网卡驱动列出加载的所有驱动,包括网卡驱动:lsmod查看模块

    2022年10月18日
  • 小米BL不解锁刷机「建议收藏」

    小米BL不解锁刷机「建议收藏」关于小米NOTE顶配近期解锁的问题中发现还有很多人不会用9008模式刷机,现出个简单教程方便米粉们救砖。硬件:小米NOTE顶配手机win10系统的电脑手机与电脑相连的数据线软件:老版本的miflash(版本号:2015.10.28。0)最新版本miflash最新解BL锁工具或者小米助手(用来装驱动)开发版6.17及以前的线刷包随便下一个一个你最终想刷的版本(推荐最新稳定版)…

    2022年4月30日
  • 关于各种无法解析的外部符号问题的相应解决方案

    关于各种无法解析的外部符号问题的相应解决方案在使用vs2008调试程序的过程中,经常会出现无法解析的外部符号问题,可能的原因有很多种,下面这些是我一年来积累的经验.仅供参考.考虑可能的原因:[0]出现无法解析可能是因为lib文件不正确,比如64位的编译配置,结果使用的是32位的lib包.[1]只写了类声明,但还没有写实现类,造成调用时无法解析[2]声明和定义没有统一,造成链接不一致,无法解析[3]没有在项目属性页的链接器的

    2022年6月28日
  • 如何一键远程开机,远程唤醒功能[通俗易懂]

    如何一键远程开机,远程唤醒功能[通俗易懂]使用ToDesk可以在千里之外为您的设备远程进行开机操作视频教程:https://update.todesk.com/wol.mp4ToDesk支持将关机状态下的设备(Windows,macOS,Linux)唤醒开机.这需要满足2个条件:1.开启电脑的网卡WakeOnLAN功能2.您要开机的电脑设备在同一交换机(路由器下),需要有另外一个ToDesk端在运行.比如其他的电脑或手机,iPad,Android电视盒子,或家人的手机安装一个ToDesk,这样您就可以在千里之外为您的电脑

    2022年6月2日
  • BT 种子 tracker 磁链

    BT 种子 tracker 磁链传统的种子和磁链的区别主要参考:https://www.quora.com/Whats-the-difference-between-magnet-links-and-regular-torrent-files-on-tracker-siteshttps://www.zhihu.com/question/36151980(很多回答,但是都没有说清楚)在使用迅雷时,磁链会先下载一个…

    2022年4月29日
  • .jar中没有主清单属性[通俗易懂]

    .jar中没有主清单属性[通俗易懂].jar中没有主清单属性问题:xxx.jar中没有主清单属性背景:maven项目,springboot服务   IDEA打包,jar包运行出现上述错误解决方案:pom.xml中添加 <build><plugins><plugin><groupId>org.sp…

    2022年9月5日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号