java之代理模式

代理

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

静态代理

特点

由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

示例

dao接口类

public interface UserDao {

	public void setUser();
	
	public void getUser();
}

接口实现类

public class UserDaoImpl implements UserDao {

	@Override
	public void setUser() {
		
		System.out.println("设置user");
	}

	@Override
	public void getUser() {
		
		System.out.println("获取user");
	}

}

静态代理类

public class MyStaticProxy implements UserDao {

	private UserDao userDao;
	public MyStaticProxy(UserDao userDao) {
		this.userDao = userDao;
	}
	@Override
	public void setUser() {
		System.out.println("开启事务");
		userDao.setUser();
		System.out.println("提交事务");
	}

	@Override
	public void getUser() {
		System.out.println("开启事务");
		userDao.getUser();
		System.out.println("提交事务");
	}

}

测试类

public class MyStaticProxyTest {

	public static void main(String[] args) {
		UserDaoImpl userDaoImpl = new UserDaoImpl();
		
		UserDao myStaticProxy = new MyStaticProxy(userDaoImpl);
		
		myStaticProxy.setUser();
		
		myStaticProxy.getUser();
	}
}

分析

  1. 跟适配器,装饰器模式类似,在代理类(MyStaticProxy)中传入被代理类对象(UserDaoImpl),然后实现被代理类接口(UserDao),然后在实现的接口方法中调用被代理类的相应方法,当然在调用方法的前后可以做一些其他操作(类似上面的事务操作),从而保证在原有逻辑不变的情况下插入逻辑;

动态代理

特点

动态代理类:在程序运行时,运用反射机制动态创建而成。

JDK自带Proxy

JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类
目标接口跟目标类沿用静态代理的UserDao跟UserDaoImpl

示例

事务类

public class Transaction {

	public void beginTransaction(){
		System.out.println("开启事务");
	}
	
	public void endTransaction(){
		System.out.println("关闭事务");
	}
	
	public void rollback(){
		System.out.println("回滚事务");
	}
}

拦截类

public class MyProxyHandler implements InvocationHandler {

	private Transaction transaction;
	private UserDao userDao;
	public MyProxyHandler(Transaction tansTransaction,UserDao userDao) {
		this.transaction = tansTransaction;
		this.userDao = userDao;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object object = null;
		try {
			transaction.beginTransaction();
			object = method.invoke(this.userDao, args);
			transaction.endTransaction();
		} catch (Exception e) {
			transaction.rollback();
			e.printStackTrace();
		}
		return object;
	}
}

jdk自带代理测试类

public class MyJdkProxyTest {

	public static void main(String[] args) {
		UserDao userDao = new UserDaoImpl();
		MyProxyHandler myProxyHandler = new MyProxyHandler(new Transaction(), userDao);
		userDao = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(), 
				userDao.getClass().getInterfaces(), myProxyHandler);
		userDao.setUser();
		userDao.getUser();
	}
}

分析

  1. Proxy类利用newProxyInstance方法传入被目标类接口跟类加载器构造一个代理对象,然后代理对象调用目标接口中的方法时会通过MyProxyHandler 对象的invoke方法调用目标实现类的相应方法,而MyProxyHandler 中的invoke方法只需要我们自己编写即可,可以理解成策略模式,依赖倒置原则;

cglib动态代理

jar包下载

  1. cglib下载
  2. asm下载

示例

设置拦截器

public class MyInterceptor implements MethodInterceptor {
	
	private UserDao userDao;
	private Transaction transaction;
	public MyInterceptor(UserDao userDao,Transaction transaction) {
		this.userDao = userDao;
		this.transaction = transaction;
	}
	
	public Object create(){
		Enhancer enhancer = new Enhancer();//该类用于生成代理对象
		
		enhancer.setCallback(this);// 参数为拦截器 
		enhancer.setSuperclass(userDao.getClass());// 设置父类
		return enhancer.create();// 创建代理对象
	}
	
    /**
     * @param obj 目标对象代理类的实例
     * @param method 代理实例上 调用父类方法的Method实例
     * @param args 传入到代理实例上方法参数值的对象数组
     * @param methodProxy 使用它调用父类的方法
     * @return
     * @throws Throwable
     */
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		Object object = null;
		try {
			this.transaction.beginTransaction();
			object = method.invoke(userDao,args);
			this.transaction.endTransaction();
		} catch (Exception e) {
			this.transaction.rollback();
			e.printStackTrace();
		}
		return object;
	}
	
	
}

测试类

public class MyCglibProxyTest {

	public static void main(String[] args) {
		UserDaoImpl userDao = new UserDaoImpl();
		Transaction transaction = new Transaction();
		
		MyInterceptor myInterceptor = new MyInterceptor(userDao, transaction);
		userDao = (UserDaoImpl)myInterceptor.create();
		userDao.setUser();
		userDao.getUser();
	}
}

可以在方法中调用生成字节码的方法查看生成的代理类class字节码文件

    /**
     * 把代理类的字节码写到硬盘上 
     * @param path 保存路径 
     */
    public static void writeProxyClassToHardDisk(String path) {
        // 第一种方法
    	//System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 
 
        // 第二种方法  
 
        // 获取代理类的字节码  
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", UserDaoImpl.class.getInterfaces());
 
        FileOutputStream out = null;
 
        try {
            out = new FileOutputStream(path);
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

最终生成的代理类class

public final class $Proxy1 extends Proxy
  implements UserDao
{
  private static Method m1;
  private static Method m4;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public $Proxy1(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void getUser()
    throws 
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void setUser()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m4 = Class.forName("com.huzd.study.proxy.staticproxy.UserDao").getMethod("getUser", new Class[0]);
      m3 = Class.forName("com.huzd.study.proxy.staticproxy.UserDao").getMethod("setUser", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

区别

  1. JDK:
    1. 目标类和代理类实现了共同的接口
    2. 拦截器必须实现InvocationHandler接口,而这个接口中invoke方法体的内容就是代理对象方法体的内容
  2. CGLIB:
    1. 目标类 是代理类的父类
    2. 拦截器必须实现MethodInterceptor接口,而接口中的intercept方法就是代理类的方法体,使用字节码增强机制创建代理对象的.

springAop代理机制

  1. 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
    1. 优点:因为有接口,所以使系统更加松耦合
    2. 缺点:为每一个目标类创建接口
  2. 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
    1. 优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
    2. 缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值