概念
- 定义接口和方法。
- 真实对象实现接口(非必须)
- 创建代理类,实现接口,并内部持有真是对象(private User user)。
- 动态代理是运行中自动生成,不用手动创建。
-
分类
静态
- JDK动态
- cglib动态
- javaassist字节码操作库
静态代理
- 自己创建代理类,再编译代理类
```java
//没有代理类
//目标对象
public class UserDao{
public void save(){
} } //测试类 public class t{ public static void main(String[] args){sout("save");
} } //添加代理类 public ProxyUserDao extend UserDao{ private UserDao u; //定义目标类 //构造方法传入目标类 private ProxyUserDao(UserDao u){UserDao u = new UserDao();
u.save();
} //方法增强 public void save(){this.u = u;
} } //静态代理测试 public void tt{ public static void main(String[] args){sout("事务、日志...");
u.save();
sout("事务、日志...");
}UserDao u = new UserDao();
ProxyUserDao p = new ProxyUserDao(u);
p.save();
}
<a name="v0ZBe"></a>
#### 动态代理
> 动态代理利用的是**反射技术**
- JDK动态基于接口必须实现接口,目标业务必须实现接口
> 涉及的两大核心类是Proxy类和InvocationHandler接口,重写invoke()方法;用Proxy.newProxyInstance(loader, interfaces,h);
```java
//JDK动态代理
//接口
public interface UserDao{
void save();
}
//接口类实现类
public class UserDapImpl implements UserDao{
public void save(){
sout("save")
}
}
//代理类
//每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
public class InvocationHandlerImpl implements InvocationHandler{
//业务实现类对象,调用具体的业务方法
private Object target;
//构造函数传入目标对象。
public InvocationHandlerImpl( Object target){
this.target = target;
}
//动态代理实际运行的代理方法
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
sout("调用开始");
//invoke()以反射方式创建对象,target要创建的对象,args构成方法的参数,由args决定创建对象使用哪个构造方法。
Object result = method.invoke(target,args);
sout("调用结束");
return result;
}
}
//利用动态代理使用代理方法
public class t{
public ststic void main(String[] args){
//被代理对象
UserDao u = new UserDaoImpl();
InvocationHandlerImpl invo = new InvocationHandlerImpl(u);
//类加载器
ClassLoader loader = u.getClass().getClassLoader();
Class<?>[] interf = u.getClass().getInterfaces();
//主要装载器、一组接口及调用处理器动态代理实例
UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(loader,interf,invo);
newProxyInstance.save();
}
}
- Cglib动态基于子类,创建目标对象的子类
- 不用实现接口,可直接代理目标,底层时字节码,实现目标子类。final不能。
```java
//Cglib动态代理
//接口
public interface UserDao{
void save();
}
//接口实现类—目标对象
public class UserDaoImpl implements UserDao{
public void save(){
} }sout("save");
//代理主要类 public class CglibProxy implements MethodInterceptor{ private Object targetObject;
//这里的目标类型为Object,可接收任意参数作为被代理类。
public Object getInstance(Object target){
//设置需要创建子类的类
this.targetObject = target;
Enhancer enh = new Enhancer();
enh.setSuperclass(target,getClass());
enh.setCallback(this);
return enh.creater();
}
//代理实际方法
public Object intercept(Object obj, Method method,Object[] args,MethodProxy proxy) throws Throwable{
sout("开启事务");
Object result = proxy.invoke(targetObject,args);
sout("关闭事务");
//返回代理对象
return result;
}
}
//测试Cglib动态代理 public class t{ public static void main(String[] args){ CglibProxy cg = new CglibProxy(); UserDao u = (UserDao) c.getinstance(new UserDaoImpl()); u.save(); } } ```
jdk动态代理 VS cglib
JDK Proxy 的优势:
- 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。
- 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。
- 代码实现简单。
cglib 优势: - 有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似 cglib 动态代理就没有这种限制。
- 只操作我们关心的类,而不必为其他相关类增加工作量。
总结
- 代理模式: 为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问。
- jdk动态代理生成的代理类继承了Proxy类并实现了被代理的接口;而cglib生成的代理类则仅继承了Proxy类。
- jdk动态代理最大缺点:只能代理接口,既委托类必须实现相应的接口
- cglib缺点:由于是通过“子类化”的方式, 所以不能代理final的委托类或者普通委托类的final修饰的方法。