1.什么是动态代理:
我的理解就是首先它是动态的,即随用随创建,随用随加载,其次在不修改源码的基础上对原有的方法进行增强。比如我们写了一个类,然后需要在里面写上打印日志,判断执行效率这些常用的方法,如果我们每一个都写的话,就很臃肿,这时候动态代理可以帮我们做这样的事情。
2.动态代理的分类:
基于接口的动态代理
基于子类的动态代理
二者的区别是什么,各自实现什么样的功能呢?
(1).首先涉及到Proxy(代理)类,然后通过Proxy的newProxyInstance来创建代理对象,同时,被代理的类至少实现一个接口,否则的话不能使用,然后上面newProxyInstance方法中的三个参数
ClassLoader(类加载器):固定的写法,被代理的类.getClass().getClassLoader()
,
Class[](字节码数组):固定的写法,被代理的类.getClasss().getInterfaces()
InvocationHandler(用于提供增强的方法):然后这个参数就是最难写的,它是让我们如何去代理的,一般都是一个接口的实现类,通常情况都是匿名内部类,此接口的实现类都是谁用谁写。
new InvocationHandler() { /** * 作用:执行被代理对象的任何接口方法都会经过该方法 * 方法参数的含义 * @param proxy 代理对象的引用 * @param method 当前执行的方法 * @param args 当前执行方法所需的参数 * @return 和被代理对象方法有相同的返回值 * @throws Throwable */ @Override 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; } });
(2).基于子类的动态代理,涉及到的类是Enhancer,是cglib提供的,然后它代理的类不能是最终类,即final修饰的类,通过create来创建代理对象,然后create里面只有两个参数
Class(字节码):固定的写法,被代理类.getClass()
Callback(用于增强的代码):这个和上面的第三个参数一样是最难的,一个该接口的实现类,是为了让我们写出如何代理的,通常情况都是匿名内部类,此接口的实现类谁用是写。
Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 执行cglib创建对象的任何方法都会经过该方法
* @param proxy
* @param method
* @param args
* 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
* @param methodProxy :当前执行方法的代理对象
* @return
* @throws Throwable
*/
@Override
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;
}
});
3.动态代理应用的两个例子:
然后写了两个demo,大致的样式如下图所示
package com.itheima.cglib;
import com.itheima.proxy.IProducer;
public class Producer implements IProducer{
public void saleProduct(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() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object returnvalue=null;
Float money=(Float)objects[0];
if("saleProduct".equals(method.getName())){
returnvalue= method.invoke(producer,money*0.8f);
}
return returnvalue;
}
});
cglibProducer.saleProduct(1000f);
}
}
–下面对应的是下面三个类---------------------------------------------------------------------
public interface IProducer {
public void saleProduct(float money);
public void afterService(float money);
}
public class Producer implements IProducer{
public void saleProduct(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();
IProducer proxyProducer=(IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
// 执行被代理对象的任何接口方法都会经过该方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 提供增强的代码
Object returnvalue=null;
Float money=(Float)args[0];
if("saleProduct".equals(method.getName())){
returnvalue= method.invoke(producer,money*0.8);
}
return returnvalue;
}
}
);
proxyProducer.saleProduct(1000f);
}
}
动态代理的另外一个例子
银行转账的时候,需要完成这几个步骤
//1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//3转出账户减钱
source.setMoney(source.getMoney()-money);
//4转入账户加钱
target.setMoney(target.getMoney()+money);
//5更新转出账户
accountDao.updateAccount(source);
//6更新转入账户
accountDao.updateAccount(target);
但是如果在第5步之后出现了异常,这时候如果没有事务回滚,那么转出账户钱减少了,但是转入账户的钱并没有增加,这时候我们可以加上一个事务
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney()-money);
//2.4转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5更新转出账户
accountDao.updateAccount(source);
int i=1/0;
//2.6更新转入账户
accountDao.updateAccount(target);
//3.提交事务
txManager.commit();
}catch (Exception e){
//4.回滚操作
txManager.rollback();
e.printStackTrace();
}finally {
//5.释放连接
txManager.release();
}
这个属于一个转账的功能,如果我们删除账户,查询账户等等都可能遇到异常,都需要加上事务管理这个功能,那么我们可以创建一个工厂类:获取代理对象
public IAccountService getAccountService() {
return (IAccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 添加事务的支持
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("test".equals(method.getName())){
return method.invoke(accountService,args);
}
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();
}
}
});
}
然后配置代理的service,然后测试代理的service可以发现每一个测试类都会实现增强的代理方法
public class AccountServiceTest {
@Autowired
@Qualifier("proxyAccountService")
private IAccountService as;
@Test
public void testTransfer(){
as.transfer("aaa","bbb",100f);
}
}
这里因为我在配置的时候,原始的配置service我还保留了,所以如果不加上==@Qualifier==那么就会显示有两个service。最后执行一下,可以发现发现,如果转账的时候出现了异常,那么事务管理就会回滚,从而使系统的可靠性得到了提升!