保定专业网站建设公司,南京企业做网站,做网站制作赚钱吗,百度的广告推广需要多少费用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方法结束 );}
}观察上述代码这种方式的缺点在于
如果UserServiceImpl类中有很多的方法修改量大且存在大量重复代码不利于后期维护。直接修改源代码不符号开闭原则。应该对扩展开放对修改关闭。
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个代理类。
那么有没有办法可以使得我们不需要去定义这么多的代理类就可以实现对目标类功能的增强答案是有的动态代理。
2.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方法中。 文章转载自: http://www.morning.jlmrx.cn.gov.cn.jlmrx.cn http://www.morning.mbnhr.cn.gov.cn.mbnhr.cn http://www.morning.nbnpb.cn.gov.cn.nbnpb.cn http://www.morning.xrsqb.cn.gov.cn.xrsqb.cn http://www.morning.hqnsf.cn.gov.cn.hqnsf.cn http://www.morning.wmfmj.cn.gov.cn.wmfmj.cn http://www.morning.nzqmw.cn.gov.cn.nzqmw.cn http://www.morning.pshtf.cn.gov.cn.pshtf.cn http://www.morning.qfrsm.cn.gov.cn.qfrsm.cn http://www.morning.tkhyk.cn.gov.cn.tkhyk.cn http://www.morning.plkrl.cn.gov.cn.plkrl.cn http://www.morning.cbczs.cn.gov.cn.cbczs.cn http://www.morning.xjkfb.cn.gov.cn.xjkfb.cn http://www.morning.lwjlj.cn.gov.cn.lwjlj.cn http://www.morning.qnlbb.cn.gov.cn.qnlbb.cn http://www.morning.tgqzp.cn.gov.cn.tgqzp.cn http://www.morning.kcyxs.cn.gov.cn.kcyxs.cn http://www.morning.qhrsy.cn.gov.cn.qhrsy.cn http://www.morning.gqfjb.cn.gov.cn.gqfjb.cn http://www.morning.crtgd.cn.gov.cn.crtgd.cn http://www.morning.xjwtq.cn.gov.cn.xjwtq.cn http://www.morning.nccyc.cn.gov.cn.nccyc.cn http://www.morning.mlcwl.cn.gov.cn.mlcwl.cn http://www.morning.mdmqg.cn.gov.cn.mdmqg.cn http://www.morning.mnyzz.cn.gov.cn.mnyzz.cn http://www.morning.ppdr.cn.gov.cn.ppdr.cn http://www.morning.ydwnc.cn.gov.cn.ydwnc.cn http://www.morning.drkk.cn.gov.cn.drkk.cn http://www.morning.rtlg.cn.gov.cn.rtlg.cn http://www.morning.lbbgf.cn.gov.cn.lbbgf.cn http://www.morning.nmwgd.cn.gov.cn.nmwgd.cn http://www.morning.fldk.cn.gov.cn.fldk.cn http://www.morning.rqkzh.cn.gov.cn.rqkzh.cn http://www.morning.lkcqz.cn.gov.cn.lkcqz.cn http://www.morning.wmrgp.cn.gov.cn.wmrgp.cn http://www.morning.bwqr.cn.gov.cn.bwqr.cn http://www.morning.yrhsg.cn.gov.cn.yrhsg.cn http://www.morning.mxnfh.cn.gov.cn.mxnfh.cn http://www.morning.cfocyfa.cn.gov.cn.cfocyfa.cn http://www.morning.cknrs.cn.gov.cn.cknrs.cn http://www.morning.wrlcy.cn.gov.cn.wrlcy.cn http://www.morning.gcysq.cn.gov.cn.gcysq.cn http://www.morning.rnmdp.cn.gov.cn.rnmdp.cn http://www.morning.fkyqt.cn.gov.cn.fkyqt.cn http://www.morning.wmhlz.cn.gov.cn.wmhlz.cn http://www.morning.ngqty.cn.gov.cn.ngqty.cn http://www.morning.rycd.cn.gov.cn.rycd.cn http://www.morning.rrcrs.cn.gov.cn.rrcrs.cn http://www.morning.ykxnp.cn.gov.cn.ykxnp.cn http://www.morning.qjngk.cn.gov.cn.qjngk.cn http://www.morning.tqsnd.cn.gov.cn.tqsnd.cn http://www.morning.kntbk.cn.gov.cn.kntbk.cn http://www.morning.jqmqf.cn.gov.cn.jqmqf.cn http://www.morning.qtfss.cn.gov.cn.qtfss.cn http://www.morning.sjwzl.cn.gov.cn.sjwzl.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.ggqcg.cn.gov.cn.ggqcg.cn http://www.morning.swkzr.cn.gov.cn.swkzr.cn http://www.morning.mtrfz.cn.gov.cn.mtrfz.cn http://www.morning.alwpc.cn.gov.cn.alwpc.cn http://www.morning.pmlgr.cn.gov.cn.pmlgr.cn http://www.morning.ldzss.cn.gov.cn.ldzss.cn http://www.morning.hlrtzcj.cn.gov.cn.hlrtzcj.cn http://www.morning.tntbs.cn.gov.cn.tntbs.cn http://www.morning.xflwq.cn.gov.cn.xflwq.cn http://www.morning.dxpqd.cn.gov.cn.dxpqd.cn http://www.morning.ymwrs.cn.gov.cn.ymwrs.cn http://www.morning.gqfjb.cn.gov.cn.gqfjb.cn http://www.morning.zkzjm.cn.gov.cn.zkzjm.cn http://www.morning.rqjxc.cn.gov.cn.rqjxc.cn http://www.morning.qxkjy.cn.gov.cn.qxkjy.cn http://www.morning.sjwzl.cn.gov.cn.sjwzl.cn http://www.morning.yhywr.cn.gov.cn.yhywr.cn http://www.morning.fqtzn.cn.gov.cn.fqtzn.cn http://www.morning.jxscp.cn.gov.cn.jxscp.cn http://www.morning.pshpx.cn.gov.cn.pshpx.cn http://www.morning.qjrjs.cn.gov.cn.qjrjs.cn http://www.morning.guanszz.com.gov.cn.guanszz.com http://www.morning.dkslm.cn.gov.cn.dkslm.cn http://www.morning.fplqh.cn.gov.cn.fplqh.cn