代理模式分为静态代理和动态代理,我们先以一个例子来回顾这两种代理模式的使用。
静态代理:
静态代理,有点类似于装饰设计模式。静态代理是一种编译期增强,还没运行就已经知道增强的目标对象。装饰设计模式是运行时增强,只有运行时才知道具体增强的目标。
以租房举例,我们可以自己去找房源去租房子,也可以通过中介去租,这里的我们也就是租房的客户就是委托者,中介就是一个代理者。作为客户,我们需要将自己的需求告知中介,作为中介,需要提供一个客户访问或接入的方式,得需要客户能找到你。站着两者的角度,他们最终目的都需要干一件事----租房。
/**
* 租房接口
*/
public interface IRentingHouse {
void rentingHouse();
}
这里的IRentingHouseImpl 就是一个委托者,需求是租房。
public class IRentingHouseImpl implements IRentingHouse {
@Override
public void rentingHouse() {
System.out.println("我要租一间两室一厅的房子");
}
}
这里的IRentingHouseProxy 就是一个代理类,他帮委托者干的活就是租房,所以也要实现IRentingHouse接口,实现rentingHouse方法。不同的是,在代理类中,通过有参构造提供了委托者接入的方式,获取到委托者对象后,在自己的租房方法中调用委托者中的原方法,实现了委托者的功能,又可以在其前后进行增强改造。
public class IRentingHouseProxy implements IRentingHouse{
private IRentingHouse rentingHouse;
public IRentingHouseProxy(IRentingHouse rentingHouse){
this.rentingHouse = rentingHouse;
}
@Override
public void rentingHouse() {
//增强逻辑
System.out.println("中介收取中介费");
//调用原业务逻辑
rentingHouse.rentingHouse();
//原逻辑后增强
System.out.println("中介将客户信息卖出");
}
}
在委托者租房时,就可以不必自己直接调用自己的租房方法,而是将自己的需求告知代理类,租房子的事情就不需要管了。
public class Test {
public static void main(String[] args) {
IRentingHouseImpl rentingHouse = new IRentingHouseImpl();
//自己租房子
// rentingHouse.rentingHouse();
//将自己需求告知对应的代理人,通过代理来租房子
IRentingHouseProxy proxy = new IRentingHouseProxy(rentingHouse);
proxy.rentingHouse();
}
}
要想使用静态代理模式,我们必须自己去写一个代理类,代理类要实现和委托类相同的接口。而动态代理就不需要我们去写代理类了,代理类可以在底层通过反射自动生成。下面介绍下JDK动态代理和Cglib动态代理的使用。
JDK动态代理:
JDK动态代理的实现是由JDK自带的API中的Proxy.newProxyInstance来实现的。IRentingHouse 接口和IRentingHouseImpl 类复用前面提到的,这里不再叙述。
/**
* 采用jdk动态代理来实现
*/
public class JDKProxy {
public static void main(String[] args) {
IRentingHouse iRentingHouse = new IRentingHouseImpl();
//通过jdk提供的Proxy获取代理对象
Object proxy = Proxy.newProxyInstance(iRentingHouse.getClass().getClassLoader(), iRentingHouse.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println(method);//public abstract void com.nanmao.demo.dynamicproxy.IRentingHouse.rentingHouse()
System.out.println(method.getName());//rentingHouse
if("rentingHouse".equals(method.getName())){
//增强逻辑
System.out.println("中介收取中介费");
//调用原业务逻辑
result = method.invoke(iRentingHouse, args);
//原逻辑后增强
System.out.println("中介将客户信息卖出");
}
return result;
}
});
//通过代理对象来调用原逻辑方法
((IRentingHouse)proxy).rentingHouse();
}
}
Cglib动态代理:
要用Cglib动态代理的话,首先要导入cglib的依赖包。实现过程和JDK动态代理类似,通过net.sf.cglib.proxy.Enhancer包下的Enhancer.create来实现。通过实现代码可以发现,与JDK动态代理很大的不同点是,Cglib不需要委托类必须有对应的父接口,而在Proxy.newProxyInstance方法中要传入委托类的接口类型,这样看来,Cglib的应用范围更广一些。
<!--引入cglib依赖包-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
public class CglibProxy {
public static void main(String[] args) {
IRentingHouse iRentingHouse = new IRentingHouseImpl();
Object proxy = Enhancer.create(iRentingHouse.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//参数中的Object[] objects表示的是方法的参数,相当于invoke中的args
Object result = null;
if ("rentingHouse".equals(method.getName())) {
//增强逻辑
System.out.println("中介收取中介费");
//调用原业务逻辑
result = method.invoke(iRentingHouse, objects);
//原逻辑后增强
System.out.println("中介将客户信息卖出");
}
return result;
}
});
((IRentingHouse)proxy).rentingHouse();
}
}
动态代理改造事务控制:
将两种动态代理方式整合成一个代理工厂。在转账案例中,我们要对事务控制采用动态代理来实现,所以我们将原转账业务前后的try…catch部分移到代理工厂中,原业务还是在service层保持不动。
public class ProxyFactory {
private ProxyFactory() {
}
public static ProxyFactory proxyFactory = new ProxyFactory();
public static ProxyFactory getInstance() {
return proxyFactory;
}
public Object getJdkProxy(Object clientObj) {
//通过jdk提供的Proxy获取代理对象
return Proxy.newProxyInstance(clientObj.getClass().getClassLoader(), clientObj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if ("transfer".equals(method.getName())) {
try {
//业务增强:开启事务
TransactionManager.getInstance().startTransaction();
//调用原转账业务
result = method.invoke(clientObj, args);
//业务增强:提交事务
TransactionManager.getInstance().commit();
} catch (Exception e) {
//业务增强:回滚事务
TransactionManager.getInstance().rollBack();
e.printStackTrace();
//抛出异常便于上层servlet捕获
throw e;
}
}
return result;
}
});
}
public Object getCglibProxy(Object clientObj) {
return Enhancer.create(clientObj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
if ("transfer".equals(method.getName())) {
try {
//业务增强:开启事务
TransactionManager.getInstance().startTransaction();
//调用原转账业务
result = method.invoke(clientObj, objects);
//业务增强:提交事务
TransactionManager.getInstance().commit();
} catch (Exception e) {
//业务增强:回滚事务
TransactionManager.getInstance().rollBack();
e.printStackTrace();
//抛出异常便于上层servlet捕获
throw e;
}
}
return result;
}
});
}
}
注释掉TransferServiceImpl中对事务的处理,保留最初的业务代码即可。
@Override
public void transfer(String fromCardId, String toCardId, int money) throws Exception {
/* try {
//开启事务
TransactionManager.getInstance().startTransaction();*/
Account fromAcctInfo = accountDao.queryAcctByCardId(fromCardId);
Account toAcctInfo = accountDao.queryAcctByCardId(toCardId);
fromAcctInfo.setMoney(fromAcctInfo.getMoney() - money);
toAcctInfo.setMoney(toAcctInfo.getMoney() + money);
accountDao.updateAcctByCardId(fromAcctInfo);
int i = 1 / 0;
accountDao.updateAcctByCardId(toAcctInfo);
/* //提交事务
TransactionManager.getInstance().commit();
} catch (Exception e) {
//回滚事务
TransactionManager.getInstance().rollBack();
e.printStackTrace();
//抛出异常便于上层servlet捕获
throw e;
}*/
}
TransferServlet 中,将获得的transferService 对象传入代理工厂中获取对应的代理对象,由代理对象执行后面操作。
public class TransferServlet extends HttpServlet {
// private TransferService transferService = new TransferServiceImpl();
// private TransferService transferService = (TransferServiceImpl)BeanFactory.getBean("transferService");
TransferService transferService = (TransferService)ProxyFactory.newInstance().getJdkProxy(BeanFactory.getBean("transferService"));
代码改造:使用IOC实现对工具类的管理
我们这里对ConnectionUtils和TransactionManager两个工具类和ProxyFactory工厂,使用xml进行管理。首先,分析下这三者间的引用关系,如下图:
ConnectionUtils被TransactionManager和AccountDaoImpI引用,所以我们需要在这两者的bean标签下添加ConnectionUtils属性,在这两者代码中通过set方法对属性赋值。
TransactionManager又被ProxyFactory引用,所以要在ProxyFactory的bean标签下添加TransactionManager属性标签,在ProxyFactory代码中通过set方法对属性赋值。
修改后的bean.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id="accountDao" class="com.nanmao.dao.impl.AccountDaoImpl">
<property name="ConnectionUtils" ref="connectionUtils"></property>
</bean>
<bean id="transferService" class="com.nanmao.service.impl.TransferServiceImpl">
<!-- set + name 找到对应的set方法,将ref中对应的对象set进去-->
<property name="AccountDao" ref="accountDao"></property>
</bean>
<!--添加新增的三个bean组件-->
<!--连接管理器-->
<bean id="connectionUtils" class="com.nanmao.utils.ConnectionUtils"></bean>
<!--事务管理器-->
<bean id="transactionManager" class="com.nanmao.utils.TransactionManager">
<property name="ConnectionUtils" ref="connectionUtils"></property>
</bean>
<!--代理工厂-->
<bean id="proxyFactory" class="com.nanmao.factory.ProxyFactory">
<property name="TransactionManager" ref="transactionManager"></property>
</bean>
</beans>
我们之前写的工具类和工厂类,都是单例模式的,但是我们需要用set方法对其赋值,所以要注释掉之前的单例部分。并且添加对应的成员变量和对应的set方法。之前我们用工厂类(或工具类)调用内部的newInstance方法产生对象,现在我们可以直接使用成员变量就可以了,在BeanFactory中会根据xml配置利用反射产生对应的对象。
TransactionManager :注释之前的单例部分,添加connectionUtils属性及set方法,将之前的ConnectionUtils.getInstance()创建工具类对象调用对应方法改成直接通过connectionUtils属性调用。
/**
* 事务管理器:用于手动管理事务的开启、提交、回滚
*/
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
// private TransactionManager() {
// }
//
// private static TransactionManager transactionManager = new TransactionManager();
//
// //饿汉式实现单例
// public static TransactionManager getInstance() {
// return transactionManager;
// }
public void startTransaction() throws SQLException {
//关闭JDBC中自动事务提交
connectionUtils.getCurConnection().setAutoCommit(false);
}
public void commit() throws SQLException {
connectionUtils.getCurConnection().commit();
}
public void rollBack() throws SQLException {
connectionUtils.getCurConnection().rollback();
}
}
AccountDaoImpl :添加connectionUtils属性及set方法,将之前的ConnectionUtils.getInstance()创建工具类对象调用对应方法改成直接通过connectionUtils属性调用。
public class AccountDaoImpl implements AccountDao {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
@Override
public Account queryAcctByCardId(String cardId) throws Exception {
//使用阿里druid连接池,获取连接
// Connection connection = DruidUtils.getInstance().getConnection();
//修改为:从当前线程中获取connection
// Connection connection = ConnectionUtils.getInstance().getCurConnection();
Connection connection = connectionUtils.getCurConnection();
String sql = "select * from account where cardId = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, cardId);
ResultSet resultSet = preparedStatement.executeQuery();
Account account = new Account();
while (resultSet.next()) {
account.setName(resultSet.getString("cardId"));
account.setMoney(resultSet.getInt("money"));
account.setCardId(resultSet.getString("cardId"));
}
//关闭连接,归还连接到连接池
resultSet.close();
preparedStatement.close();
//连接不可以关闭,否则下次调用的时候就又需要创建新的连接
// connection.close();
return account;
}
@Override
public int updateAcctByCardId(Account account) throws Exception {
//使用阿里druid连接池,获取连接
// Connection connection = DruidUtils.getInstance().getConnection();
//修改为:从当前线程中获取connection
// Connection connection = ConnectionUtils.getInstance().getCurConnection();
Connection connection = connectionUtils.getCurConnection();
String sql = "update account set money = ? where cardId = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, account.getMoney());
preparedStatement.setString(2, account.getCardId());
int result = preparedStatement.executeUpdate();
//关闭连接,归还连接到连接池
preparedStatement.close();
//连接不可以关闭,否则下次调用的时候就又需要创建新的连接
// connection.close();
return result;
}
}
ProxyFactory :添加transactionManager属性及set方法,将之前的TransactionManager .getInstance()创建工厂类对象调用对应方法改成直接通过transactionManager属性调用。
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
// private ProxyFactory() {
// }
//
// public static ProxyFactory proxyFactory = new ProxyFactory();
//
// public static ProxyFactory getInstance() {
// return proxyFactory;
// }
public Object getJdkProxy(Object clientObj) {
//通过jdk提供的Proxy获取代理对象
return Proxy.newProxyInstance(clientObj.getClass().getClassLoader(), clientObj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if ("transfer".equals(method.getName())) {
try {
//业务增强:开启事务
transactionManager.startTransaction();
//调用原转账业务
result = method.invoke(clientObj, args);
//业务增强:提交事务
transactionManager.commit();
} catch (Exception e) {
//业务增强:回滚事务
transactionManager.rollBack();
e.printStackTrace();
//抛出异常便于上层servlet捕获
throw e;
}
}
return result;
}
});
}
public Object getCglibProxy(Object clientObj) {
return Enhancer.create(clientObj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
if ("transfer".equals(method.getName())) {
try {
//业务增强:开启事务
transactionManager.startTransaction();
//调用原转账业务
result = method.invoke(clientObj, objects);
//业务增强:提交事务
transactionManager.commit();
} catch (Exception e) {
//业务增强:回滚事务
transactionManager.rollBack();
e.printStackTrace();
//抛出异常便于上层servlet捕获
throw e;
}
}
return result;
}
});
}
}
TransferServlet:proxyFactory 对象由原来的ProxyFactory.newInstance()获取,改为通过BeanFactory读取xml配置获取。
@WebServlet(name = "transferServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// private TransferService transferService = new TransferServiceImpl();
// private TransferService transferService = (TransferServiceImpl)BeanFactory.getBean("transferService");
// TransferService transferService = (TransferService)ProxyFactory.newInstance().getJdkProxy(BeanFactory.getBean("transferService"));
//从BeanFactory获取ProxyFactory代理工厂对象,然后通过proxyFactory获取代理对象
private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
TransferService transferService = (TransferService)proxyFactory.getJdkProxy(BeanFactory.getBean("transferService"));
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置请求体的字符编码
req.setCharacterEncoding("UTF-8");
String fromCardId = req.getParameter("fromCardId");
String toCardId = req.getParameter("toCardId");
String moneyStr = req.getParameter("money");
int money = Integer.parseInt(moneyStr);
Result result = new Result();
try {
// 调用service层方法
transferService.transfer(fromCardId, toCardId, money);
result.setStatus("200");
} catch (Exception e) {
e.printStackTrace();
result.setStatus("300");
result.setMessage(e.toString());
}
// 响应
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(JsonUtils.object2Json(result));
}
}