网站开发源代码mvc,qq空间网站根目录,淘宝客网站一定要备案,辽宁建设工程信息网官网新网址什么是代理模式
代理模式#xff1a;为其他对象提供一种代理#xff0c;用以控制对这个对象的访问#xff1b; 我们使用代理对象#xff0c;可以在不修改目标对象的基础上#xff0c;增强主业务逻辑#xff1b;比如说我们的系统中有一个登录接口#xff0c;我们要对这个…什么是代理模式
代理模式为其他对象提供一种代理用以控制对这个对象的访问 我们使用代理对象可以在不修改目标对象的基础上增强主业务逻辑比如说我们的系统中有一个登录接口我们要对这个登录接口做一下功能迭代比如说加一个统计登录耗时的接口但是我们要加的这个功能不能去修改原有的代码逻辑就是你不能在登录接口里面直接加这个统计代码那怎么呢那就可以通过代理对象来做通过代理对象可以在不修改目标对象的登录接口的基础上还能增强这个业务的逻辑。 这里我们可以使用的代理方式从大范围上讲是有两种一种是静态代理一种是动态代理。这两个代理的区别主要是在于代理类创建的时间不同静态代理是在代码编译的时候就已经创建好了而动态代理就是在代码运行中的时候创建的。 静态代理 有一个名为 MemberService 的接口定义了用户会员服务的基本功能如登录、登出、获取用户信息。MemberServiceImpl 类实现了 MemberService 接口它提供了具体的会员服务功能的实现例如登录、登出、获取用户信息。当调用这些方法时会在控制台上输出相应的操作信息。MemberServiceProxy 类也实现了 MemberService 接口但它不是直接提供服务的而是充当一个代理。这个代理类的构造函数接受一个真正的会员服务对象作为参数它会在真正的服务对象执行前后添加一些额外的功能。在代理类中login 方法是一个示例。在调用真正的会员服务的 login 方法之前代理类会记录当前时间戳然后在调用完成后记录另一个时间戳并计算出操作耗时。这样你可以看到登录操作花费了多少时间。最后在 Test 类中我们创建了一个会员服务的代理对象 proxy并使用它来执行登录操作。当登录时代理会在控制台上显示登录信息和操作耗时。 public interface MemberService {String login(String username, Stringpassword);void logout();String getMemberInfo();
}public class MemberServiceImpl implements MemberService {Overridepublic String login(String username, Stringpassword) {System.out.printf(执行登录业务: %s, %s\n, username, password);return 登录成功;}Overridepublic void logout() {System.out.println(执行登出业务);}Overridepublic String getMemberInfo() {System.out.println(获取用户信息);return 用户信息;}
}public class MemberServiceProxy implements MemberService {private MemberService target;public MemberServiceProxy(MemberService target) {this.target target;}Overridepublic String login(String username, Stringpassword) {long start System.currentTimeMillis();String result target.login(username,password);long end System.currentTimeMillis();System.out.println(登录耗时 (end -start) ms);return result;}Overridepublic void logout() {target.logout();}Overridepublic String getMemberInfo() {return target.getMemberInfo();}
}public class Test {public static void main(String[] args) {MemberServiceProxy proxy new MemberServiceProxy(new MemberServiceImpl());proxy.login(123456, 123456);}
}这种静态代理的方式可以让你在不修改原始服务类的情况下添加一些额外的功能这样看起来还是比较好用的但是呢这种方式还是有一定的缺点假如说你想给服务里其他接口也添加这个统计耗时的功能你想给其他服务的接口也添加这个功能那是不是 就需要增加类的数量每个需要代理的类都需要一个对应的代理类。当你有成百上千个服务的时候代码就会变得非常复杂维护这成百上千个代理类也会变得困难。这种方式也不够灵活因为静态代理模式是编译时确定的那就意味着在编译的时候就需要知道代理的具体类型。如果需要在运行时决定使用哪个代理静态代理就很难做到了。这样做维护成本是非常高的如果我原始类的接口发生变化代理类也需要相应地更改。这就会导致大量的代理类需要维护。所以我们可以考虑一个更好的方式来做就是动态代理。 动态代理
动态代理实现方式有两种基于 JDK 的动态代理和基于CGLIB 的动态代理。 静态代理的那个代理类是我们要自己写的如果有一万个service那我们要写一万个静态代理类那么动态代理呢就是你不管有多少个service我只要制定一个规则那这一万个service的代理类我会在程序运行的时候自动帮你生成了。动态代理这种方式就很好的解决了我们之前说的那些问题它的灵活度非常高但是呢也有一定的缺点就是动态代理是基于反射实现的在运行的时候要执行反射的逻辑性能还是有一定损耗的但是影响不大。 JDK的动态代理
// 首先还是创建一个动态代理类然后让他实现jdk的InvocationHandler 接口
public class JdkDynamicProxy implements InvocationHandler {// 其中接收一个参数这个参数就是被代理的对象如MemberServiceImpl private Object target;public JdkDynamicProxy(Object target) {this.target target;}// 获取代理对象这个代理对象通过 JDK 的 Proxy 的newProxyInstance 方法进行创建// 其中要传入被代理对象的类加载器这个类加载器会帮助我们加载新生成代理对象的字节码// 再传入被代理类的接口类型也就是告诉一下代理对象你要生成的对象类型是啥是Member// 服务接口还是Order服务接口// 最后传入 this这个就是一个回调程序这个程序要实现 InvocationHandler 接口刚好// 我们当前这个类实现了就直接传进去然后生成的这个代理对象在调用后就会调用到重写的 invoke 方法public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), //被代理对象的类加载器target.getClass().getInterfaces(), // 被代理类的接口类型this // 回调程序传入当前InvocationHandler 对象会回调重写的 invoke 方法);
}// proxy: 生成的代理对象proxy是jdk在运行时赋值的在方法中直接使用// method目标方法// args目标方法的参数方法执行的参数Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long start System.currentTimeMillis();Object result method.invoke(target,args);long end System.currentTimeMillis();System.out.println(method.getName() 方法耗时 (end - start) ms);return result;}
}public class Test {public static void main(String[] args) {JdkDynamicProxy jdkDynamicProxy newJdkDynamicProxy(new MemberServiceImpl());MemberService proxy (MemberService)jdkDynamicProxy.getProxy();proxy.login(whoiszxl, 123456);proxy.getMemberInfo();proxy.logout();}
}大致流程就是 创建 JDK 动态代理对象并将 MemberServiceImpl 对象作为 target 参数传进去。接着调用 getProxy 方法其中调用Proxy.newProxyInstance 来加载这个MemberServiceImpl 的代理对象。通过生成之后的代理对象调用 login 方法那就不是MemberServiceImpl 本身的这个方法了而是调到了InvocationHandler里的 invoke 方法了。invoke方法中就可以做对应的增强其中 method 参数就是被代理对象的方法args 就是被代理对象的方法。 CGLIB动态代理 静态代理还有JDK动态代理它们都需要目标对象去实现一个接口案例中MemberServiceImpl 实现MemberService但是还有些场景下目标对象就是一个对象它没有实现任何接口那这种情况我们就不能使用JDK动态代理了我们就需要使CGLib 动态代理了。 有一个名为 LoginService 的普通类它包含一个 login方法用于执行登录操作当调用这个方法时它会在控制台上输出一行文本。有一个名为 CglibDynamicProxy 的类它实现了MethodInterceptor 接口。这个类的目的是创建一个CGLIB动态代理对象用于代替其他对象执行方法。在 CglibDynamicProxy 类的构造函数中它接收一个目标对象 target表示你希望代理的对象。getProxy 方法用于创建代理对象。它使用CGLIB的Enhancer 类将目标对象的类作为超类父类并设置当前对象作为方法拦截器this 表示当前对象。然后它使用 Enhancer 创建代理对象并返回。在 intercept 方法中当代理对象的方法被调用时它会首先输出一行文本表示CGLIB动态代理的开始然后调用目标对象的相应方法。接着它再次输出一行文本表示CGLIB动态代理的结束。在 CglibDynamicTest 类中我们创建了一个CglibDynamicProxy 对象并将 LoginService 对象传递给它作为目标。然后我们使用 getProxy 方法获取代理对象并将其转型为 LoginService 类型。最后我们使用代理对象来调用 login 方法。每次方法被调用时代理会在方法前后输出文本以表示CGLIB动态代理的开始和结束。 public class LoginService {public void login() {System.out.println(执行登录操作);}
}public class CglibDynamicProxy implements MethodInterceptor {private Object target;public CglibDynamicProxy(Object target) {this.target target;}public Object getProxy() {Enhancer enhancer new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create();}Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(Cglib动态代理start);Object invoke method.invoke(target,objects);System.out.println(Cglib动态代理end\n);return invoke;}
}
public class CglibDynamicTest {public static void main(String[] args) {CglibDynamicProxy proxy new CglibDynamicProxy(new LoginService());LoginService memberServiceProxy (LoginService) proxy.getProxy();memberServiceProxy.login();}
}写法上和JDK动态代理类似只有在实现接口的地方和获取代理类的地方有点区别。最大的区别是CGLib代理的不需要被代理对象实现任何接口它会重写被代理类的所有方法所以不能代理final修饰的方法或者类。因为被final修饰后表明这个类不能被继承。 两种动态代理的区别
实现方式不同 JDK动态代理通过反射去实现被代理对象的所有接口所以JDK只能代理实现了接口的对象CGLlib会对代理类所有方法进行重写所以不是所有的方法都能被代理private、final不行 拦截被代理对象的方式不同 JDK动态代理是通过实现InvocationHandler接口来实现的代理对象调用任意方法时都会先进入 InvocationHandler的invoke()方法由该方法决定是否调用原对象的方法CGLIB动态代理则是通过生成目标类的子类覆盖其中的业务方法在其内部加入横切逻辑实现拦截的 性能和效率不同 JDK 动态代理跟CGLib代理都是在运行期生成新的字节码但是 CGLib 实现更为复杂用的是 ASM 框架生 成代理类的效率比 JDK 低JDK 动态代理是通过反射机制去查询所有被代理对象的接口CGLib 代理是通过 FastClass机制直接调用方法所以执行效率CGLib 比 JDK 代理要高 spring用的是哪个代理模式 bean有实现接口的时候用jdk没有实现的话模式cglib。也可以强制用cglib。 代理模式优缺点
优点 能将代理对象与真实被调用的目标对象分离降低系统的耦合程度易于扩展在增强目标对象职责的同时可以起到保护对象的作用 缺点 增加系统的复杂度会在客户端与目标对象之间增加一个代理对象请求速度变慢