简述Java代理模式的设计与实现
1.代理对象存在的价值主要用于拦截对真实业务对象的访问,建立代理类是为了增强、扩展被代理类(即目标对象)的功能,实际上业务还是调用目标对象的方法。
2.实现:
第一步:先写一个接口,接口中封装了客户端(目标对象)需要的执行的操作
第二步:代理和客户端同时实现该接口,其中客户端中真正实现了接口中的方法,是一个真正的实体类,而代理只是保存了一个客户端的引用,使得其能访问该实体,代理在实现的接口中通过反射调用实体中的方法
其实代理模式有点像明星与经纪人的关系,明星不用操心接通告接代言的事,这些都交由经纪人打理,但最后演戏拍广告都由明星出演。这个例子中明星就相当于目标对象,经纪人就是代理对象。
动态代理技术
动态代理是一种较为高级的代理模式,动态代理技术主要有JDK,CGLIB,Javasst ,ASM。其中JDK主要针对接口代理,它的典型应用就是Spring AOP。CGLIB针对类进行代理。
静态代理实例
//这里创建了一个接口
public interface Study {
void math(String math);
void english(String english);
}
//下面是接口实现类
public class StudyImpl implements Study{
@Override
public void math(String math) {
System.out.println("考试科目为:"+math);
}
@Override
public void english(String english) {
System.out.println("您正在进行"+english);
}
}
//这是一个静态代理类
public class staticProxy implements Study{
Study stu;
public staticProxy(Study target) {
this.stu=target;
}
public static void main(String[] args) {
Study st=new StudyImpl();
staticProxy s=new staticProxy(st);
s.math("数学");
s.english("英语");
}
@Override
public void math(String math) {
System.out.println("考试开始,请在答题纸上做答");
stu.math("数学");
System.out.println("考试结束,请放下笔等待收卷");
}
@Override
public void english(String english) {
System.out.println("考试开始,请在答题纸上做答");
stu.english("英语");
System.out.println("考试结束,请放下笔等待收卷");
}
}
ouput:
考试开始,请在答题纸上做答
考试科目为:数学
考试结束,请放下笔等待收卷
考试开始,请在答题纸上做答
考试科目为英语
考试结束,请放下笔等待收卷
通过实例我们可以看出在静态代理类中实现了增强的方法,而且调用的是实现类的方法
动态代理实例
以下实例为jdk动态代理
//这里动态代理实现的接口和代理类与静态代理相同,创建了一个ProxyHandler类来增强方法
public class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("考试开始,请在答题纸上做答");
Object obj= method.invoke(target,args);
System.out.println("考试结束,请放下笔等待收卷");
return obj;
}
//动态代理类
public class proxyTest{
public static void main(String[] args) {
Study s=new StudyImpl();
ProxyHandler p=new ProxyHandler(s);
Study proxy= (Study) Proxy.newProxyInstance(s.getClass().getClassLoader(),s.getClass().getInterfaces(),p);
proxy.math("数学");
System.out.println("代理对象proxy的类型"+proxy.getClass());
System.out.println("代理对象proxy的父类"+proxy.getClass().getGenericSuperclass());
}
}
output:
考试开始,请在答题纸上做答
考试科目为:数学
考试结束,请放下笔等待收卷
代理对象proxy的类型class com.sun.proxy.$Proxy0
代理对象proxy的父类class java.lang.reflect.Proxy
总结
- 由动态代理实例可以看到,com.sun.proxy.$Proxy0 代理对象继承了一个java.lang.reflect.Proxy类并且实现了代理接口。而java语言允许只单继承,接口可以有多个。所以jdk动态代理不能代理类,只能代理接口。
- 动态代理与静态代理相比,当有多个类需要增强方法时,静态代理会重复写多个代理类,增加工作量。而使用动态代理创建了ProxyHandler,代理类只需将被代理类传入ProxyHandler的构造器中便可增强方法
- jdk动态代理需要提供接口才能代理,当有些类不方便提供接口时我们就要使用cglib动态代理了,它只要提供一个非抽象类就能进行代理