概念
为其他类提供一种代理,以控制对这个类的访问
代理可以理解为助理,中介等,你想找直接领导只能找中间者,然后通过中间者去找领导
在面向对象系统中,有些对象由于(创建对象开销大、操作安全控制、需要进程外的访问)等一系列的问题,直接访问会带来很多麻烦,所以我们在访问此对象时加上一个此对象的访问层
角色
抽象角色:通过接口或抽象类声明真实角色实现的业务方法
代理角色:实现抽象角色,是真实角色的代理
真实角色:实现抽象角色,定义真是角色需要实现的业务逻辑,供代理角色调用
设计与实现
设计思想:汽车移动,计时,日志
一、没有代理
1、提供移动接口
package com.xjion.noproxy;
public interface Moveable {
void move();
}
2、提供汽车
package com.xjion.noproxy;
import java.util.Random;
public class Car implements Moveable{
@Override
public void move() {
System.out.println("汽车开始移动...");
try {
long startTime=System.currentTimeMillis();
System.out.println("汽车移动中...");
Thread.sleep(new Random().nextInt(2000));
long endTime=System.currentTimeMillis();
System.out.println("汽车运行结束,消耗时间为:"+(endTime-startTime)+"毫秒数!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、测试
package com.xjion.noproxy;
public class Test {
public static void main(String[] args) {
Moveable car=new Car();
car.move();
}
}
二、通过继承实现静态代理
1、移动接口
package com.xjion.staticproxy;
public interface Moveable {
void move();
}
2、汽车
package com.xjion.staticproxy;
import java.util.Random;
public class Car implements Moveable{
@Override
public void move() {
try {
System.out.println("汽车移动中...");
Thread.sleep(new Random().nextInt(2000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、代理添加日志,计时
package com.xjion.staticproxy;
/**
* 通过继承的方式实现静态代理
*
*/
public class LogTimeProxy extends Car{
@Override
public void move() {
System.out.println("记录开始日志");
System.out.println("汽车开始移动...");
long startTime=System.currentTimeMillis();
super.move();
long endTime=System.currentTimeMillis();
System.out.println("汽车停止移动,移动的时间为:"+(endTime-startTime)+"毫秒!");
System.out.println("记录结束日志");
}
}
4、测试
package com.xjion.staticproxy;
public class Test {
public static void main(String[] args) {
Moveable car=new LogTimeProxy();
car.move();
}
}
三、通过实现接口实现静态代理
1、提供移动接口,汽车
同上 ↑
2、提供计时功能
package com.xjion.staticproxy2;
public class TimeProxy implements Moveable{
private Moveable moveable;
public TimeProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
System.out.println("汽车开始移动...");
long startTime=System.currentTimeMillis();
moveable.move();
long endTime=System.currentTimeMillis();
System.out.println("汽车停止移动,移动的时间为:"+(endTime-startTime)+"毫秒!");
}
}
3、提供日志功能
package com.xjion.staticproxy2;
public class LogProxy implements Moveable{
private Moveable moveable;
public LogProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
System.out.println("记录日志开始...");
moveable.move();
System.out.println("记录日志结束...");
}
}
4、测试
package com.cdsxt.staticproxy2;
public class Test {
public static void main(String[] args) {
//计时并打印日志
//Moveable car=new TimeProxy(new LogProxy(new Car()));
//计时
Moveable car=new TimeProxy(new Car());
car.move();
}
}
四、实现InvocationHandler接口实现动态代理
1、汽车和移动同上
2、飞机和飞行接口
package com.xjion.dynamicproxy;
public interface Flyable {
void fly();
}
package com.xjion.dynamicproxy;
import java.util.Random;
public class Aircraft implements Flyable{
@Override
public void fly() {
try {
System.out.println("飞机在飞飞飞...");
Thread.sleep(new Random().nextInt(2000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、代理
package com.xjion.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 1)实现InvocationHandler,重写接口的方法
* 2)获取代理对象 Proxy.newProxyInstance调用
*/
public class LogProxy implements InvocationHandler{
private Object obj;
//给被代理对象赋值
public LogProxy(Object obj) {
this.obj = obj;
}
/**
* proxy 代理对象
* method 被调用的方法
* args 实参列表
* return 调用方法之后的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("开始日志");
//相当于之前的super.move()
method.invoke(obj, args);
System.out.println("结束日志");
return null;
}
}
4、测试
package com.xjion.dynamicproxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// Moveable car=(Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(),
// Car.class.getInterfaces(), new LogProxy(new Car()));
// car.move();
Flyable aircraft=(Flyable) Proxy.newProxyInstance(Aircraft.class.getClassLoader(),
Aircraft.class.getInterfaces(), new LogProxy(new Aircraft()));
aircraft.fly();
}
}
注意:
Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader
:指定当前目标对象使用的类加载器,获取加载器的方法是固定的Class<?>[] interfaces
:指定目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler:
指定
动态处理器,
执行目标对象的方法时,会触发事件处理器的方法
五、CGLIB代理
1、汽车移动
package com.xjion.cglibproxy;
import java.util.Random;
public class Car{
public void move() {
try {
System.out.println("汽车移动中...");
Thread.sleep(new Random().nextInt(2000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、飞机飞行
package com.xjion.cglibproxy;
import java.util.Random;
public class Aircraft{
public void fly() {
try {
System.out.println("飞机在飞飞飞...");
Thread.sleep(new Random().nextInt(2000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、代理
package com.xjion.cglibproxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class TimeProxy implements MethodInterceptor{
/**
* 提供返回代理对象的方法
*/
public Object getProxy(Class clazz){
//创建代理对象工具实例
Enhancer enhancer=new Enhancer();
//设置被代理对象的反射对象
enhancer.setSuperclass(clazz);
//调用回调函数
enhancer.setCallback(this);
//返回代理对象
return enhancer.create();
}
/**
* obj 被代理的对象
* method 被代理的方法
* args 调用方法的实参
* proxy 代理对象
*
* 返回值是 调用方法后的返回值
*/
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
long startTime=System.currentTimeMillis();
//调用被代理的方法
proxy.invokeSuper(obj, args);
long endTime=System.currentTimeMillis();
System.out.println("汽车运行结束,使用时间为"+(endTime-startTime)+"毫秒!");
return null;
}
}
4、测试
package com.xjion.cglibproxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// Car car=(Car) new TimeProxy().getProxy(Car.class);
// car.move();
Aircraft aircraft=(Aircraft) new TimeProxy().getProxy(Aircraft.class);
aircraft.fly();
}
}
总结:
两种静态代理:
1、可以在符合开闭原则的情况下对目标进行功能扩展
2、不易于管理,接口变实现类也会变
JDK动态代理:
1、减少开发任务
2、降低对接口的依赖,从而降低了耦合度
3、CHLIB创建动态代理对象性能低
4、创建对象花费时间少
CGLIB代理:
1、使用字节码技术
2、CHLIB创建动态代理对象性能高
3、创建对象花费时间多
4、适合创建单例对象
与装饰器模式相比
装饰器模式:
1、不改变接口动态扩展功能
2、将对象传递给Decorator的构造函数使用
3、增强自身的功能
代理模式:
1、提供代理对象,不改变接口控制对象的访问
2、创建真实对象的实例
3、让比尔去做一些和本身业务没有太多关系的职业
共同点
1、都是实现同一个接口,一个类包装另一个类
2、都是对类的方法进行扩展