设计模式之代理模式
代理模式(Proxy) 为其他对象提供一个代理以控制对这个对象的访问。
代理模式实现又分静态代理和动态代理。
结构图
代码示例
静态代理
需求:某一天,我和朋友老王聚会时,看见他的朋友娇妹很漂亮,想追求她。但是我又不好意思直接和娇妹打招呼,就想通过老王进行介绍认识。
实现时目标类和代理类实现同一个接口即可。
首先,定义打招呼的接口
public interface IMeetGirl {
String sayHello();
String doMyGirlFriend();
}
真实的请求对象我:
public class Me implements IMeetGirl {
private BeautifulGirl girl;
public Me(BeautifulGirl girl) {
this.girl = girl;
}
//定义真实的行为
@Override
public String sayHello() {
return girl.getName()+" 你好,我是一个大帅比!";
}
@Override
public String doMyGirlFriend() {
return girl.getName()+" 做我女朋友吧!";
}
}
代理对象老王:
public class LaoWang implements IMeetGirl {
private Me me;
public LaoWang(BeautifulGirl girl) {
me = new Me(girl);
}
//在代理方法中调用真实的方法
@Override
public String sayHello() {
return me.sayHello();
}
@Override
public String doMyGirlFriend() {
return me.doMyGirlFriend();
}
}
实体类
public class BeautifulGirl {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class test {
public static void main(String[] args) {
BeautifulGirl girl = new BeautifulGirl();
girl.setName("娇妹");
LaoWang proxy = new LaoWang(girl);
System.out.println(proxy.sayHello());
System.out.println(proxy.doMyGirlFriend());
//娇妹 你好,我是一个大帅比!
//娇妹 做我女朋友吧!
}
}
这样,我们就通过老王成功的向娇妹打招呼了。
动态代理
在Spring中,AOP的实现离不开动态代理。而实现动态代理又可通过jdk或者cglib来实现。
jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。
JDK动态代理
使用jdk动态代理需要基于接口编程
public interface HelloService {
void sayHello();
}
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello() {
System.out.println("Hello World!");
}
}
/**
* 使用此种方法动态代理对象需要基于接口编程
*/
public class HelloServiceJdkProxyFactory implements InvocationHandler {
//需要被代理的目标对象
private HelloService helloService;
public HelloServiceJdkProxyFactory(HelloService helloService) {
this.helloService = helloService;
}
//生成代理对象
public HelloService newProxyInstance() {
/**
* loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
*/
return (HelloService) Proxy.newProxyInstance(helloService.getClass().getClassLoader(),
helloService.getClass().getInterfaces(), this);
}
/**
* @param proxy 被代理的对象
* @param method 要调用的方法
* @param args 方法调用时所需要参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before hello world");
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object o = method.invoke(helloService, args);
System.out.println("after hello world");
return o;
}
}
@Test
public void jdkProxyTest() {
HelloService helloService = new HelloServiceImpl();
HelloServiceJdkProxyFactory proxy = new HelloServiceJdkProxyFactory(helloService);
proxy.newProxyInstance().sayHello();
}
测试结果:
before hello world
Hello World !
after hello world
CGlib动态代理
/**
* Cglib动态代理,需要cglib包和asm依赖包
**/
public class HelloServiceCglibProxyFactory implements MethodInterceptor {
public Object newProxyInstance(Class c) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(c);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before hello world");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("after hello world");
return invoke;
}
}
@Test
public void cglibProxyTest() {
HelloServiceCglibProxyFactory proxy = new HelloServiceCglibProxyFactory();
HelloService helloService = (HelloService) proxy.newProxyInstance(HelloServiceImpl.class);
helloService.sayHello();
}
测试结果:
before hello world
Hello World !
after hello world
那么什么样的需求时候适合使用代理模式呢?
代理模式的使用场景:
远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
虚拟代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。通过它来存放需要很长时间的真实对象
安全代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
智能代理,指当调用真实的对象时,代理处理另外一些事。如计算真实对象的引用次数,当该对象没有引用时,可以自动释放它。
本文地址:http://blog.youkuaiyun.com/ProdigalWang/article/details/75675756