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层,完成转账主业务.
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • jenkinsfile docker_dockerfile制作镜像

    jenkinsfile docker_dockerfile制作镜像前言之前我们用docker手动安装了jenkins环境,在jenkins中又安装了python3环境和各种安装包,如果我们想要在其他3台机器上安装,又是重复操作,重复劳动,那会显得很low,这里可以

  • 电子元器件常用品牌汇总(持续更新)

    电子元器件常用品牌汇总(持续更新)电阻:Yageo国巨、Fenghua风华、Rohm罗姆、TDK、Samsung三星、Uniohm厚声、Walsin华新科、Ralec旺诠、KOA兴亚、Panasonic松下、AVX、TMTEC泰铭、Kyocera京瓷、PHYCOM飞元。电容:Yageo国巨、Fenghua风华、Murata村田、TDK、Samsung三星、Eyang宇阳、Taiyo太诱、Kyocera京瓷、HEC禾伸堂、Kemet基美、ISND华信安、AVX、Nichicon尼吉康、Panasonic松下、SANYO三洋。电感:Mura

  • mvc页面传值的几种方式_netuse

    mvc页面传值的几种方式_netuse1、比如Request.Form["ddlType"]为control中需要模拟的内容2、在单元测试中调用的代码(使用的是VS2010自带的单元测试框架)         varrequest=newMock&lt;HttpRequestBase&gt;();//request.Setup(r=&gt;r.HttpMethod)….

  • 《渔歌子》之景

    《渔歌子》之景

  • 计算机机房整改工作报告,机房整改总结.doc

    计算机机房整改工作报告,机房整改总结.docXX机房整改方案目录一、XX站整改项目说明3二、XX站整改项目目标3三、项目实施要求41、可靠性:42、环境保护:43、灵活性:44、安全性:4四、施工方案简述4第一部分机房工程方案4第二部分:机房装修设计及施工方案6第三部分机房外缆整改及设备支座制作8五、机房综合工程设计标准10六、效果图11XX站整改项目说明本次XX站机房整改项目主要涉及XXX二楼机房、话务机房、电源机房等机房的整体维护。…

  • 字符串转小数 java(java中string转int)

    javastring小数点转long[2021-01-3019:30:55]简介:在php中可以直接使用“floatval”函数去除小数点后的0,floatval函数的语法是“floatfloatval(mixed$var)”,使用该函数转换既可以保留有效的小数点,也可以去掉后面多余的0php保留小数点后两位且不四舍五入的方法:使用函数substr,代码为【echosprin…

发表回复

您的电子邮箱地址不会被公开。

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