一。代理模式:
代理模式可以通过聚合和继承两种方式实现,
聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现 灵活多变。
继承式的实现方式则不够灵活,一般而言我们更倾向于使用聚合实现方式来实现代理,如图 是聚合方式
二。静态代理模式
1.什么是代理模式?
代理这个词是来源于Java设计模式中的代理模式,代理模式最简单的理解就是通过第三方来代理我们的工作
例如: 房东 、中介 、租客 的关系
首先创建一个主题接口(创建接口,JDK动态代理就是以接口、对象、代理对象为基础)
public interface UserDao(){
void testAdd();
}
然后创建该接口的实现类(真实主题),也称为被代理类(主要功能就在于该类中),我们需要一个代理类来代理这个类实现他的testAdd方法
public class UserDaoImpl implements UserDao(){
@Override
public void testAdd(){
System.out.println("add.....");
}
}
然后我们创建上述实现类的代理类(两个类需要实现共同的主题接口,这样才能对原有功能进行增强),使用代理类对原有类进行包装
public class UserDaoProxy implements UserDao{
private UserDao target;
public UserDaoProxy(UserDao target){
this.target = target;
}
public void testAdd(){
System.out.println("before...");
target.add();
System.out.println("after...");
}
}
最后进行测试
@Test
public void test1(){
UserDao userDao = new UserDaoImpl();
UserDaoProxy proxy = new UserDaoProxy(userDao);
proxy.testAdd();
}
总结:
为什么要去代理?而且前两段代码就能实现的功能,为什么还需要一个代理类去代理它,这就涉及到了工作需求。
在不改变实现类的情况下,对实现类进行功能的增加,而上述代码中两个输出语句就是对功能的增强(当然可以根据实际需求来添加实际代码),由此而产生了代理类,生成代理对象
2.静态代理的概念:
静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。
三。动态代理模式
代理类在程序运行时创建的代理方式被称为动态代理。 我们上面静态代理的例子中,代理类(Proxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于进行可以很方便的对代理类的函数统一的处理,而不用修改每个代理类中的方法。
1.JDK代理
代理步骤:
(1)定义一个事件管理器类实现invocationHandle接口,并重写invoke(代理类,被代理的方 法,方法的参数列表)方法。
(2)实现被代理类及其实现的接口,
(3)调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代 理实例。
(4)通过该代理实例调用方法。
//1. 抽象主题
public interface Moveable {
void move() throws Exception;
}
//2. 真实主题
public class Car implements Moveable {
public void move() throws Exception {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中…");
}
}
//3.事务处理器
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
/**
* 参数:
*proxy 被代理的对象
*method 被代理对象的方法
*args 方法的参数
*Object 方法返回值
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶…");
method.invoke(target, args);
long stopTime = System.currentTimeMillis();
System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");
return null;
}
}
//测试类
public class Test {
public static void main(String[] args) throws Exception{
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class<?> cls = car.getClass();
/**
*loader 类加载器
*interfaces 实现接口
*h InvocationHandler
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
m.move();
}
}
在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。
2.cglib动态代理
因为Java只允许单继承,而JDK生成的代理类本身就继承了Proxy类,因此,使用JDK实现的动态代理不能完成继承式的动态代理,但是我们可以使用cglib来实现继承式的动态代理。
//1.具体主题
public class Train{
public void move(){
System.out.println("火车行驶中…");
}
}
//2.生成代理
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 拦截所有目标类方法的调用
* 参数:
* obj 目标实例对象
* method 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//代理类调用父类的方法
System.out.println("日志开始");
proxy.invokeSuper(obj, args);
System.out.println("日志结束");
return null;
}
}
//3.测试
public class Test {
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
Train t = (Train) proxy.getProxy(Train.class);
t.move();
}
}
3.小结
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 很美很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补。class与interface的区别本来就模糊,在java8中更是对interface进行了加强,使得interface越来越接近class,