JAVA中代理模式

时间:2022-08-11 15:59:20

代理模式

在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。

简单来说代理模式就是通过一个代理对象去访问一个实际对象,并且可以像装饰模式一样给对象添加一些功能。

静态代理

所谓静态代理即在程序运行前代理类就已经存在,也就是说我们编写代码的时候就已经把代理类的代码写好了,而动态代理则是在程序运行时自动生成代理类。

描述起来太过抽象,看一下代码就明白是怎么回事了

接口

public interface Subject {
public void aa();
}

被代理类

public class Student implements Subject {
public void aa() {
System.out.println("添加学生");
}
}

代理类

//代理类 //与被代理类实现同一个接口
public class Proxy implements Subject {
private Student stu;
public void aa() {
if(stu==null){
stu=new Student();
}
      //在执行被代理对象的方法前做一些事情
System.out.println("============before");
      //执行被代理对象的方法
stu.aa();
      //在执行被代理对象的方法后做一些事
System.out.println("============after");
}
}

测试类

public class MyTest {
@Test
public void test1(){
Proxy proxy=new Proxy();
proxy.aa();
}
}

执行结果

============before
添加学生
============after

动态代理

观察以上代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。

再来看一下动态代理: 
JDK动态代理中包含一个类和一个接口:

public interface IUserDao {
public void add();
}
public class UserDaoImpl implements IUserDao {
public void add() {
System.out.println("添加学生");
}
}

测试

//JDK动态代理
@Test
public void testOne(){
final IUserDao dao=new UserDaoImpl();
InvocationHandler in=new InvocationHandler() {
/**
*
* @param proxy 代理对象
* @param method 目标对象的方法
* @param args 目标对象方法的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("==========before");
Object result = method.invoke(dao, args);
System.out.println("==========after");
return result;
}
};      
      //Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    IUserDao proxy = (IUserDao)Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(),in);
proxy.add(); }

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,

//Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)参数说明
ClassLoader loader:类加载器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子类实例 

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器; 
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的; 
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类; 
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。

动态代理 
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。

Cglib动态代理 
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

public interface IUserDao {
public void add();
}
public class UserDaoImpl  {
  1. /**
  2. * 这个是没有实现接口的实现类
  3. *
  4. * @author student
  5. *
  6. */
public void add() {
System.out.println("添加学生");
}
}

测试类

 //Cglib动态代理
@Test
public void test1(){
//创建目标对象
final UserDaoImpl impl=new UserDaoImpl();
//创建一个代理对象
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(impl.getClass());
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param o 代理对象
* @param method 目标对象方法
* @param objects 目标对象方法参数
* @param methodProxy 代理类的方法
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("==============before");
Object result = method.invoke(impl, objects);
System.out.println("==============after");
return result;
}
});
UserDaoImpl proxy = (UserDaoImpl) enhancer.create();
proxy.add();
}