您现在的位置是:首页 >技术杂谈 >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的逻辑无法进行事务控制,需要对程序作出修改。
- 创建连接工具类,用于从数据库获取数据库连接,并实现和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();
}
}
- 添加事务管理工具类,包括开启事务、提交事务、回滚事务、释放连接等操作。
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执行指定数据库连接。
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);
}
}
}
- 为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示例
- 将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);
}
}
- 编写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();
}
}
});
}
}