JAVA【设计模式】代理模式(proxy)
一、基本介绍
1、为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
2、代理模式有不同的形式, 主要有静态代理和动态代理, 动态代理分为JDK代理和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口)。
二、分类
2.1 静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
例如:读大学的时候,大家都有遇到过这样的事情吧。某一天你走在校园的林荫小道上,路过了一个漂亮的女生,这时你的内心波涛汹涌,暗暗发誓她一定要成为你的女朋友。但是你直接上去联系或者表达好感这样会有点太唐突了,于是你迂回战术,先跟她的室友闺女拉近关系,让她帮忙送礼物给你的女神,然后。。。
首先得出现一个美女:BeautifulGirl
package com.qf.staticproxy;
public class BeautifulGirl {
private String name;
public BeautifulGirl() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后是抽象主题,送礼物:SendGift
package com.qf.staticproxy;
public interface SendGift {
void sendCard(String name);
void sendFlower(String name);
void sendMessage(String name);
}
你小子准备好礼物:You
package com.qf.staticproxy;
public class You implements SendGift {
@Override
public void sendCard(String name) {
System.out.println(name+"送你一个卡片");
}
@Override
public void sendFlower(String name) {
System.out.println(name+"送你一束花");
}
@Override
public void sendMessage(String name) {
System.out.println(name+"送你一束花");
}
}
她得室友闺蜜(代理对象)负责送礼物:HerTeam
package com.qf.staticproxy;
public class HerTeam implements SendGift{
@Override
public void sendCard(String name) { new You().sendCard(name); }
@Override
public void sendFlower(String name) {
new You().sendFlower(name);
}
@Override
public void sendMessage(String name) {
new You().sendMessage(name);
}
}
最终礼物是成功送了出去,你也捕获到你女神的芳心了。。。
package com.qf.staticproxy;
public class Test {
public static void main(String[] args) {
BeautifulGirl girl=new BeautifulGirl();
girl.setName("小甜甜");
HerTeam herTeam=new HerTeam();
herTeam.sendCard(girl.getName());
herTeam.sendFlower(girl.getName());
herTeam.sendMessage(girl.getName());
}
}
运行结果
小甜甜送你一个卡片
小甜甜送你一束花
小甜甜送你一束花
总结:
优点:体会到了代理模式的好处,一定的程度上降低了系统的耦合度,保护了被代理对象
缺点:一旦新增了接口,就要再次维护代理对象和被代理对象
2.2 jdk动态代理
基于jdk的反射机制,在java.lang.reflect.Proxy下面
通过调用newProxyInstance方法得到代理对象,前提是要实现接口newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
例如:每个男人都有信仰,都希望成为超人,拯救地球维护世界和平,但是归根结底还是人类,需要饮食。。。
抽象主题,信仰,吃饭:Man
package com.qf.dynimicproxy;
public interface Man {
/**
* 信仰
* @return
*/
String getBelief();
/**
* 吃东西
* @param name
*/
void eat(String name);
}
有思想需要饱腹一日三餐的人类:Human
package com.qf.dynimicproxy;
public class Human implements Man{
@Override
public String getBelief() {
return "I believe i can fly";
}
@Override
public void eat(String name) {
System.out.println("吃:"+name);
}
}
基于jdk的动态代理,获取代理对象:ProxyFactory
package com.qf.dynimicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
public static Object getProxyInstance(Object obj){ //被代理的对象
MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
myInvocationHandler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),myInvocationHandler);
}
}
class MyInvocationHandler implements InvocationHandler {
private Object obj;
public void bind(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(obj, args);
return result;
}
}
测试运行:ProxyTest
package com.qf.dynimicproxy;
public class ProxyTest {
public static void main(String[] args) throws NoSuchMethodException {
Human human=new Human();
Man proxyInstance = (Man) ProxyFactory.getProxyInstance(human);
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("小火锅");
}
}
运行结果
I believe i can fly
吃:小火锅
总结:
特点:不用每次都像静态代理那样,生成代理对象了,只需要实现接口即可获得
2.3 cglib动态代理
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。
注意:使用前可以下载cglib的官方jar包,更方便的方式直接导入maven依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
例如:生活中我们常常需要找房子,这时我们通过房产中介就能很快的找到房子,我们只需要付定金就行了
我,需要找房子,我只要付钱就好了:Person
package com.qf.cglib;
public class Person {
public void rentHouse(){
System.out.println("付定金");
}
}
基于cglib的动态代理,获取代理对象:ProxyUtils
package com.qf.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyUtils implements MethodInterceptor {
private Object target;
public Object createProxyInstance(Object target){
this.target=target;
//创建一个工具类
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 result = method.invoke(target, objects);
return result;
}
}
代理测试:ProxyTest
package com.qf.cglib;
import com.qf.staticproxy.You;
public class ProxyTest {
public static void main(String[] args) {
Person person=new Person();
Person proxyInstance = (Person) new ProxyUtils().createProxyInstance(person);
proxyInstance.rentHouse();
}
}
运行结果:
代理对象在帮忙找房子
付定金
总结:
特点:cglib是基于创建子类对象而产生的,不用像静态代理和jdk代理需要实现定义接口。
在AOP编程中如何选择代理模式:
目标对象需要实现接口,用JDK代理
目标对象不需要实现接口,用Cglib代理