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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 自己动手编写CSDN博客备份工具-blogspider源码下载(4) .

    自己动手编写CSDN博客备份工具-blogspider源码下载(4) .作者:gzshun.原创作品,转载请标明出处!来源:http://blog.csdn.net/gzshun本博文需要声明几点:1.blogspider_v1.0源码已经更新,文件名为”blogspider_v2.0.tar.bz2″,下载地址在下文。2.该备份工具用其他语言(比如Java,PHP)可能比较简单,但本人只会C/C++。3.blogspider_v1.0源码报

    2022年7月25日
    11
  • 终于懂了TCP和UDP协议区别[通俗易懂]

    终于懂了TCP和UDP协议区别[通俗易懂]终于懂了TCP和UDP协议区别

    2022年6月7日
    31
  • vscode取消注释的快捷键_vscode解除注释

    vscode取消注释的快捷键_vscode解除注释注释CTRL+K+C取消注释CTRL+K+U

    2022年8月15日
    5
  • 硬件SPI控制ST7789V「建议收藏」

    文章目录前言一、能用的方法有那些?1.利用硬件UART模拟SPI,实现9位数据发送2.搭建一个数字逻辑电路,人为实现9位spi发送3.发送两组8位SPI二、具体操作1.具体实现总结前言这段时间闲着没事,决定自己做个手表玩玩,参考了一些开源项目和数据手册,决定主控使用STM32F411CEU6,屏幕选用了淘宝一家商家的ST7789V主控的240*240分辨率的圆形tft屏幕。利用商家给的软件spi例程成功将屏幕点亮之后,尝试使用改为硬件spi的方式,加快屏幕的刷新速度但是尝试了多种方案后始终无法点亮,

    2022年4月9日
    605
  • 完美解决:针对tensorflow中,tf.logging.set_verbosity(tf.logging.ERROR)问题。

    完美解决:针对tensorflow中,tf.logging.set_verbosity(tf.logging.ERROR)问题。tf.logging.set_verbosity(tf.logging.ERROR)代码作用:让tensorflow只讲错误信息进行记录。因为Tensorflow2.0移除了一些API,其中就包括logging属性。所以如果你用tensorflow2.0的话,请参考下文解决。解决方法:将此代码更换为:tf.compat.v1.logging.set_verbosity(tf.compat…

    2025年6月14日
    3
  • idea社区版_idea社区版够用吗

    idea社区版_idea社区版够用吗IDEA官网地址:https://www.jetbrains.com/idea/download/#section=windows下载社区版后,点击安装,就进行傻瓜式的安装了。以上两个步骤中有一个点击next的时候时间会稍稍有点久,耐心等待一下就好了。点击安装,IDEA社区版就安装完成了,安装好之后打开IDEA工具,会有如下提示:我是第一次使用,直接选择Donotimportsettings就行了。到这里就进入IDEA工具了,选择你喜欢的主题风格,当然这还不算正式的完成,我

    2025年11月29日
    7

发表回复

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

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