代理模式
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
静态代理
静态代理目录结构
主题接口 IUserDao
public interface IUserDao {
void saveUser();
}
主题接口实现类(被代理类) UserDao
public class UserDao implements IUserDao{
@Override
public void saveUser() {
System.out.println("保存用户数据");
}
}
代理类 UserDaoProxy
public class UserDaoProxy implements IUserDao {
//目标对象
private IUserDao targer;
public UserDaoProxy(IUserDao iUserDao){
this.targer = iUserDao;
}
@Override
public void saveUser() {
System.out.println("开启事务");
targer.saveUser();
System.out.println("提交事务");
}
}
测试类 ProxyTest
public class ProxyTest {
@Test
void testStaticProxy(){
//目标对象
IUserDao target = new UserDao();
//代理对象
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.saveUser();
}
}
动态代理
要求真实对象必须有实现接口
说明:
- Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
//返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader,//类加载器
类<?>[] interfaces,//代理类实现的接口列表
InvocationHandler h//调度方法调用的调用处理函数
) throws IllegalArgumentException
InvocationHandler 是由代理实例的调用处理程序实现的接口
Object invoke(Object proxy, //调用该方法的代理实例
方法 method, //所述方法对应于调用代理实例上的接口方法的实例
Object[] args) //接口方法参数
throws Throwable
IUserDao、UserDao同上
UserDaoProxy
public class ProxyFactory {
//目标对象
private Object targer;
public ProxyFactory(Object targer){
this.targer = targer;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(targer.getClass().getClassLoader(), targer.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass());
System.out.println("开启事务");
method.invoke(targer, args);
System.out.println("提交事务");
return null;
}
});
}
}
一般可以改成如下
说明:
代理类继承InvocationHandler
public class ProxyFactory2 implements InvocationHandler{
//目标对象
private Object targer;
public ProxyFactory2(Object targer){
this.targer = targer;
}
public <T> T getProxyInstance() {
return (T) Proxy.newProxyInstance(targer.getClass().getClassLoader(),
targer.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
Object invoke = method.invoke(targer, args);
System.out.println("提交事务");
return invoke;
}
}
动态代理测试
public class ProxyTest {
@Test
void testStaticProxy(){
//目标对象
IUserDao target = new UserDao();
//代理对象
// IUserDao proxyInstance = (IUserDao) new UserDaoProxy(target).getProxyInstance();
IUserDao proxyInstance = new UserDaoProxy2(target).getProxyInstance();//将强转放到了代理类中
proxyInstance.saveUser();
}
}
注意
- 若在invoke中调用 proxy.toString()会出现栈溢出,原因是改方法会继续调用invoke方法,出现循环调用,最终栈溢出。
- 关于invoke方法中proxy参数的作用:只有proxy 实例在InvocationHandler 实现类里加载才能产生第二个参数method (静态代码块是虚拟机加载类的时候执行的,而且只执行一次)
cglib代理
引入依赖
compile "cglib:cglib:3.2.5"
UserDao
public class UserDao {
public void saveUser() {
System.out.println("保存用户数据");
}
}
ProxyFactory
public class ProxyFactory implements MethodInterceptor {
//目标对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事务");
Object invoke = method.invoke(target, args);
System.out.println("提交事务");
return invoke;
}
}
ProxyTest类
public class ProxyTest {
@Test
public void testcglibProxy(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
proxy.saveUser();
}
}
代理方式 | 实现 | 优点 | 缺点 | 特点 |
---|---|---|---|---|
JDK静态代理 | 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 | 实现简单,容易理解 | 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低 | |
JDK动态代理 | 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 | 不需要硬编码接口,代码复用率高 | 只能够代理实现了接口的委托类 | 底层使用反射机制进行方法的调用 |
CGLIB动态代理 | 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 | 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 | 不能对final类以及final方法进行代理 | 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用 |
参考:
https://segmentfault.com/a/1190000011291179#articleHeader3
https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0