代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问。
代理模式结构图如下:
代理模式结构图代码解析:
//代理类和真实类的公共接口(接口或抽象类)
public abstract class Subject{
public abstract void request();
}
//真实类,RealSubject类,定义 Proxy类所代表的真实实体
public class RealSubject extends Subject{
@Override
public void reqeust(){
print("真实的请求....");
}
}
//代理类,Proxy类,用来保存一个引用使得代理可以访问实体,并提供一个与Subject接口相同的接口,这样代理类就可以替代实体。
public class Proxy extends Subject{
RealSubject realSubject; //Spring的DI在这里挺好用
//调用Proxy的request方法就等于调用真实类的reqeust方法
@Override
public void reqeust(){
if(realSubject == null){
realSubject = new RealSubject();
}
realSubject.Reqeust();
}
}
//客户端代码
public class Main{
public static void main(String[] args){
//创建代理类
Proxy proxy = new Proxy();
proxy.reqeust(); //内部实质调用了真实类的request()方法
}
}
代理模式的应用:
①远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
②. 虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的对象。这样可以达到性能的优化,比如在打开一个很大的HTML网页,里面有很多文字和图片,但还是可以很快的打开它,此时会先看到文字,而图片是一张一张下载后才能看到。那些没有打开的图片框,就是通过虚拟代理替代了真实的图片,此时代理存储了真实图片的路径和尺寸。
③. 安全代理:用来控制对真实对象访问时的权限。一般用于对象应该有不同访问权限的时候。
④. 智能代理:指当调用真实对象时,代理处理另外一些事。
上述代理模式是最简单的一种代理,即静态代理。静态代理的优点是:可以在不修改原类的情况下,对原来的类进行扩展。但它的缺点也很明显:因为代理类要和目标对象类实现相同的接口,因此会有很多代理类,并且如果接口中增加方法,代理类和目标对象类都要进行维护。
解决静态代理缺点的方式就是使用动态代理。
动态代理的优点有哪些?
①. 代理类不用实现和目标类一样的接口。
②. 代理对象的产生是利用JDK的API,动态的在内存中构建代理对象<需要我们指定创建代理对象/目标对象实现的接口的类型>。
③. 动态代理,也称jdk代理,接口代理。
JDK中生成代理对象的API:
代理类所在的包:java.lang.reflect.Proxy
JDK实现代理需要使用一个方法:newProxyInstance方法,该方法有三个参数。方法的完整写法时:static Object newProxyInstance(ClassLoader loader,Class
/**
* Created by 杨Sir on 2017/11/14.
* 动态代理的接口
*/
public interface IGiveGift {
//送花的方法
void GiveFlowers();
}
/**
* Created by 杨Sir on 2017/11/14.
*/
public class Girl {
private String name;
public Girl() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* Created by 杨Sir on 2017/11/14.
* 目标类
*/
public class Pursuit implements IGiveGift {
Girl girl;
public Pursuit(Girl girl) {
this.girl = girl;
}
@Override
public void GiveFlowers() {
System.out.println(girl.getName()+ ": 送你鲜花。");
}
}
/**
* Created by 杨Sir on 2017/11/14.
* 动态代理类,创建代理对象,
* 不要实现接口,但要指定和目标对象一样的接口类型
*/
public class DynamicPorxy {
//维护一个目标对象
private Object target;
public DynamicPorxy(Object target) {
this.target = target;
}
//给目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("去买鲜花");
//执行目标对象的方法
Object returnValue = method.invoke(target,args);
System.out.println("送花完成");
return returnValue;
}
});
}
}
public class Main {
public static void main(String[] args) {
//创建一个Girl对象
Girl girl = new Girl();
girl.setName("校花");
//目标对象
Pursuit pursuit = new Pursuit(girl);
//根据目标对象生成代理对象,这个代理对象在不改动目标类的情况下扩展了目标类的功能。
IGiveGift giveGift = (IGiveGift) new DynamicPorxy(pursuit).getProxyInstance();
//调用代理对象的方法
giveGift.GiveFlowers();
}
}
需要注意的是,使用动态代理时,代理类虽然不需要实现接口,但是目标对象类一定要实现接口,否则不能用动态代理。
上面的两种代理方式都需要目标类实现一个接口,当如果目标类没有实现接口的话,那怎么创建目标对象的代理对象呢?答案是使用 Cglib 代理。
前面说的静态代理和动态代理都需要目标对象实现一个接口,当如果目标类没有实现任何接口的话,那怎么创建目标对象的代理对象呢?答案是使用 Cglib代理。
Cglib代理,也叫做子类代理,它通过在内存中构建一个子类对象从而实现对目标对象的扩展。
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就需要使用Cglib代理实现。
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,进而为他们提供方法的interception(拦截)。
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
Cglib代理的实现方法:
引入Cglib的jar包, 幸运的是Spring的core包已经引入了CGLIB的jar包,所以使用Spring的项目不用再次引入cglib的jar。
引入jar后,即可在内存中构建代理目标类的子类。
代理的类不能加 final。
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象之外的业务方法。
在Spring的面向切面编程(aop)中,如果加入容器的目标对象实现了接口,用动态代理,否则用 CGLIB代理。
举个简单例子看一下:
/**
* Created by 杨Sir on 2017/11/15.
* 目标类
*/
public class Pursuit {
public void GiveFlowers() {
System.out.println("我是目标类。");
}
}
/**
* Created by 杨Sir on 2017/11/15.
* CGLIB代理 创建目标对象的代理对象
*/
public class CglibProxy implements MethodInterceptor {
//维护目标对象
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
//为目标对象创建代理对象
public Object getProxyInstance(){
//工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建代理对象(目标类的子类)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("购买鲜花...");
//执行目标对象的方法
Object returnVal = method.invoke(target,objects); //传入目标对象和方法的参数
System.out.println("送花完成...很开心");
return returnVal;
}
}
/**
* Created by 杨Sir on 2017/11/15.
* 测试
*/
public class Main {
public static void main(String[] args) {
//创建目标对象
Pursuit pursuit = new Pursuit();
//代理对象
Pursuit proxy = (Pursuit) new CglibProxy(pursuit).getProxyInstance();
//执行代理对象方法,送花
proxy.GiveFlowers();
}
}