动态代理的两种实现方式
一、什么是动态代理
1、字面意思,代理就是代替别人去做一些事情,如线下店代替工厂去卖电脑、代理工厂做售后工作,线下店就是代理商,从卖给工厂的获得的钱提取分成就是增强的方法。
2、Java中就是在不改变别别的类,对类做增强处理,如打印日志、事物的控制,权限的管理,后续我们都会介绍。
二、两种实现动态代理的方法
1、基于JDK的动态代理
基于接口的动态代理,用到的类是Proxy的newProxyInstance静态方法创建,要求被代理对象至少实现一个接口,如果没有,则不能创建代理对象。
2、基于cglib的动态代理
要导入cglib第三方库,使用的类是Enhancer的create静态方法创建,要求被代理类不能是最终类,即不能用final修饰,如String类。
三、代码演示
1、首先创建一个IProduct接口,并创建被代理类,实现这个接口
IProduct
public interface IProduct {
String sell(Float money);
void afterSell();
}
Product
public class Product implements IProduct {
@Override
public String sell(Float money) {
System.out.println("代理员交给工厂:"+money);
return "aaa";
}
@Override
public void afterSell() {
System.out.println("代理员做售后。。");
}
}
2、通过JDK来实现动态代理,创建一个消费者Consumer
这里我们直接通过匿名内部类来实现,当然不是必须的
Consumer类
public class Consumer {
public static void main(String[] args) {
// 创建一个被代理对象
final Product product = new Product();
// 创建一个代理对象,并在InvocationHandler的invoke方法里面,对被代理类的方法做增强
IProduct proxyProduct = (IProduct) Proxy.newProxyInstance(product.getClass().getClassLoader(), product.getClass().getInterfaces(), new InvocationHandler() {
// 实现具体的增强操作
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取方法在运行中可能产生的返回值
Object returnValue = null;
Float money = (Float) args[0];
if("sell".equals(method.getName())){
// 执行具体的方法
returnValue = method.invoke(product, money*0.8F);
}
return returnValue;
}
});
System.out.println(proxyProduct.sell(1000F));
}
}
代码分析
1、Proxy.newProxyInstance的三个参数
IProduct proxyProduct = (IProduct) Proxy.newProxyInstance(product.getClass().getClassLoader(), product.getClass().getInterfaces(), new InvocationHandler() {
}
1
2
ClassLoader loader获取被代理类的类加载器。
Class<?>[] interfaces获取被代理类的实现接口的数组。
InvocationHandler h在invok方法中对方法做增强处理。
2、invoke方法的三个参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
}
1
2
Object proxy 当前代理对象
Method method 当前方法
Object[] args方法传递的参数
3、通过cglib来实现动态代理,创建一个消费者Consumer
public class Consumer {
public static void main(final String[] args) {
// 创建一个被代理对象,这里要求必须是final
final Product product = new Product();
Product proxyProduct =(Product) Enhancer.create(product.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Float money = (Float) objects[0];
Object returnValue = null;
if("sell".equals(method.getName())){
returnValue = method.invoke(product, 0.8f * money);
}
return returnValue;
}
});
System.out.println(proxyProduct.sell(1000f));
}
}
代码分析
1、Enhancer.create的2个参数
Product proxyProduct =(Product) Enhancer.create(product.getClass(), new MethodInterceptor() {
}
1
2
Class type被代理类的class文件
Callback callback一个Callback接口,我们通常使用MethodInterceptor接口,继承了Callback接口
2、intercept方法的参数
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
}
1
2
Method method当前方法
Object[] objects方法用到的参数数组
4、测试结果
代理员交给工厂:800.0
aaa
代理商收取了200块提成。
四、结合BeanFactory创建Bean的方式来控制事务
学完动态代理,可以结合BeanFactory创建Bean的方式来控制事务
1、改造事务分析
Spring学习(四):事务的学习之银行转账案例
原来的事务控制我们是写在Service层,现在我们要把重复代码抽取出来,统一交给代理对象去管理事务。
原Service代码
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
TransactionManager transactionManager;
@Autowired
IAccountDao accountDao;
@Autowired
private ConnectionUtils connectionUtils;
@Override
public void updateAccount(Account account) {
try {
transactionManager.beginTransaction();
accountDao.updateAccount(account);
int a = 1/0; // 模拟业务层出错
transactionManager.commitTransaction();
}catch (Exception e){
transactionManager.rollbackTransaction();
e.printStackTrace();
}finally {
transactionManager.release();
}
}
}
现在我们只留一行代码
accountDao.updateAccount(account);
1
2、代码编写思路分析
创建一个BeanFactory,里面注入一个AccountService。
在get方法中返回一个代理对象。
选择一种动态代理的实现方法,编写代理详细实现代码。
配置bean.xml配置文件
3、代码的实现
BeanFactory类
public class BeanFactory {
@Autowired
/**
* 由于配置文件有2个AccountService实现类的bean配置,所以要指定beanId才可以自动注入
* proxyAccountService、accountService
*/
@Qualifier("accountService")
private IAccountService iAccountService;
@Autowired
TransactionManager transactionManager;
// 通过JDK动态代理实现
public IAccountService getAccountService() {
IAccountService proxyIaccountService = (IAccountService) Proxy.newProxyInstance(iAccountService.getClass().getClassLoader(), iAccountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
try {
transactionManager.beginTransaction();
System.out.println("开启事务。。。");
System.out.println("执行【"+method.getName()+"】方法。。。");
returnValue = method.invoke(iAccountService, args);
System.out.println(5/0);
transactionManager.commitTransaction();
System.out.println("COMMIT事务。。。");
}catch (Exception e){
System.out.println("ROLLBACK事务。。。");
transactionManager.rollbackTransaction();
e.printStackTrace();
}finally {
transactionManager.release();
}
return returnValue;
}
});
return proxyIaccountService;
}
// 通过Cglib动态代理实现
public IAccountService getAccountServiceByCglib() {
IAccountService proxyAccountServiceByCglib = (IAccountService) Enhancer.create(IAccountService.class, new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
try {
transactionManager.beginTransaction();
System.out.println("开启事务。。。");
System.out.println("执行【"+method.getName()+"】方法。。。");
returnValue = method.invoke(iAccountService, objects);
System.out.println(5/0);
transactionManager.commitTransaction();
System.out.println("COMMIT事务。。。");
}catch (Exception e){
System.out.println("ROLLBACK事务。。。");
transactionManager.rollbackTransaction();
e.printStackTrace();
}finally {
transactionManager.release();
}
return returnValue; }
});
return proxyAccountServiceByCglib;
}
public void setIAccountService(IAccountService iAccountService) {
this.iAccountService = iAccountService;
}
}
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--找到对应的XML头,和打开包扫描-->
<context:component-scan base-package="com"/>
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" />
<property name="user" value="root"/>
<property name="password" value="123456" />
</bean>
<bean id="connectionUtils" class="com.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="transationManager" class="com.utils.TransactionManager">
<property name="connectionUtils" ref="connectionUtils" />
</bean>
<!-- 配置BeanFactory类,用工厂创建我们的代理AccountService -->
<bean id="beanFactory" class="com.utils.BeanFactory"></bean>
<!-- 通过JDK动态代理实现 -->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
<!-- 通过Cglib动态代理实现 -->
<bean id="proxyAccountServiceByCglib" factory-bean="beanFactory" factory-method="getAccountServiceByCglib"></bean>
</beans>
测试类
public void testFindAccountAll(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = (IAccountService) context.getBean("proxyAccountService");
Account account = new Account();
account.setId(1);
account.setMoney(500D);
account.setName("aaa");
accountService.updateAccount(account);
}
可以看到代理类实现了事务,当代码报错,数据正常回滚了。
在这里插入图片描述
五、总结
1、JDK动态代理,自带的,方便使用,但是要要求必须实现接口,有一定的约束。
2、cglib,需要导入第三方jar包,使用的时候没有什么约束。
3、SpringAOP以上2种方法都用到了。
4、学完动态代理,可以结合BeanFactory创建Bean的方式来控制事务。
----------------实现AOP
1 import org.junit.Test;
2 /*
3 * 目标是让目标对象和增强都可以切换!
4 */
5 public class Demo3 {
6 @Test
7 public void fun1() {
8 ProxyFactory factory = new ProxyFactory();//创建工厂
9 factory.setTargetObject(new ManWaiter());//设置目标对象
10 factory.setBeforeAdvice(new BeforeAdvice() {//设置前置增强
11 public void before() {
12 System.out.println("您好不好!");
13 }
14 });
15
16 factory.setAfterAdvice(new AfterAdvice() {//设置后置增强
17 public void after() {
18 System.out.println("再见不见!");
19 }
20 });
21 Waiter waiter = (Waiter)factory.createProxy();
22 waiter.shouQian();
23 }
24 }
Demo3
1 import java.lang.reflect.InvocationHandler;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.Proxy;
4
5 /**
6 * 它用来生成代理对象
7 * 它需要所有的参数
8 * * 目标对象
9 * * 增强
10 * @author cxf
11 */
12 /**
13 * 1. 创建代理工厂
14 * 2. 给工厂设置三样东西:
15 * * 目标对象:setTargetObject(xxx);
16 * * 前置增强:setBeforeAdvice(该接口的实现)
17 * * 后置增强:setAfterAdvice(该接口的实现)
18 * 3. 调用createProxy()得到代理对象
19 * * 执行代理对象方法时:
20 * > 执行BeforeAdvice的before()
21 * > 目标对象的目标方法
22 * > 执行AfterAdvice的after()
23 * @author cxf
24 *
25 */
26 public class ProxyFactory {
27 private Object targetObject;//目标对象
28 private BeforeAdvice beforeAdvice;//前置增强
29 private AfterAdvice afterAdvice;//后置增强
30 /**
31 * 用来生成代理对象
32 * @return
33 */
34 public Object createProxy() {
35 /*
36 * 1. 给出三大参数
37 */
38 ClassLoader loader = this.getClass().getClassLoader();
39 Class[] interfaces = targetObject.getClass().getInterfaces();
40 InvocationHandler h = new InvocationHandler() {
41 public Object invoke(Object proxy, Method method, Object[] args)
42 throws Throwable {
43 /*
44 * 在调用代理对象的方法时会执行这里的内容
45 */
46 // 执行前置增强
47 if(beforeAdvice != null) {
48 beforeAdvice.before();
49 }
50
51 Object result = method.invoke(targetObject, args);//执行目标对象的目标方法
52 // 执行后置增强
53 if(afterAdvice != null) {
54 afterAdvice.after();
55 }
56
57 // 返回目标对象的返回值
58 return result;
59 }
60 };
61 /*
62 * 2. 得到代理对象
63 */
64 Object proxyObject = Proxy.newProxyInstance(loader, interfaces, h);
65 return proxyObject;
66 }
67
68
69 public Object getTargetObject() {
70 return targetObject;
71 }
72 public void setTargetObject(Object targetObject) {
73 this.targetObject = targetObject;
74 }
75 public BeforeAdvice getBeforeAdvice() {
76 return beforeAdvice;
77 }
78 public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
79 this.beforeAdvice = beforeAdvice;
80 }
81 public AfterAdvice getAfterAdvice() {
82 return afterAdvice;
83 }
84 public void setAfterAdvice(AfterAdvice afterAdvice) {
85 this.afterAdvice = afterAdvice;
86 }
87 }
ProxyFactory
1 public class ManWaiter implements Waiter {
2 public void serve() {
3 System.out.println("服务中...");
4 }
5
6 public void shouQian() {
7 System.out.println("混蛋,给我钱!");
8 }
9 }
ManWaiter
1 // 服务员
2 public interface Waiter {
3 // 服务
4 public void serve();
5 public void shouQian();
6 }
1 public interface AfterAdvice {
2 public void after();
3 }
1 /**
2 * 前置增强
3 * @author cxf
4 *
5 */
6 public interface BeforeAdvice {
7 public void before();
8 }
BeforeAdvice
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?