什么是代理模式
代理模式是一种结构型设计模式,简单解释就是使用代理对象调用目标对象,并且在不改变源码的情况下,实现对目标对象的功能扩展。
代理类中的两个角色
目标类
代理类
举个例子,有个艺术表演者,他的核心任务是表演才艺
public class Performer{
public void perform(){
System.out.println("表演才艺");
}
}
他在表演才艺之前,先跟观众打招呼,表演完才艺之后,向观众致谢
public void perform(){
System.out.println("各位观众朋友们大家好");
System.out.println("表演才艺");
System.out.println("感谢大家的观看");
}
核心任务是表演才艺,非核心任务是打招呼跟致谢,我们想把这两项分开。表演者类设计为目标类,只保留表演才艺的核心任务。再设计一个代理类,实现对目标类的调用与扩展,代理类中实现打招呼跟致谢的功能,这就是我们用到的代理模式。
分类
代理模式分为静态代理跟动态代理,动态代理又分为jdk动态代理跟cglib动态代理
静态代理
使用静态代理重构之前的例子
表演接口
public interface IPerform {
public void perform();
}
表演者目标类
public class Performer implements IPerform {
@Override
public void perform() {
System.out.println("表演才艺");
}
}
表演者代理类
public class PerformProxy implements IPerform {
//接收目标对象
private IPerform target;
public PerformProxy(IPerform perform) {
this.target = perform;
}
@Override
public void perform() {
//表演前先向观众们问好
System.out.println("各位观众朋友们大家好");
//调用目标方法
target.perform();
//表演完成向观众致谢
System.out.println("感谢大家的观看");
}
}
测试
public class Test {
public static void main(String[] args) {
//目标对象
IPerform perform = new Performer();
//代理对象,将目标对象注入
IPerform performProxy = new PerformProxy(perform);
performProxy.perform();
}
}
测试结果
各位观众朋友们大家好
表演才艺
感谢大家的观看
静态代理总结
静态代理中,将目标类注入到代理类中,实现在代理类中调用目标类的方法,并扩展目标类的功能。但是代理对象需要与目标对象实现一样的接口,代理对象必须提前写出,如果接口层发生了变化,代理对象的代码也要进行维护。如果能在运行时动态地写出代理对象,不但减少了一大批代理类的代码,也少了不断维护的烦恼。这种方式就是接下来的动态代理
jdk动态代理
jdk动态代理代理类所在的包为:java.lang.reflect.Proxy,调用Proxy类的静态方法newProxyInstance,该方法会返回代理类对象。
newProxyInstance(ClassLoader loader, Class>[] interfaces,InvocationHandler h )接收的三个参数依次为:
ClassLoader loader:指定当前目标对象使用类加载器,写法固定
Class>[] interfaces:目标对象实现的接口的类型,写法固定
InvocationHandler h:事件处理接口,需传入一个实现类,一般直接使用匿名内部类
java底层封装了实现细节
使用jdk动态代理重构之前的例子
表演接口
public interface IPerform {
public void perform();
}
表演者目标类
public class Performer implements IPerform {
@Override
public void perform() {
System.out.println("表演才艺");
}
}
表演者代理类
public class PerformProxy {
//接收目标对象
private IPerform target;
public PerformProxy(IPerform perform) {
this.target = perform;
}
//使用jdk动态代理,为目标对象生成代理对象
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 Test {
public static void main(String[] args) {
//目标对象
IPerform perform = new Performer();
//代理对象,将目标对象注入
PerformProxy performProxy = new PerformProxy(perform);
IPerform proxy = (IPerform)performProxy.getProxyInstance();
//通过代理对象调用目标对象方法
proxy.perform();
}
}
jdk动态代理总结
jdk动态代理中的目标对象必须实现接口,否则无法使用该代理方式。如果目标对象没有实现接口,我们可以使用cglib动态代理。
cglig动态代理
前提条件:
需要引入cglib的jar文件与asm的jar文件,cglib依赖于asm。由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
目标类不能为final
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
使用jdk动态代理重构之前的例子
表演者目标类
public class Performer {
public void perform() {
System.out.println("表演才艺");
}
}
表演者代理类
public class PerformProxy implements MethodInterceptor {
//接收目标对象
private Object target;
public PerformProxy(Object perform) {
this.target = perform;
}
//获取目标对象的代理对象
public Object getProxyInstance(){
//工具类
Enhancer en = new Enhancer();
//设置父类对象
en.setSuperclass(this.target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类,也就是代理对象
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//表演前先向观众们问好
System.out.println("各位观众朋友们大家好");
//执行目标对象的方法
Object returnValue = method.invoke(target, objects);
//表演完成向观众致谢
System.out.println("感谢大家的观看");
return returnValue;
}
}
测试
public class Test {
public static void main(String[] args) {
//目标对象
Performer perform = new Performer();
PerformProxy performProxy = new PerformProxy(perform);
//生成代理对象
Performer proxy = (Performer)performProxy.getProxyInstance();
proxy.perform();
}
}
在Spring的AOP编程中: 如果加入容器的目标对象有实现接口,用JDK代理 如果目标对象没有实现接口,用Cglib代理