镇江公司做网站,国内能用的搜索引擎,攻击自己做的网站,外贸营销活动方案概述
代理在计算机编程领域#xff0c;是一个很通用的概念#xff0c;包括#xff1a;代理设计模式#xff0c;代理服务器等。
代理类持有具体实现类的实例#xff0c;将在代理类上的操作转化为实例上方法的调用。为某个对象提供一个代理#xff0c;以控制对这个对象的…概述
代理在计算机编程领域是一个很通用的概念包括代理设计模式代理服务器等。
代理类持有具体实现类的实例将在代理类上的操作转化为实例上方法的调用。为某个对象提供一个代理以控制对这个对象的访问。代理类和委托类有共同的父类或父接口这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。为其他对象提供一种代理以控制对这个对象的访问。代理对象起到中介作用可以去掉功能服务或者增加额外的功能。
角色
代理模式一般涉及到的角色有
对象Client请求客户端抽象角色Subject声明真实对象和代理对象的共同接口对应代理接口真实角色RealSubject代理角色所代表的真实对象是最终要引用的对象对应委托类代理角色Proxy代理对象角色内部含有对真实对象的引用从而可以操作真实对象同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时代理对象可以在执行真实对象操作时附加其他的操作相当于对真实对象进行封装对应代理类可以有未公开的方法。
UML类图如下 调用顺序示意图
分类
代理从类型来分类
虚代理虚拟代理Virtual Proxy根据需要来创建开销很大的对象该对象只有在需要时才会被真正创建即所谓的延迟加载远程代理Remote Proxy用来在不同的地址空间上代表同一个对象这个不同的地址空间可以是在本机也可以在其它机器上在Java里面最典型的就是RMI技术copy-on-write代理在客户端操作时只有对象确实改变后才会真的拷贝一个日标对象算是虚代理的一个分支保护代理Protect Proxy控制对原始对象的访问如果有需要可以给不同的用户提供不同的访问权限以控制他们对原始对象的访问Cache代理为那些昂贵操作的结果提供临时的存储空间以便多个客户端可以共享这些结果防火墙代理保护对象不被恶意用户访问和操作同步代理使多个用户能够同时访问目标对象而没有冲突智能引用代理Smart Reference Proxy在访问对象时执行一些附加操作。比如对指向实际对象的引用计数、第一次引用一个持久对象时将它装入内存等
另外根据代理类的生成时间的不同可分为静态代理和动态代理。
静态代理
代理和被代理对象目标对象在代理之前是确定的都实现相同的接口或者继承相同的抽象类。目标对象作为代理对象的一个属性具体接口实现中代理对象可以在调用目标对象相应方法前后加上其他业务处理逻辑。由程序员创建或工具生成代理类的源码再编译代理类。所谓静态在程序运行前就已经存在代理类的字节码文件代理类和委托类的关系在运行前就确定。
实现静态代理有继承、聚合方法。
优点业务类只需要关注业务逻辑本身保证业务类的重用性。
缺点
需要大量硬编码一个代理类只能代理一个业务类如果业务类增加方法时相应的代理类也要增加方法
动态代理
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。优势在于可以很方便的对代理类的函数进行统一的处理添加方法调用次数、添加日志功能等而不用修改每个代理类的函数。分为JDK动态代理和cglib动态代理。
实现
一般有JDK和cglib等实现方式。
JDK
基于反射核心API包括 java.lang.reflect.ProxyJava动态代理机制生成的所有动态代理类的父类提供一组静态方法来为一组接口动态地生成代理类及其对象共4个static方法
public class Proxy implements java.io.Serializable {// 用于获取指定代理对象所关联的调用处理器public static InvocationHandler getInvocationHandler(Object proxy)// 用于获取关联于指定类装载器和一组接口的动态代理类的类对象public static class? getProxyClass(ClassLoader loader, Class?... interfaces)// 用于判断指定类对象是否是一个动态代理类public static boolean isProxyClass(Class? cl)// 用于为指定类装载器、一组接口及调用处理器生成动态代理类实例public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h)
}InvocationHandler接口主要用来处理执行逻辑源码
public interface InvocationHandler {// obj指代理类method指被代理的方法args为该参数的方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}使用JDK动态代理的步骤
创建被代理的类以及接口创建一个实现接口InvocationHandler并重写invoke方法的类调用Proxy的静态方法创建代理类newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)通过代理调用方法。
实例
代表Subject的接口
public interface Movable {void move();
}Car实现Movable接口是要代理的实际对象对应RealSubject
Slf4j
public class Car implements Movable {Overridepublic void move() {// 实现开车try {Thread.sleep(new Random().nextInt(1000));System.out.println(汽车行驶中....);} catch (InterruptedException e) {log.error(thread fail, e);}}
}对应于Proxy的TimeHandler类实现InvocationHandler接口并在invoke()方法中添加额外的逻辑用于在代理对象方法调用前后执行
public class TimeHandler implements InvocationHandler {private final Object target;TimeHandler(Object target) {super();this.target target;}/*** 参数* proxy被代理对象* method被代理对象的方法* args方法的参数* res方法的返回值*/Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime System.currentTimeMillis();System.out.println(汽车开始行驶....);Object res method.invoke(target, args);long endTime System.currentTimeMillis();System.out.println(汽车结束行驶.... 汽车行驶时间 (endTime - startTime) 毫秒);return res;}
}JDK动态代理测试类也就是上图中的Client类
public class Test {public static void main(String[] args) {Car car new Car();InvocationHandler h new TimeHandler(car);Class? cls car.getClass();/** loader类加载器* interfaces实现接口* hInvocationHandler*/Movable m (Movable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);m.move();}
}使用Proxy.newProxyInstance()方法创建代理对象指定Movable接口作为代理对象类型并将TimeHandler对象作为代理对象的InvocationHandler。
缺点
仍有硬编码需要在对象初始化时使用特定的方式进行初始化
源码分析
基于JDK-22以Proxy.newProxyInstance()方法为切入点
CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) {Objects.requireNonNull(h);SuppressWarnings(removal)final Class? caller System.getSecurityManager() null ? null : Reflection.getCallerClass();// 查找或生成指定的代理类及其构造函数Constructor? cons getProxyConstructor(caller, loader, interfaces);return newProxyInstance(caller, cons, h);
}基于源码的注释进一步查看getProxyConstructor代码
private static Constructor? getProxyConstructor(Class? caller, ClassLoader loader, Class?... interfaces) {// optimization for single interfaceif (interfaces.length 1) {Class? intf interfaces[0];if (caller ! null) {checkProxyAccess(caller, loader, intf);}return proxyCache.sub(intf).computeIfAbsent(loader, (ld, clv) - new ProxyBuilder(ld, clv.key()).build());} else {// interfaces clonedfinal Class?[] intfsArray interfaces.clone();if (caller ! null) {checkProxyAccess(caller, loader, intfsArray);}final ListClass? intfs Arrays.asList(intfsArray);return proxyCache.sub(intfs).computeIfAbsent(loader, (ld, clv) - new ProxyBuilder(ld, clv.key()).build());}
}核心方法是构建ProxyBuilder实例ProxyBuilder是java.lang.reflect.Proxy的静态内部类利用构造器模式
ProxyBuilder(ClassLoader loader, ListClass? interfaces) {// 在模块系统完全初始化之前不支持代理if (!VM.isModuleSystemInited()) {throw new InternalError(Proxy is not supported until module system is fully initialized);}// 接口数不得超过65535个if (interfaces.size() 65535) {throw new IllegalArgumentException(interface limit exceeded: interfaces.size());}SetClass? refTypes referencedTypes(loader, interfaces);// IAE if violates any restrictions specified in newProxyInstancevalidateProxyInterfaces(loader, interfaces, refTypes);this.interfaces interfaces;this.context proxyClassContext(loader, interfaces, refTypes);assert getLoader(context.module()) loader;
}核心方法之一referencedTypes检查是否为static方法
private static SetClass? referencedTypes(ClassLoader loader, ListClass? interfaces) {var types new HashSetClass?();for (var intf : interfaces) {for (Method m : intf.getMethods()) {// 不能为static方法if (!Modifier.isStatic(m.getModifiers())) {addElementType(types, m.getReturnType());addElementTypes(types, m.getSharedParameterTypes());addElementTypes(types, m.getSharedExceptionTypes());}}}return types;
}validateProxyInterfaces方法检查是否是接口
private static void validateProxyInterfaces(ClassLoader loader, ListClass? interfaces, SetClass? refTypes) {MapClass?, Boolean interfaceSet new IdentityHashMap(interfaces.size());for (Class? intf : interfaces) {// 检查是否为接口if (!intf.isInterface()) {throw new IllegalArgumentException(intf.getName() is not an interface);}// 检查是否为隐藏类JDK15引入新特性if (intf.isHidden()) {throw new IllegalArgumentException(intf.getName() is a hidden interface);}// 检查是否是密封类JDK17引入新特性if (intf.isSealed()) {throw new IllegalArgumentException(intf.getName() is a sealed interface);}// 验证类加载器是否将此接口的名称解析为同一个Class对象下同ensureVisible(loader, intf);// 检查是否已经生成过此接口的代理类if (interfaceSet.put(intf, Boolean.TRUE) ! null) {throw new IllegalArgumentException(repeated interface: intf.getName());}}for (Class? type : refTypes) {ensureVisible(loader, type);}
}为什么JDK动态代理只能代理接口
面试时常见的问题之一参考上面的源码分析实际上还可以补充回答JDK动态代理对密封类隐藏类不生效即不能代理密封类隐藏类。
cglib
Code Generation Library一款高性能Code生成类库的开源组件可在运行期扩展Java类与实现Java接口很多其他开源组件都在使用cglib
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调类型经常被基于代理的AOP用来实现拦截方法的调用接口只定义一个方法
public interface MethodInterceptor extends Callback {// object是代理对像method是拦截方法args是方法参数。原来的方法可通过使用Method对象的一般反射调用或使用MethodProxy对象调用后者更快Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable;
} 实例
创建代理类
public class CglibProxy implements MethodInterceptor {private final Enhancer enhancer new Enhancer();Object getProxy(Class? clazz) {// 设置创建子类的类enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}/*** 拦截所有目标类方法的调用*/Overridepublic Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {System.out.println(日志开始...);// 代理类调用父类的方法Object res proxy.invokeSuper(obj, args);System.out.println(日志结束...);return res;}
}测试类
public class Client {public static void main(String[] args) {CglibProxy proxy new CglibProxy();Train t (Train) proxy.getProxy(Train.class);t.move();}static class Train {void move() {System.out.println(火车行驶中...);}}
}源码分析
基于cglib最新版3.3.0来分析从enhancer.create()入手调用createHelper方法
private Object createHelper() {// 校验callbackTypes、filter是否为空及相应处理策略preValidate();Object key KEY_FACTORY.newInstance((superclass ! null) ? superclass.getName() : null,ReflectUtils.getNames(interfaces),filter ALL_ZERO ? null : new WeakCacheKeyCallbackFilter(filter),callbackTypes,useFactory,interceptDuringConstruction,serialVersionUID);this.currentKey key;Object result super.create(key);return result;
}核心方法是create
protected Object create(Object key) {try {ClassLoader loader getClassLoader();// 查询缓存MapClassLoader, ClassLoaderData cache CACHE;ClassLoaderData data cache.get(loader);// DCLif (data null) {synchronized (AbstractClassGenerator.class) {cache CACHE;data cache.get(loader);if (data null) {// 构建缓存MapClassLoader, ClassLoaderData newCache new WeakHashMapClassLoader, ClassLoaderData(cache);data new ClassLoaderData(loader);newCache.put(loader, data);CACHE newCache;}}}this.key key;Object obj data.get(this, getUseCache());if (obj instanceof Class) {// 用于向后兼容return firstInstance((Class) obj);}// 真正创建代理对象return nextInstance(obj);} catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);}
}不管是firstInstance还是nextInstance最后都是调用ReflectUtils.newInstance方法
public static Object newInstance(final Constructor cstruct, final Object[] args) {boolean flag cstruct.isAccessible();try {if (!flag) {cstruct.setAccessible(true);}// 使用JDKreturn cstruct.newInstance(args);} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {throw new CodeGenerationException(e);} finally {if (!flag) {cstruct.setAccessible(flag);}}
}最后使用JDK源码Constructor.newInstance(args);因此cglib不能对声明为final的方法进行代理因为cglib原理是动态生成被代理类的子类。
区别
主要区别
JDK利用拦截器实现InvocationHanlder加上反射机制生成一个实现代理接口的匿名类在调用具体方法前调用InvokeHandler来处理cglib利用ASM开源包对代理对象类的class文件加载进来通过修改其字节码生成子类来处理
具体来说 JDK动态代理只能针对实现接口的类生成代理实例化一个类。此时代理对象和目标对象实现相同的接口目标对象作为代理对象的一个属性具体接口实现中可以在调用目标对象相应方法前后加上其他业务处理逻辑。
cglib是针对类实现代理主要是对指定的目标类生成一个子类没有实例化一个类覆盖其中的方法通过方法拦截技术拦截所有父类方法的调用。
使用区别
JDK不能用于非接口类、隐藏类、未经允许扩展的密封类的子类cglib不能用于final方法、隐藏类、同上
Spring AOP
Spring AOP基于JDK Proxy和cglib来生成代理对象具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认策略是如果目标类是接口则使用JDK动态代理如果目标对象没有实现接口则默认会采用cglib代理。
代理模式将继承模式和关联模式结合在一起使用是两者的综合体不过这个综合体的作用倒不是解决对象注入的问题而是为具体操作对象找到一个保姆或者是秘书对外代表具体的实例对象实例对象的入口和出口都是通过这个二号首长具体的实例对象是一号首长一号首长是要干大事的所以一些事务性重复性的工作例如泡茶安排车子这样的工作是不用劳烦一号首长的大驾而是二号首长帮忙解决的这就是AOP的思想。AOP解决程序开发里事务性和核心业务无关的问题但这些问题对于业务场景的实现是很有必要的在实际开发里AOP也是节省代码的一种方式。
AOP将应用系统分为两部分核心业务逻辑Core business concerns及横向的通用逻辑也就是所谓的方面Crosscutting enterprise concerns例如大中型应用都会涉及到的持久化、事务、安全、日志和调试等。
实现AOP的技术主要分为两大类
动态代理技术利用截取消息的方式对该消息进行装饰以取代原有对象行为的执行静态织入的方式引入特定的语法创建Aspect从而使得编译器可以在编译期间织入有关Aspect代码。
拓展
代理服务器
一般有正向代理、反向代理。
正向代理
一个位于客户端和原始服务器(origin server)之间的服务器为了从原始服务器取得内容客户端向代理发送一个请求并指定目标(原始服务器)然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。如通过Chrome的SwitchSharp访问外网。
反向代理
反向代理以代理服务器来接受Internet上的连接请求然后将请求转发给内部网络上的服务器并将从服务器上得到的结果返回给Internet上请求连接的客户端此时代理服务器对外就表现为一个反向代理服务器。这个服务器没有保存任何网页的真实数据所有的静态网页或者CGI程序都保存在内部的Web服务器上。因此对反向代理服务器的攻击并不会使得网页信息遭到破坏这样就增强Web服务器的安全性。
反向代理经常和CDN一起工作其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节使内容传输的更快、更稳定。通过在网络各处放置反向代理节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容解决Internet网络拥挤的状况提高用户访问网站的响应速度。
区别
正向代理解决的是客户端访问互联网的问题客户端知道目标的 反向代理解决的是互联网收到客户端请求如何把请求转到内网服务器的问题不知道目标代理的是服务端。