Spring中的动态代理

Spring中的动态代理

一、模拟转账(引出问题进而引出动态代理)

1.1 要求:
  • 转出账户减少金额
  • 转入账户增加金额
  • 模拟人为制造异常,在转入之前发生异常
1.2 实现(以下代码未贴出全部,仅供参考)
1.2.1 POJO
@Data
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
}
1.2.2 Mapper
public interface IAccountMapper {
/**
* 根据姓名查询账户
* @param name
* @return
*/
public Account findByName(String name);
/**
* 更新账户
* @param account
* @return
*/
public int update(Account account);
}
1.2.3 业务层
package com.bdit.service.impl;
import com.bdit.mapper.IAccountMapper;
import com.bdit.pojo.Account;
import com.bdit.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* ClassName: AccountServiceImpl
* Package: com.bdit.service.impl
* date: 2020/11/16 16:04
*/
@Service
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountMapper accountMapper;
    /**
     *
     * @param source 转出
     * @param target 转入
     * @param money 金额
     */
    @Override
    public void transfer(String source, String target, double money) {
//先查询转出账户的信息
        Account accountSource= accountMapper.findByName(source);
//再查询转入账户的信息
        Account accountTarget=accountMapper.findByName(target);
//更新转出账户的信息
        accountSource.setMoney(accountSource.getMoney()-money);
        accountMapper.update(accountSource);
//人为制造异常
        System.out.println(10/0);
//更新转入账户的信息
        accountTarget.setMoney(accountTarget.getMoney()+money);
        accountMapper.update(accountTarget);
    }
}
1.2.4 解决问题(仅仅参考)
package com.bdit.config;
        import com.alibaba.druid.pool.DruidDataSource;
        import org.springframework.beans.factory.annotation.Qualifier;
        import org.springframework.beans.factory.annotation.Value;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.PropertySource;
        import org.springframework.jdbc.datasource.DataSourceUtils;
        import
        org.springframework.transaction.support.TransactionSynchronizationManager;
        import javax.sql.DataSource;
        import java.sql.Connection;
/**
 * ClassName: JdbcConfig
 * Package: com.bdit.config
 * Description: 数据源配置类
 * author: BDIT
 自定义事务管理类
 * date: 2020/11/16 14:42
 * Version 1.0
 */
@PropertySource(value = {"classpath:db.properties"})
public class DataSourceConfig {
    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean(value = "druidDataSource")
    public DataSource createDataSource(){
        DruidDataSource druidDataSource=new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
    @Bean
    public Connection createConnection(@Qualifier("druidDataSource") DataSource
                                               dataSource){
/*
* 保证当前的事务代码中的Connection对象与MyBatis中的SqlSession对象中的Connection
对象是同一个
*
* 使用ThreadLocal技术
*
* 通过Spring框架来实现这个操作
* */
//1 Spring框架提供了事务同步管理器,实现SqlSession对象中的Connection与事务中的
        Connection对象一致
//spring事务同步管理器也是通过ThreadLocal实现了当前线程与Connection对象进行绑定。
        TransactionSynchronizationManager.initSynchronization();
//Spring框架提供了一个DataSourceUtils工具类,getConnection()方法,
// 实现了从ThreadLocal中获取当前线程绑定的Connection对象
        Connection connection=DataSourceUtils.getConnection(dataSource);
        return connection;
    }
}
1.2.5 自定义事务管理类
package com.bdit.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Connection;
        import java.sql.SQLException;
/**
 * ClassName: MyTransactionManager
 * Package: com.bdit.tx
 * Description: 事务类
 * date: 2020/11/16 16:40
 * Version 1.0
 */
@Component
public class MyTransactionManager {
    @Autowired
    private Connection connection;
    public void begin(){
        try {
            connection.setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void commit(){
        try {
            connection.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void rollback(){
        try {
            connection.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void close(){
        try {
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

业务层引入事务管理类

package com.bdit.service.impl;
import com.bdit.mapper.IAccountMapper;
import com.bdit.pojo.Account;
import com.bdit.service.IAccountService;
import com.bdit.tx.MyTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * ClassName: AccountServiceImpl
 * Package: com.bdit.service.impl
 * Description: TODO
 * date: 2020/11/16 16:04
 * Version 1.0
 */
@Service
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountMapper accountMapper;
    @Autowired
    private MyTransactionManager myTransactionManager;
    /**
     *
     * @param source 转出
     * @param target 转入
     * @param money 金额
     */
    @Override
    public void transfer(String source, String target, double money) {
        myTransactionManager.begin();
        try{
//先查询转出账户的信息
            Account accountSource = accountMapper.findByName(source);
//再查询转入账户的信息
            Account accountTarget = accountMapper.findByName(target);
//更新转出账户的信息
            accountSource.setMoney(accountSource.getMoney() - money);
            accountMapper.update(accountSource);
//人为制造异常
            System.out.println(10 / 0);
//更新转入账户的信息
            accountTarget.setMoney(accountTarget.getMoney() + money);
            accountMapper.update(accountTarget);
            myTransactionManager.commit();
        }catch (Exception ce){
            myTransactionManager.rollback();
        }
    }
}

二、问题产生

业务层的方法变得臃肿了,里面充斥着很多重复的代码,并且业务层方法和事务控制的代码耦合了;如果我们 按照这种方式进行事务的提交、回滚等操作,都需要进行业务层代码的调整,而且导致所有的业务类都要和事 务进行耦合。 如果要想优化以上解决方案,我们需要 代理(动态代理和静态代理)

2.1 代理模式
  • 代理

    • 指的是位一个目标对象提供一个代理对象,并由代理对象控制对目标对象的引用,使用代理的对象,就是为了在不修改原代理的基础上,进行增强目标对象的业务逻辑性。
  • 静态代理

    • 特点:为每一个业务增强都提供一个代理类,由代理类来创建代理对象
  • 动态代理

    • 特点: 代理对象直接由代理生成工具动态生成。
    • 原理:**使用一个代理将原本的对象包装起来,**然后使用该代理对象“取代”原始对象。任何 对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
    • 方式:
      • 基于接口实现动态代理:JDK动态代理
      • 基于继承实现动态代理:Cglib、Javassist动态代理
  • JDK 动态代理

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

    • CGLIB动态代理的原理是生成目标类的子类, 这个子类对象就是代理对象, 代理对象是被增强过的。

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

  • JDK 和 Cglib 的区别:

    • JDK 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体的方法前调用了 InvokeHandler 来处理
    • Cglib 动态代理是利用 ASM 开源包,对代理对象类的class文件进行加载,通过修改其字节码生成子类来处理

    注:

    1. 如果对象实现了接口(是个实现类),默认情况下采用 JDK 进行动态代理实现AOP(java默认使用 JDK 动态代理) ,但也可以强制使用 Cglib进行动态代理
    2. 如果对象没有实现接口(不是个实现类),则必须采用 Cglib ,Spring 会自动在 JDK 和 Cglib 之间进行转换

三、解决问题

3.1 使用 JDK 官方的 Proxy来实现动态代理
package com.bdit.com.bdit.proxy;
        import com.bdit.service.IAccountService;
        import com.bdit.tx.MyTransactionManager;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.beans.factory.annotation.Qualifier;
        import org.springframework.context.annotation.Bean;
        import org.springframework.stereotype.Component;
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        import java.util.Arrays;
/**
 * ClassName: ProxyAccountServiceFactory
 * Package: com.bdit.com.bdit.proxy
 * Description: 基于JDK的动态代理,作用:用于创建AccountService动态代理对象的 工厂类
 * date: 2020/11/17 9:53
 * Version 1.0
 */
@Component
public class ProxyAccountServiceFactory {
    //指定被代理对象(AccountServiceImpl)对象
    @Autowired
    @Qualifier("accountServiceImpl")
    private IAccountService accountService;
    @Autowired
    private MyTransactionManager myTransactionManager;
    @Bean("proxyAccountService")
    public IAccountService createAccountServiceProxy(){
//创建IAccountService对象
/**
 * 参数1:被代理对象的类加载器
 * 参数2:被代理对象,所实现的所有接口
 * 参数3:指定原有方法,如何进行增强
 */
        IAccountService proxyAccountService= (IAccountService)
                Proxy.newProxyInstance(
                        accountService.getClass().getClassLoader(),
                        accountService.getClass().getInterfaces(),
                        new InvocationHandler() {
                            /**
                             * invoke方法,就是对原有功能方法的增强
                             * @param proxy 就是创建出来的动态代理对象的引用
                             * @param method 被代理对象所要执行的方法
                             * @param args 被代理对象所要执行的方法所接收的实际参数
                             * @return
                             * @throws Throwable
                             */
                            @Override
                            public Object invoke(Object proxy, Method method, Object[]
                                    args) throws Throwable {
                                System.out.println("=====被代理对象:"+accountService);
                                System.out.println("=====被增强的方法:"+method.getName());
                                System.out.println("=====被增强方法的实际参数:"+
                                        Arrays.toString(args));
                                Object rtValue=null;
                                try{
//开启事务
                                    myTransactionManager.begin();
//调用被代理对象的原有方法
                                    rtValue=method.invoke(accountService,args);
//提交事务
                                    myTransactionManager.commit();
                                }catch (Exception ce){
                                    ce.printStackTrace();
//发生异常
                                    myTransactionManager.rollback();
                                }finally {
                                    myTransactionManager.close();
                                }
                                return rtValue;
                            }
                        }
                );
        return proxyAccountService;
    }
}
业务层代码
package com.bdit.service.impl;
        import com.bdit.mapper.IAccountMapper;
        import com.bdit.pojo.Account;
        import com.bdit.service.IAccountService;
        import com.bdit.tx.MyTransactionManager;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Service;
        import java.util.ArrayList;
        import java.util.List;
/**
 * ClassName: AccountServiceImpl
 * Package: com.bdit.service.impl
 * Description: TODO
 * author: BDIT
 * date: 2020/11/16 16:04
 * Version 1.0
 */
@Service
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountMapper accountMapper;
    /**
     * @param source 转出
     * @param target 转入
     * @param money 金额
     */
    @Override
    public void transfer(String source, String target, double money) {
//先查询转出账户的信息
        Account accountSource = accountMapper.findByName(source);
//再查询转入账户的信息
        Account accountTarget = accountMapper.findByName(target);
//更新转出账户的信息
        accountSource.setMoney(accountSource.getMoney() - money);
        accountMapper.update(accountSource);
//人为制造异常
        System.out.println(10 / 0);
//更新转入账户的信息
        accountTarget.setMoney(accountTarget.getMoney() + money);
        accountMapper.update(accountTarget);
    }
    @Override
    public Account findByName(String name) {
        Account account = null;
        account = accountMapper.findByName(name);
        return account;
    }
    @Override
    public boolean update(Account account) {
        int i = 0;
        i = accountMapper.update(account);
        if (i > 0) {
            return true;
        } else {
            return false;
        }
    }
    @Override
    public boolean save(Account account) {
        int i = 0;
        i = accountMapper.save(account);
        if (i > 0) {
            return true;
        } else {
            return false;
        }
    }
    @Override
    public boolean delete(Integer id) {
        int i = 0;
        i = accountMapper.delete(id);
        if (i > 0) {
            return true;
        } else {
            return false;
        }
    }
    @Override
    public Account findById(Integer id) {
        Account account = null;
        account = accountMapper.findById(id);
        return account;
    }
    @Override
    public List<Account> findAll() {
        List<Account> list = new ArrayList<>();
        list = accountMapper.findAll();
        return list;
    }
}
测试类
package com.bdit.test;
        import com.bdit.config.SpringConfig;
        import com.bdit.service.IAccountService;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.beans.factory.annotation.Qualifier;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.test.context.ContextConfiguration;
        import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
 * ClassName: AccountTest
 * Package: com.bdit.test
 * Description: TODO
 * date: 2020/11/16 16:12
 * Version 1.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class AccountTest {
    @Autowired
    @Qualifier("proxyAccountService")
    private IAccountService accountService;
    @Test
    public void test1(){
        accountService.transfer("zhangsan","lisi",500);
    }
}
3.2 使用 Cglib 动态代理
pom.xml
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

package com.bdit.com.bdit.proxy;
        import com.bdit.service.IAccountService;
        import com.bdit.tx.MyTransactionManager;
        import net.sf.cglib.proxy.Enhancer;
        import net.sf.cglib.proxy.MethodInterceptor;
        import net.sf.cglib.proxy.MethodProxy;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.beans.factory.annotation.Qualifier;
        import org.springframework.context.annotation.Bean;
        import org.springframework.stereotype.Component;
        import java.lang.reflect.Method;
        import java.util.Arrays;
/**
 * ClassName: CglibAccountServiceFactory
 * Package: com.bdit.com.bdit.proxy
 * Description: TODO
 * date: 2020/11/17 10:37
 * Version 1.0
 */
@Component
public class CglibAccountServiceFactory {
    //指定被代理对象(AccountServiceImpl)对象
    @Autowired
    @Qualifier("accountServiceImpl")
    private IAccountService accountService;
    @Autowired
    private MyTransactionManager myTransactionManager;
    //用于创建IAccountService动态代理对象
    @Bean("cglibAccountService")
    public IAccountService createAccountService(){
        IAccountService cglibAccountService=(IAccountService) Enhancer.create(
                accountService.getClass(),
                new MethodInterceptor() {
/**
 * intercept 方法,就是对原有功能方法的增强
 * @param o 就是创建出来的动态代理对象的引用
 * @param method 被代理对象所要执行的方法
 * @param objects 被代理对象所要执行方法,所接收的实际参数
 * @param methodProxy 所要执行的方法的代理对象 method.invoke
 * @return
 * @throws Throwable
 */
                    @Override
                    public Object intercept(Object o, Method method, Object[]
                            objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("=====被代理对象:"+accountService);
                        System.out.println("=====被增强的方法:"+method.getName());
                        System.out.println("=====被增强方法的实际参数:"+
                                Arrays.toString(objects));
                        Object obj=null;
                        try{
//开启
                            myTransactionManager.begin();
//调用被代理对象所要执行的方法
                            obj=method.invoke(accountService,objects);
                            myTransactionManager.commit();
                        }catch (Exception ce){
                            ce.printStackTrace();
                            myTransactionManager.rollback();
                        }finally {
                            myTransactionManager.close();
                        }
                        return obj;
                    }
                }
        );
        return cglibAccountService;
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值