自己如何高效有力的维护一个网站,阿里云wordpress帮助,北京网站建设公司电扬,免费网站备案号码1、什么是动态代理 可能很多小伙伴首次接触动态代理这个名词的时候#xff0c;或者是在面试过程中被问到动态代理的时候#xff0c;不能很好的描述出来#xff0c;动态代理到底是个什么高大上的技术。不方#xff0c;其实动态代理的使用非常广泛#xff0c;例如我们平常使…1、什么是动态代理 可能很多小伙伴首次接触动态代理这个名词的时候或者是在面试过程中被问到动态代理的时候不能很好的描述出来动态代理到底是个什么高大上的技术。不方其实动态代理的使用非常广泛例如我们平常使用的 Spring中的 Transactional注解其依赖于 AOP而 AOP的底层实现便是动态代理看到这里是不是更有兴趣去了解动态代理了呢
动态代理可以分解为动态“代理”。
代理代理一词在我们的生活中也是随处可见的例如房屋中介其是对房主的一种代理房主需要出租其房屋但是可能没时间去接待租客给租客介绍房屋信息带领租客看房但是房屋中介可以为租客提供这些服务所以代理其是对被代理对象的一个功能增强动态动态通常与静态相比较静态描述的是事物是固定存在的动态则描述的是事物是随着需求而动态生成的。
所以静态代理存在一定的局限性不能很好的满足需求的千变万化动态代理的出现就是为了解决这些局限性。
我们先来看看静态代理。
2.、静态代理
在开发中通常需要为方法添加日志打印能够记录程序的执行过程以便后续出现异常问题的时候能更好的排查定位。
假设我们现在已经完成了系统用户的增加、删除、修改等功能这些功能在类 UserServiceImpl中已经实现。
代码示例
public class UserServiceImpl {public void add() {System.out.println(添加用户);}public void update() {System.out.println(修改用户);}public void delete() {System.out.println(删除用户);}
}现在我们需要在UserServiceImpl类中的方法添加日志功能那么怎么才能更好地去实现这个需求呢
1直接在目标方法前后添加日志代码
代码示例
public class UserServiceImpl {public void add() {System.out.println( add方法开始 );System.out.println(添加用户);System.out.println( add方法结束 );}
public void update() {System.out.println( update方法开始 );System.out.println(修改用户);System.out.println( update方法结束 );}
public void delete() {System.out.println( delete方法开始 );System.out.println(删除用户);System.out.println( delete方法结束 );}
}观察上述代码这种方式的缺点在于
2静态代理方式实现
静态代理需要我们将目标类的方法抽取到接口中代理类和目标类实现同一个接口既然要实现代理代理类自然需要在其内部维护目标对象的引用并通过构造函数为其赋值然后在代理类的方法中调用目标对象的同名方法并在调用前后完成功能的增强。
实现步骤
抽取UserService接口创建目标类UserServiceImpl实现UserService接口创建代理类UserServiceProxy实现UserService接口代理类中完成功能的增强
代码实现
// 目标接口
public interface UserService {void add();void update();void delete();
}
// 目标类
public class UserServiceImpl implements UserService {Overridepublic void add() {System.out.println(添加用户);}Overridepublic void update() {System.out.println(修改用户);}Overridepublic void delete() {System.out.println(删除用户);}
}
// 代理类
public class UserServiceProxy implements UserService {private UserService userService;public UserServiceProxy(UserService userService) {this.userService userService;}Overridepublic void add() {System.out.println( add方法开始 );userService.add();System.out.println( add方法结束 );}Overridepublic void update() {System.out.println( update方法开始 );userService.update();System.out.println( update方法结束 );}Overridepublic void delete() {System.out.println( delete方法开始 );userService.delete();System.out.println( delete方法结束 );}
}观察上述代码静态代理遵循开闭原则在不修改目标类的前提下完成了功能的增强但是依然存在大量重复的代码且一个代理类只能代理一个目标类如果有n个目标类需要被代理就需要同比增加n个代理类。
那么有没有办法可以使得我们不需要去定义这么多的代理类就可以实现对目标类功能的增强答案是有的动态代理。
3、JDK动态代理
前面我们提到静态代理的实现方式代理类和目标类都实现同一个接口在代理类中维护目标类对象并完成对目标类对象方法的增强这种方式虽然遵循开闭原则但是代理类和目标类至少是一对一的绑定关系如果需要被代理的目标类个数越多代理类就会越多会产生大量重复的代码也不利于后期的维护。
从静态代理中我们知道代理类也是接口的一个实现类代理对象的类型也是属于接口类型我们来验证一下。
public class Test {public static void main(String[] args) {UserServiceProxy userServiceProxy new UserServiceProxy(new UserServiceImpl());System.out.println(userServiceProxy instanceof UserService);}
}
// 打印结果true那么能不能动态生成这些代理对象呢我们知道类是构造对象的模板代理类都还不存在怎么去构造代理对象呢
除了不存在代理类还剩下 UserService接口和 UserServiceImpl目标类JDK动态代理的目的就是通过接口来生成代理类以及代理类的对象我们知道接口是不能直接通过new关键字创建对象的。
那么JDK动态代理是怎么创建出代理类以及代理类对象的呢
我们先来看看通过 new关键字创建对象的过程。
UserServiceImpl userService new UserServiceImpl();
/*
创建对象的过程1.执行new指令如果类未加载先执行类加载过程。1.加载JVM通过ClassLoader将UserServiceImpl.class文件加载到方法区Method Area在堆内存中创建代表该类的Class对象。2.验证3.准备为静态变量分配内存并设置类型初始值。4.解析5.初始化为静态变量赋值、执行静态代码块2.为对象分配内存将对象的实例字段初始化类型零值。3.执行构造方法对对象进行初始化
*/追踪上述过程我们得知创建对象需要先得到该类的Class对象通过Class对象去创建实例对象。为了验证这一点我们不妨来看看通过反射的方式创建对象的过程。
public class Test {SneakyThrowspublic static void main(String[] args) {// 获取Class对象ClassUserServiceImpl userServiceClass UserServiceImpl.class;// 获取构造器Constructor?[] constructors userServiceClass.getConstructors();for (Constructor? constructor : constructors) {// 通过构造器创建实例对象System.out.println(constructor.newInstance());}}
}现在问题回归到接口不能直接new也没有构造方法并且不存在代理类的class文件怎么获得Class对象了。
动态代理关键类
我们先来看看JDK动态代理的实战代码
需要自定义个 CustomInvocationHandler实现 InvocationHandler接口。利用 Proxy.newProxyInstance构建实例对象。
// UserService接口
public interface UserService {void add();void update();void delete();
}// 目标类
public class UserServiceImpl implements UserService {Overridepublic void add() {System.out.println(添加用户);}Overridepublic void update() {System.out.println(修改用户);}Overridepublic void delete() {System.out.println(删除用户);}
}// CustomInvocationHandler
public class CustomInvocationHandler implements InvocationHandler {// 目标对象private Object target;public CustomInvocationHandler(Object target) {this.target target;}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println( 方法开始 );Object result method.invoke(target, args);System.out.println( 方法结束 );return result;}
}public class Test {public static void main(String[] args) {UserServiceImpl userService new UserServiceImpl();// 关键代码UserService service (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new CustomInvocationHandler(userService));service.add();}
}从测试代码可以看出Proxy类是关键。我们来看看Proxy为我们提供的方法
Class? getProxyClass(ClassLoader loader, Class?... interfaces)虽然被标注为过时方法但是从名字上可以得知其目的是为了获得代理类的Class对象。话不多说我们来调用一下。
public class Test {public static void main(String[] args) {Class? proxyClass Proxy.getProxyClass(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces());System.out.println(proxyClass.getName());for (Method method : proxyClass.getDeclaredMethods()) {System.out.println(method.getDeclaringClass() . method.getName() ());}System.out.println(Arrays.toString(proxyClass.getConstructors()));}
}可以看到
获得的Class对象的名称为 $Proxy0。定义了我们需要的 add();update();delete()方法。定义了一个有参构造方法 $Proxy0(InvocationHandler handler)。
虽然没有无参构造方法我们还是得尝试一下调用一下这个有参的构造方法需要我们传入一个 java.lang.reflect.InvocationHandler对象
public class Test {SneakyThrowspublic static void main(String[] args) {System.getProperties().put(jdk.proxy.ProxyGenerator.saveGeneratedFiles, true);Class? proxyClass Proxy.getProxyClass(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces());// 获取$Proxy0(InvocationHandler handler)构造方法Constructor? constructor proxyClass.getConstructor(InvocationHandler.class);UserService userService (UserService) constructor.newInstance(new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(proxy.getClass());System.out.println(method.getDeclaringClass() . method.getName() ());return null;}});userService.add();}
}看的出来**当我们获得代理对象之后通过代理对象来调用接口方法都会回调构造时传进来的 InvocationHandler对象的 invoke(Object proxy, Method method, Object[] args)方法**该方法有3个参数
Object proxy代表的是代理对象本身。Method method代表的是被调用的方法的Method对象。Object[] args代表的是被调用方法的参数。
可以猜测JDK动态代理生成的代理类中维护了InvocationHandler类的对象变量并且在实现接口方法时通过InvocationHandler对象调用了 invoke(Object proxy, Method method, Object[] args)方法。
System.getProperties().put(jdk.proxy.ProxyGenerator.saveGeneratedFiles, true);不知道大家没有看到这行代码哈当添加了这行代码之后可以将在项目目录下保存动态创建的class文件 com/sun/proxy/$Proxy0.class。 可以看到生成的代理类 $Proxy0继承自 Proxy类并实现了 UserService接口并且在 add()方法中通过其父类 Proxy中维护的 InvocationHandler对象调用 invoke()方法这也就成功的解释了前面调用 userService.add()方法会回调到invoke()方法。
这时候我们再把代码改造一下如下
public class CustomInvocationHandler implements InvocationHandler {// 目标对象private Object target;public CustomInvocationHandler(Object target) {this.target target;}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println( 方法开始 );Object result method.invoke(target, args);System.out.println( 方法结束 );return result;}
}public class Test {SneakyThrowspublic static void main(String[] args) {UserServiceImpl target new UserServiceImpl();Class? proxyClass Proxy.getProxyClass(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces());Constructor? constructor proxyClass.getConstructor(InvocationHandler.class);UserService userService (UserService) constructor.newInstance(new CustomInvocationHandler(target));userService.add();}
}这样就完成了对目标对象功能的增强前面我们提到过 Class? getProxyClass(ClassLoader loader, Class?... interfaces)
已经被标注为过时推荐我们使用 Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h)方法。
动态代理设计思想
好的到这里我们来总结一下JDK动态的设计思想 使用 JDK动态代理使得我们免去编写代理类只需要将增强功能编写在 InvocationHandler的 invoke方法中。
4、CGLib动态代理
CGLib代理的目标对象不需要事先任何接口它是通过动态集成目标对象实现动态代理的。CGLib代理执行代理方法的效率之所以比JDK高是因为CGLib采用了FastClass机制为代理类和被代理类各生成一个类这个类会为代理类或被代理类的方法分配一个indexint类型;这个index当作一个入参FastClass 就可以直接定位要调用的方法并直接进行调用省去了反射调用所以调用效率比JDK代理通过反射调用高。FastClass并不是跟代理类一起生成的而是在第一次执行MethodProxy的invoke()或invokeSuper()方法时产生并放在缓存中的。
5、CGLib和JDK动态代理对比
JDK动态代理实现了被代理对象的接口CGLib代理继承了被代理对象。JDK动态代理和CGLib代理在运行期生成字节码JDK动态代理直接写Class字节码CGLib代理使用ASM框架字节码操控框架写Class字节码CGLib代理实现更复杂生成代理类比JDK动态代理效率低。JDK动态代理调用代理方法是通过反射机制调用的CGLib代理是通过FastClass机制直接调用方法的CGLib代理的执行效率更高
6、Spring中的代理选择原则
当Bean有实现接口时Spring就会用JDK动态代理当Bean没有实现接口时Spring会选择CGLib代理Spring可以通过配置强制使用CGLib代理只需要在配置中加入aop:aspectj-autoproxy roxy-target-clas“true”