Spring学习笔记(3)

动态代理

  • 特点: 字节码随用随创建,随用随加载
  • 作用: 不修改源码的基础下,使方法增强
  • 分类:
    • 基于接口的动态代理
    • 基于子类的动态代理

基于接口的动态代理

  • 涉及的类: Proxy
  • 提供者: JDK
  • 如何创建代理对象
    • 使用Proxy类中的newProxyInstance方法
  • 创建代理对象的要求
    • 被代理对象最少实现一个接口,如果没有则不能使用
  • newProxyInstance方法的参数:
    • ClassLoader: 类加载器
      • 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器(就是你要代理谁,就写谁的类加载器)【固定写法】
    • Class[]: 字节码数组
      • 它是用于让代理对象和被代理对象有相同方法【固定写法】
    • InvocationHandler: 用于提供增强的代码
      • 它是让我们写如何代理,一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
      • 此接口的实现类是谁用谁写

基于接口动态代理的一个例子

当我们的类不实现接口的时候,是不能用动态代理的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String[] args) {
final Producer producer = new Producer();

IProducer proxyProduce = (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 returnValue = null;
// 1.获取方法执行的参数
Float money = (Float) args[0];
// 2.判断当前方法是不是销售
if ("saleProduct".equals(method.getName())){
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
proxyProduce.saleProduct(10000);
}

基于子类的动态代理

需要第三方JAR包的支持

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>

  • 涉及的类: Enhancer
  • 提供者: 第三方类库cglib
  • 如何创建代理对象
    • 使用Enhancer类中的create方法
  • 创建代理对象的要求
    • 被代理类不能是最终类(最终类就不能创建子类了)
  • create方法的参数:
    • Class: 字节码
      • 用于指定被代理对象的字节码
    • Callback: 用于提供增强的代码
      • 它是让我们写如何代理,一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
      • 此接口的实现类是谁用谁写
      • 我们一般写的都是该接口的子接口实现类: MethodInterceptor

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static void main(String[] args) {
final Producer producer = new Producer();
Producer proxyProducer = (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 returnValue = null;
// 1.获取方法执行的参数
Float money = (Float) args[0];
// 2.判断当前方法是不是销售
if ("saleProduct".equals(method.getName())){
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});

proxyProducer.saleProduct(10000);
}

通过动态代理改善代码

即实现了简化开发,又实现了事务控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 用于创建Service代理对象的工厂
*/
public class BeanFactory {
private AccountService accountService;

private TransactionManager txManager;

public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}

public final void setAccountService(AccountService accountService) {
this.accountService = accountService;
}

public AccountService getAccountService(){
return (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object rtValue = null;
try {
// 1.开启事务
txManager.beginTransaction();
// 2.执行操作
rtValue = method.invoke(accountService, args);
// 3.提交事务
txManager.commit();
// 4.返回结果
return rtValue;
} catch (Exception e){
// 5.回滚操作
txManager.rollback();
throw new RuntimeException(e);
} finally {
// 6.释放连接
txManager.release();
}
}
});
}
}

AccountServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void transfer(String sourceName, String targetName, Integer money) {
System.out.println("transfer");
Account source = accountDao.findAccountByName(sourceName);

Account target = accountDao.findAccountByName(targetName);

source.setMoney(source.getMoney() - money);

target.setMoney(target.getMoney() + money);

accountDao.updateAccount(source);
// int i = 1/0;
accountDao.updateAccount(target);

}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
@Qualifier("proxyAccountService")
private AccountService accountService;

@Test
public void testTransfer() {
accountService.transfer("aaa", "bbb", 100);
}
}

bean.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置代理的Service-->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
<!--配置BeanFactory-->
<bean id="beanFactory" class="com.xushui.factory.BeanFactory">
<property name="txManager" ref="transactionManager"></property>
<property name="accountService" ref="accountService"></property>
</bean>

<bean id="accountService" class="com.xushui.service.Impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>

...


# Spring

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×