动态代理的两种实现方式

一、什么是动态代理

 

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

posted @   lucken  阅读(718)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示