spring 动态代理

这篇具有很好参考价值的文章主要介绍了spring 动态代理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

感觉不错 三连支持 一起进步!

动态代理



动态代理:

● 特点:字节码随用随创建,随用随加载
● 作用:不修改源码的基础上对方法增强
● 分类:
○ 基于接口的动态代理
○ 基于子类的动态代理

一、基于接口的动态代理

基于接口的动态代理,要求被代理对象的类最少实现一个接口,否则不能创建代理对象。

● 涉及的类:java.lang.reflect.Proxy
● 提供者:jdk官方

如何创建代理对象:使用Proxy的newProxyInstance方法

创建代理对象的要求:被代理的类最少实现一个接口,如果没有则不能创建。

newProxyInstance方法的参数:

● classLoader:类加载器
它是用于加载代理对象字节码文件的,和被代理对象使用相同的类加载器。是固定写法。

● Class[]:字节码数组
它是用于让代理对象和被代理对象有相同的方法。固定的写法。

● invocationHandler:用于提供增强的代码
它是让我们写如何代理。一般都是写一个该接口的实现类。通常情况下都是匿名内部类,但不是必须。此接口的实现类都是谁用谁写

IProducer接口:

public interface IProducer {
    Float sellProduct(Float money);
    void afterSell(Float money);
}

IProducer的实现类Produer类

// 基于接口代理要求被代理类最少实现一个接口。如果此处不实现IProducer接口则无法创建被代理对象
public class Producer implements IProducer {
    public Float sellProduct(Float money) {
        System.out.println("销售商品");
        return money;
    }
    public void afterSell(Float money) {
        System.out.println("售后服务");
    }
}

编写main方法测试,并在main方法中加入代理:

    public static void main(String[] args) {
        final Producer producer = new Producer();
        
        // 返回的是Object类型对象,需要强转
        IProducer proxyProducer = 
            (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 执行被代理对象的任何方法都会经过此方法
                     * @param proxy 代理对象的引用(一般用不上)
                     * @param method 当前执行的方法
                     * @param args 当前执行方法的参数
                     * @return 和被代理对象方法有相同类型的返回值
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) 
                        throws Throwable {
                        // 编写增强的代码
                        Object result = null;
                        // 获取方法的传入参数
                        Float money = (Float) args[0];
                        // 判断方法名
                        if("sellProduct".equals(method.getName())) {
                            // 执行方法(此处代理将原本传入的金额打了8折)
                            result = method.invoke(producer, money * 0.8f);
                        }
                        return result;
                    }
                });

        // 通过创建的代理对象执行对应的方法
        Float result = proxyProducer.sellProduct(1000f);
        System.out.println(result);
    }

二、基于子类的动态代理

基于子类的动态代理:

● 涉及的类:Enhancer
● 提供者:第三方cglib

如何创建对象:使用Enhancer的create方法

创建代理对象的要求:被代理类不能是最终类

create方法的参数:

● class:字节码
指定被代理对象的字节码

● callback:提供增强的代码
我们一般写的都是该接口的子接口实现类:MethodInterceptor

pom中引入cglib:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.1_3</version>
</dependency>

编写Producer类:

// cglib不要求Producer实现接口,但是Producer类不能是final类
public class Producer{
    public Float sellProduct(Float money) {
        System.out.println("销售商品");
        return money;
    }

    public void afterSell(Float money) {
        System.out.println("售后服务");
    }
}

编写main方法测试,并在main方法中加入代理

public static void main(String[] args) {
        final Producer producer = new Producer();
		// 使用cglib创建代理对象,返回Object类型需要强转
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy 代理对象的引用(一般用不上)
             * @param method 当前执行的方法
             * @param args 当前执行方法传入的参数
             * @param methodProxy 当前执行方法的代理对象(一般用不上)
             * @return 和被代理对象有相同类型的返回值
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // 编写增强的代码
                Object result = null;
                // 获取方法的传入参数
                Float money = (Float) args[0];
                // 判断方法名
                if("sellProduct".equals(method.getName())) {
                    // 执行方法(此处代理将原本传入的金额打了8折)
                    result = method.invoke(producer, money * 0.8f);
                }
                return result;
            }
        });
        float money = cglibProducer.sellProduct(1000f);
        System.out.println(money);
    }

三、示例

动态代理改造service

1.改造前service

QueryRunner在构造方法中传入数据源,在每次执行sql时不再指定数据源,此时在每次执行sql时都会创建一个数据库连接并在执行完之后提交。对于转账交易这类需要执行多条更新sql的逻辑无法进行事务控制,需要对程序作出修改。

  1. 创建连接工具类,用于从数据库获取数据库连接,并实现和ThreadLocal线程的绑定
public class ConnectionUtil {

    private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
    // 添加set方法,使用xml配置Spring注入
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取当前线程上的连接
     */
    public Connection getThreadConnection() {
        try {
            Connection connection = threadLocal.get();
            if (connection == null) {
                connection = dataSource.getConnection();
                threadLocal.set(connection);
            }
            return connection;
        }catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和线程解绑
     */
    public void removeConnection() {
        threadLocal.remove();
    }
}
  1. 添加事务管理工具类,包括开启事务、提交事务、回滚事务、释放连接等操作。
public class TransactionManager {

    // 添加set方法,使用xml配置Spring注入
    private ConnectionUtil connectionUtil;
    public void setConnectionUtil(ConnectionUtil connectionUtil) {
        this.connectionUtil = connectionUtil;
    }

    /**
     * 开启事务
     */
    public void beginTransaction() {
        try {
            connectionUtil.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 提交事务
     */
    public void commit() {
        try {
            connectionUtil.getThreadConnection().commit();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 回滚事务
     */
    public void rollback() {
        try {
            connectionUtil.getThreadConnection().rollback();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 释放连接
     */
    public void release() {
        try {
            // close并不会真正把连接关闭,只是把该连接还回了连接池中
            connectionUtil.getThreadConnection().close();
            // 该ThreadLocal中的连接已经被还回连接池,下次该线程如果还使用当前连接对象就用不了了。
            // 所以在线程绑定的连接关闭后,线程要和连接进行解绑。下次该线程要使用连接时重新从连接池获取连接绑定到线程。
            connectionUtil.removeConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

3.修改dao层的QueryRunner操作,为每次sql执行指定数据库连接。文章来源地址https://www.toymoban.com/news/detail-478869.html

public class AccountDaoImpl implements IAccountDao {

    private QueryRunner runner;
    private ConnectionUtil connectionUtil;

    public void setConnectionUtil(ConnectionUtil connectionUtil) {
        this.connectionUtil = connectionUtil;
    }

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public List<Account> findAllAccount() {
        try {
            // 调用QueryRunner重载的带有数据源连接参数的query方法
            return runner.query(connectionUtil.getThreadConnection(), "select * from account", new BeanListHandler<Account>(Account.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public Account findAccountByName(String accountName) {
        try {
            List<Account> accounts = runner.query(connectionUtil.getThreadConnection(), "select * from account where name = ?", new BeanListHandler<Account>(Account.class), accountName);
            if(accounts == null || accounts.size() == 0) {
                return null;
            }
            if(accounts.size() > 1) {
                throw new RuntimeException("查询的账户数量不止一个");
            }
            return accounts.get(0);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
  1. 为service层的代码包裹事务。
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;
    private TransactionManager transactionManager;
    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public List<Account> findAllAccount() {
        try {
            transactionManager.beginTransaction();
            List<Account> accounts = accountDao.findAllAccount();
            transactionManager.commit();
            return accounts;
        } catch (Exception e) {
            transactionManager.rollback();
            throw new RuntimeException(e);
        } finally {
            transactionManager.release();
        }
    }

    public void transfer(String sourceName, String targetName, Float money) {
        try {
            transactionManager.beginTransaction();
            Account sourceAccount = accountDao.findAccountByName(sourceName);
            Account targetAccount = accountDao.findAccountByName(targetName);
            sourceAccount.setMoney(sourceAccount.getMoney() - money);
            targetAccount.setMoney(targetAccount.getMoney() + money);
            accountDao.updateAccount(sourceAccount);
            // int i = 1 / 0;
            accountDao.updateAccount(targetAccount);
            transactionManager.commit();
        } catch (Exception e) {
            e.printStackTrace();
            transactionManager.rollback();
        } finally {
            transactionManager.release();
        }
    }
}

2.使用动态代理重构带有事务的service示例

  1. 将AccountServiceImpl中每个方法因为事务管理添加的重复性代码移除
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;
    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }
    
    public void transfer(String sourceName, String targetName, Float money) {
        Account sourceAccount = accountDao.findAccountByName(sourceName);
        Account targetAccount = accountDao.findAccountByName(targetName);
        sourceAccount.setMoney(sourceAccount.getMoney() - money);
        targetAccount.setMoney(targetAccount.getMoney() + money);
        accountDao.updateAccount(sourceAccount);
        int i = 1 / 0;
        accountDao.updateAccount(targetAccount);
    }
}
  1. 编写BeanFactory类,作为service的代理对象的创建工厂
public class BeanFactory {
    private IAccountService accountService;
    private TransactionManager transactionManager;
    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }

    // 创建service的代理对象
    public IAccountService getAccountService() {
        return (IAccountService) Proxy.newProxyInstance(
            accountService.getClass().getClassLoader(), 
            accountService.getClass().getInterfaces(), 
            new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) 
                throws Throwable {
                Object returnValue = null;
                try {
                    transactionManager.beginTransaction();
                    returnValue = method.invoke(accountService, args);
                    transactionManager.commit();
                    return returnValue;
                } catch (Exception e) {
                    transactionManager.rollback();
                    throw new RuntimeException(e);
                } finally {
                    transactionManager.release();
                }
            }
        });
    }
}

到了这里,关于spring 动态代理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring封装的动态代理

    看proxyFactory.addAdvice主要干了什么? 看下继承关系:  将advisor加入advisors   看下如何生成代理对象 org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy  org.springframework.aop.framework.DefaultAopProxyFactory#hasNoUserSuppliedProxyInterfaces  判断是否有接口,没有接口直接返回true  如果是接口

    2023年04月09日
    浏览(33)
  • Spring Boot 中的 AOP,到底是 JDK 动态代理还是 Cglib 动态代理

    大家都知道,AOP 底层是动态代理,而 Java 中的动态代理有两种实现方式: 基于 JDK 的动态代理 基于 Cglib 的动态代理 这两者最大的区别在于基于 JDK 的动态代理需要被代理的对象有接口,而基于 Cglib 的动态代理并不需要被代理对象有接口。 那么,Spring 中的 AOP 是怎么实现的

    2024年02月12日
    浏览(37)
  • 动态代理与Spring Aop

    JDK 动态代理 使用JAVA反射包中的类和接口实现动态代理的功能,JAVA.lang.reflect包;主要是三个类: InvocationHandler,Method,Proxy; CGLIB动态代理,第三方工具类库,创建代理对象,cglib的原理是继承,通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改

    2024年02月11日
    浏览(36)
  • spring的aop动态代理对象注入时机

    bean生命周期: bean实例化 populateBean填充属性 invokeAwareMethods调用aware方法 postProcessBeforeInitialization后置处理器before方法 initializeBean初始化bean postProcessAfterAfterInitialization后置处理器after方法 代理对象注入有两种情况:提前和非提前生成代理对象 1. 非提前生成代理对象 依赖于bea

    2024年02月12日
    浏览(44)
  • WebSocket的那些事(5-Spring STOMP支持之连接外部消息代理)

    上节我们在 WebSocket的那些事(4-Spring中的STOMP支持详解) 中详细说明了通过 Spring内置消息代理 结合 STOMP 子协议进行Websocket通信,以及相关注解的使用及原理。 但是Spring内置消息代理会有一些限制,比如只支持STOMP协议的一部分命令,像 acks 、 receipts 命令都是不支持的,还有

    2024年02月09日
    浏览(42)
  • Spring AOP 学习(动态代理、JdbcTemplate、Junit)

    Proxy  jdk动态代理,面向接口 cglib   第三方动态代理,面向父类 在 不修改原有代码 ,或者没有办法修改原有代码的情况下, 增强对象功能 ,使用代理对象代替原来的对象去完成功能,进而达到拓展功能的目的 JDK Proxy 动态代理是 面向接口 的动态代理,一定要有接口和实现

    2024年02月08日
    浏览(43)
  • Spring | Srping AOP (AOP简介、动态代理、基于“代理类”的AOP实现)

    作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习! 该文章 参考学习教材 为: 《Java EE企业级应用开发教程 (Spring + Spring MVC +MyBatis)》 黑马程序员 / 编著 文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和

    2024年01月22日
    浏览(39)
  • Spring之CGLIB和JDK动态代理底层实现

    目录 CGLIB 使用示例-支持创建代理对象,执行代理逻辑 使用示例-多个方法,走不同的代理逻辑 JDK动态代理 使用示例-支持创建代理对象,执行代理逻辑 ProxyFactory 如何自动在CGLIB和JDK动态代理转换 使用示例-使用CGLIB代理方式 使用示例-使用JDK动态代理方式 Spring会自动在JDK动态

    2024年04月25日
    浏览(32)
  • Spring Boot拦截器与动态代理深度剖析

    🎉欢迎来到架构设计专栏~Spring Boot拦截器与动态代理深度剖析 ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹 ✨博客主页:IT·陈寒的博客 🎈该系列文章专栏:架构设计 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习 🍹文章作者技术和水平有限,如果

    2024年01月22日
    浏览(51)
  • 54.Spring的AOP是在哪里创建的动态代理?

    正常的Bean会在Bean的生命周期的‘初始化’后, 通过BeanPostProcessor.postProcessAfterInitialization创建aop的动态代理 还有一种特殊情况: 循环依赖的Bean会在Bean的生命周期‘属性注入’时存在的循环依赖的情况下, 也会为循环依赖的Bean 通过MergedBeanDefinitionPostProcessor.postProcessMergedBe

    2024年02月02日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包