1. 动态代理的特点
- 字节码随用随创建,随用随加载。
- 它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
- 装饰者模式就是静态代理的一种体现。
2. 动态代理常用的有两种方式
2.1 基于接口的动态代理
- 提供者: JDK 官方的 Proxy 类。
- 要求:被代理类最少实现一个接口。
2.1.1 创建一个新项目
- 在pom.xml文件中修改打包方式为 jar。
<packaging>jar</packaging>
- 创建对生产商的要求:生产者接口
public interface Producer {
/**
* 销售商品
* @param money
*/
void sale(Float money) ;
/**
* 售后服务
* @param money
*/
void afterService(Float money);
}
- 创建生产者接口的实现类 :
public class ProducerImpl implements Producer {
public void sale(Float money) {
System.out.println("销售电脑,并收获:" + money);
}
public void afterService(Float money) {
System.out.println("提供售后服务,并收获:" + money);
}
}
- 使用基于接口的动态代理实现对
sale
方法的增强 :
public class Client {
public static void main(String[] args) {
final Producer producer = new ProducerImpl();
/*
* 动态代理特点 : 字节码随用随创建,随用随加载
* 动态代理的作用:不修改源码的基础上对方法进行增强
* 动态代理的分类 :
* 基于接口的动态代理
* 基于子类的动态代理
*
* 基于接口的动态代理
* */
Producer producerProxyed = (Producer) 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 res = null;
// 提供增强的代码
// 1. 获取方法执行的参数
Float money = (Float) args[0];
// 判断当前方法是不是销售
if ("sale".equals(method.getName())) {
res = method.invoke(producer, money * 0.8f);
}
return res;
}
});
producerProxyed.sale(1000.f);
}
}
2.2 基于子类的动态代理
- 提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
<dependencies>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
</dependencies>
- 要求:被代理类不能用
final
修饰的类(最终类)。
- 创建生产商类
Producer
public class Producer{
public void sale(Float money) {
System.out.println("销售电脑,并收获:" + money);
}
public void afterService(Float money) {
System.out.println("提供售后服务,并收获:" + money);
}
}
- 基于子类的动态代理代码编写 :
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
/*
* 基于子类的动态代理
*/
Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* 以上三个参数和基于接口的动态代理中invoke的方法的参数是一样的
* @param methodProxy 当前执行方法的代理对象
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 提供增强的代码
Object res = null;
// 提供增强的代码
// 1. 获取方法执行的参数
Float money = (Float) args[0];
// 判断当前方法是不是销售
if ("sale".equals(method.getName())) {
res = method.invoke(producer, money * 0.8f);
}
return res;
}
});
cglibProducer.sale(10000.0f);
}
}
3. 使用动态代理实现事务的控制
-
编写
BeanFactory
类 创建代理对象,实现对方法的增强。
public class BeanFactory {
private AccountService accountService;
private TransactionManager txManager;
/**
* 用于注入 AccountService 对象
*
* @param accountService
*/
public final void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
/**
* 用于注入 TransactionManager 对象
*
* @param txManager
*/
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public AccountService getAccountService() throws IllegalAccessException, InstantiationException {
return (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object res = null;
try {
// 1. 开启事务
txManager.beginTransaction();
// 2. 执行操作
res = method.invoke(accountService, objects);
// 3. 提交事务
txManager.commitTransaction();
// 4. 返回结果集
return res;
} catch (Exception e) {
// 5. 回滚事务
throw new RuntimeException(e);
} finally {
// 6. 释放连接
txManager.closeTransaction();
}
}
});
}
}
-
删除
AccountServiceImpl
实现类中所有事务控制的代码 -
修改
bean.xml
文件 配置
<!-- 配置代理的service对象 -->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"/>
<!-- 配置 BeanFactory -->
<bean id="beanFactory" class="com.lyp.factory.BeanFactory">
<property name="txManager" ref="txManager"/>
<property name="accountService" ref="accountService"/>
</bean>
- 此时的
bean
容器中将会出现 两个AccountService
类型的对象 ,所以在测试类中注入的AccountService
对象将会出现冲突的情况。所以需要使用@Qualifier
注解指定需要注入的具体的AccountService
对象的名称。
@Autowired
@Qualifier("proxyAccountService") // 这里使用的是动态代理对象
private AccountService as;
- 通过在代码中制造异常和删除异常来测试事务控制是否成功!
网友评论