代理是一种基本的设计模式,代理模式的主要作用是为其他对象(被代理的对象,下面称为原对象)提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理对象既可以将客户端的请求完全转发给原对象,也可以提供一些额外或不同的操作。这篇文章从静态代理模式说起,然后到Java的动态代理最后分析一个动态代理在mybatis应用的实例。
1.静态代理
静态代理相当于为每个需要被代理的类构建一个代理类出来,然后将代理类的对象作为被代理类对象提供给外部操作。下面是一个简单的示例:
public interface Car {
double getPrice();
String getName();
}
public class Bicycle implements Car {
private double price = 200;
private String name = "bicycle";
@Override
public double getPrice() {
System.out.println("bicycle's price is " + price);
return price;
}
public String getName() {
System.out.println("name is " + name);
return name;
}
}
public class SimpleProxy implements Car {
private Car proxied; //被代理对象
public SimpleProxy(Car proxied) {
this.proxied = proxied;
}
//对价格的访问会被直接拦截掉
public double getPrice() {
System.out.println("car price is 1000");
return 1000;
}
//对名字的访问会被直接转发给被代理对象
public String getName() {
return proxied.getName();
}
}
Car是一个接口而Bicycle是Car的一个具体子类,SimpleProxy也实现了Car接口并且内部还只有一个Car类型的引用,所以可以作为Bicycle代理对象使用。下面是对上面代理的测试:
public class TestProxy {
public static void getCarInfo(Car car) {
car.getPrice();
car.getName();
}
public static void main(String[] args) {
//代理对象需要持有被代理对象的引用,用于做请求的转发
Car car = new SimpleProxy(new Bicycle());
getCarInfo(car);
}
}
/**
car price is 1000
name is bicycle
*/
对于getCarInfo方法来说,它接受的参数是接口类型,所以无法真正知道获得的是Bicycle对象还是SimpleProxy对象,因为这两种都实现了Car接口。这样的情况下,SimpleProxy实际上已经被插入到客户端和Bicycle对象之间作为一个中间人的角色,它拦截了对被代理对象方法的调用,对于其中一些方法它直接返回了自定义的信息,对于另外一些方法它转发给了被代理对象。这种静态代理的好处在于通过代理类就可以对原对象的某些操作进行修改,而不用去修改原对象的代码。
2.java 动态代理
对于上面展示的静态代理来说,需要对每一个类型(实现同一个接口的子类)都手动的编写一个代理类,在实际应用中这是一个很大的限制(比如说有时候所需要代理的类是从外部引用的,没有源码)。而Java的动态代理解决了这个问题,它可以动态的创建代理并动态的处理对所代理方法的调用。不过与静态代理不同,动态代理不会为被代理对象中的每一个方法都提供一个代理实现,而是将在动态代理上做的所有调用都重定向到一个单一处理器(方法)上,然后在这个方法上根据调用的类型进行转发。先来看一个具体的实例,对上面的SimpleProxy进行改写,将其变成动态代理模式:
public class DynamicProxyHandler implements InvocationHandler {
//被代理对象
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
//proxy是代理对象,Method是代理对象中被调用的谋改革方法,args是调用这个method是传入的参数.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里只是简单的打印出被拦截的方法,然后调用proxied对象的相应方法进行处理
System.out.println("拦截了: "+method.getName());
return method.invoke(proxied,args);
}
}
public class TestProxy {
public static void getCarInfo(Car car) {
car.getPrice();
car.getName();
}
public static void main(String[] args) {
Bicycle bicycle = new Bicycle();
//使用Proxy创建一个动态代理
Car car = (Car) Proxy.newProxyInstance(bicycle.getClass().getClassLoader(), new Class<?>[]{Car.class},