代理模式的要点
代理模式的要点是:代理要获得和目标对象(被代理的对象)一样的方法,这样就能在对外提供方法的同时做到
1、隐藏目标对象
2、功能增强
代理类怎么获得和目标对象一样的方法?
实现相同的接口
这里我以用户登录为例
创建UserServer接口
public interface UserServer {
void login(String name, String Password);
}
它的实现类:UserServerImpl
public class UserServerImpl implements UserServer{
@Override
public void login(String name, String Password) {
System.out.println("登录中.....");
}
}
它的实现类UserServerProxy,即代理
public class UserServerProxy implements UserServer {
@Override
public void login(String name, String Password) {
//1、拿到真实对象
//2、功能增强
//提供服务
}
}
在代理类中需要做的两件事:1、拿到真实对象对外提供真正的服务。 2、做功能增强
public class UserServerProxy implements UserServer {
@Override
public void login(String name, String Password) {
//1、拿到真实对象
UserServerImpl serverImpl = new UserServerImpl();
//2、功能增强
System.out.println("日志或增强");
//调用真正的login服务
serverImpl.login(name,Password);
}
}
至此,静态代理完成
总结:
代理类 = 与真实类有相同的接口 + 拿到真实类提供服务+ 额外功能
JDK动态代理
静态代理,之所以叫静态是因为在程序运行前代理类(即UserServerProxy )的 .java 和 .class 文件就已经存在了。
而且上文中的代理类仅针对了UserServer这一服务,也就是说如果其他对象也需要代理那我们就得再新增一个代理类。
所以需要动态代理
JDK动态代理是指JDK自带的动态代理
首先
java原生里提供了Proxy类,它来自反射类
并提供了newProxyInstance方法,并且包含了三个参数ClassLoader loader 、Class<?>[] interfaces 、InvocationHandler h
参数1 ClassLoader loader(类加载器)
前面说过,静态代理之所以是静态是因为在程序运行前代理类的 .java 和 .class 文件就已经存在了。而在这里可以实时的把类转递过去
参数2 Class<?>[] interfaces(接口类)
前面说过:代理类 = 与真实类有相同的接口 + 拿到真实类提供服务+ 额外功能
因此这个参数就是用来提供真实类接口的
参数3 InvocationHandler h
这里直接打出来看看
Proxy.newProxyInstance(UserController.class.getClassLoader(), new Class[]{UserServer.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
可以看到,InvocationHandler 是一个函数式接口,内部有一个让我们重写的invoke方法
invoke() 方法有三个参数,分别是:proxy、method 和 args。
proxy :表示代理对象,也就是说 Proxy.newProxyInstance() 方法创建出的代理对象也会作为 invoke()方法的参数,但我们一般不使用 proxy 参数;
method :表示需要被添加额外功能的原始方法,比如我需要给 login()方法添加额外功能,那么 method 就表示 login() 方法;
args :表示需要被添加额外功能的原始方法的参数列表,比如 login() 方法有两个参数,那个 args[0] 就表示其第一个参数,args[1] 表示其第二个参数。
在invoke() 方法中我们需要重复静态代理的步骤:即拿到真实对象,做功能增强,调用真实对象提供服务
Proxy.newProxyInstance(UserController.class.getClassLoader(), new Class[]{UserServer.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//拿到真实对象
UserServerImpl serverImpl = new UserServerImpl();
//功能增强
System.out.println("用户鉴权增强");
//提供服务,这里的method就是我们待会需要调用的login方法
Object result = method.invoke(serverImpl, args);
return result;
}
});
至此JDK动态代理完成
在jdk动态代理中,它把增强和调用这些步骤抽离了出去把这些交给了我们这些使用者来书写
最后运行
public class JdkProxy {
public static void main(String[] args) {
UserServer proxyInstance = (UserServer) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), new Class[]{UserServer.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//拿到真实对象
UserServerImpl serverImpl = new UserServerImpl();
//功能增强
System.out.println("用户鉴权增强");
//提供服务,这里的method就是我们需要调用的login方法
Object result = method.invoke(serverImpl, args);
return result;
}
});
proxyInstance.login(username,password);
}
}
Cglib代理
真实对象是父类,代理是其子类
public class CglibProxy {
static class Target{
public void foo(){
System.out.println("target foo");
}
}
public static void main(String[] args) {
Target target = new Target();
// 目标是父类,代理是目标的子类
Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("前置增强");
Object result;
result = method.invoke(target, args); //用反射调用目标
result = methodProxy.invoke(target,args); //内部没有反射,需要目标
result = methodProxy.invokeSuper(proxy,args); //内部没有反射,需要代理
return result;
}
});
proxy.foo();
}
}