发布时间:2025-06-24 17:16:32 作者:北方职教升学中心 阅读量:095
这样就可以避免静态代理中代理类接口过多的问题。前言
二、
jdk动态代理的使用步骤如下:
创建一个需要动态代理的接口,即Movie接口;
创建一个需要动态代理接口的真实实现,即RealMovie类;
创建一个动态代理处理器,实现InvocationHandler接口,并重写invoke方法去增强真实实现中的目标方法;
在测试类中,生成动态代理的对象;
自定义一个类,实现InvocationHandler接口,并重写里面的invoke方法
public class ProxyInvocationHandler implements InvocationHandler { //需要动态代理接口的真实实现类 private Object object; //通过构造方法去给需要动态代理接口的真实实现类赋值 public ProxyInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法增强 System.out.println("方法执行前操作"); //object是真实实现,args是调用方法的参数 //当代理对象调用真实实现的方法,那么这里就会将真实实现和方法参数传递过去,去调用真实实现的方法 method.invoke(object,args); //方法增强 System.out.println("方法执行后操作"); return null; }}
编写测试类,使用自定义的handler生成代理对象并调用目标方法
public class DynamicProxyTest { public static void main(String[] args) { //需要动态代理接口的真实实现 LoginServiceImpl realMovie = new LoginServiceImpl(); //动态代理处理类 ProxyInvocationHandler handler = new ProxyInvocationHandler(realMovie); //获取动态代理对象 //第一个参数:真实实现(被代理对象)的类加载器 //第二个参数:真实实现类(被代理对象)它所实现的所有接口的数组 //第三个参数:动态代理处理器 LoginService loginService = (LoginService) Proxy.newProxyInstance(realMovie.getClass().getClassLoader(), realMovie.getClass().getInterfaces(), handler); loginService.login("user"); }}
执行结果
3.1.1 jdk动态代理模拟实现
由于jdk的代理是在运行期间动态产生字节码,所以很难看到运行期间代理产生的源码,我们可以模拟其原理进行一个简单的实现,代码如下:
定义一个接口,作为被代理的对象,实现类也一并贴出
interface MinuService { int addMinu(); void reduceMinu(); } //目标对象实现类 static class Target implements MinuService { @Override public int addMinu() { System.out.println("tager do addMinu"); return 1; } @Override public void reduceMinu() { System.out.println("tager do reduceMinu"); } }
定义一个handler,handler的作用可理解为作为代理对象中实际执行方法调用的一个入口
interface MyInvocationHandler { Object invoke(Object proxy,Method method, Object[] args); }
定义代理类,代理类是核心实现,在代理类中主要做的事情如下:
引用自定义handler;
引用代理类中待执行的目标方法(该方法其实是运行期间动态生成的);
调用handler中的invoke反射调用目标方法,并得到执行结果;
import java.lang.reflect.Method;public class MyProxy implements JdkInner.MinuService { private JdkInner.MyInvocationHandler handler; public MyProxy(JdkInner.MyInvocationHandler handler) { this.handler = handler; } static Method addMinu; static Method reduceMinu; static { try { addMinu = JdkInner.MinuService.class.getMethod("addMinu"); reduceMinu = JdkInner.MinuService.class.getMethod("reduceMinu"); } catch (NoSuchMethodException e) { e.printStackTrace(); } } @Override public int addMinu() { Object invoke = null; invoke = handler.invoke(this, addMinu, new Object[0]); return Integer.parseInt(invoke.toString()); } @Override public void reduceMinu() { handler.invoke(this, reduceMinu, new Object[0]); }}
编写测试类,有没有发现这个写法和上述使用jdk的动态代理很像
public static void main(String[] args) { MinuService minuService = new MyProxy(new MyInvocationHandler() { @Override public Object invoke(Object proxy,Method method, Object[] args) { System.out.println("before invoke method"); Object result = null; try { result = method.invoke(new Target(), args); System.out.println("result :" + result); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return result; } }); System.out.println(minuService.getClass()); minuService.addMinu(); }
如果你像查看jdk动态代理时产生的源码,可以考虑使用arthars进行反编译查看
3.2 CGLIB 代理
CGLIB 代理是一个基于字节码操作的代理方式,它是一个强大的、
3.2.2 cglib代理源码模拟实现
上面的案例了解了如何使用cglib进行代理以及代码的实现,下面来模拟一下其底层源码的实现
定义一个目标类
public class CglibTarget { public void add(){ System.out.println("add()"); } public void add(int num){ System.out.println("add() :" + num); } public void reduce(int count){ System.out.println("reduce() :" + count); }}
定义代理类
通过上面的介绍了解到,cglib的代理对象是通过生成目标对象的子类实现的,所以代理类需要继承目标类
import org.springframework.cglib.proxy.MethodInterceptor;import java.lang.reflect.Method;public class CglibProxy extends CglibTarget { private MethodInterceptor methodInterceptor; public void setMethodInterceptor(MethodInterceptor methodInterceptor) { this.methodInterceptor = methodInterceptor; } static Method add_1; static Method add_2; static { try { add_1 = CglibTarget.class.getMethod("add"); add_2 = CglibTarget.class.getMethod("add",int.class); } catch (NoSuchMethodException e) { e.printStackTrace(); } } @Override public void add() { try { methodInterceptor.intercept(this,add_1,new Object[0],null); } catch (Throwable throwable) { throwable.printStackTrace(); } } public void add(int num){ try { methodInterceptor.intercept(this,add_2,new Object[]{num},null); } catch (Throwable throwable) { throwable.printStackTrace(); } } public void reduce(int count){ super.reduce(count); }}
测类类
import com.congge.aop.cglib.CglibProxy;import com.congge.aop.cglib.CglibTarget;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibTest { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); CglibTarget target = new CglibTarget(); proxy.setMethodInterceptor(new MethodInterceptor() { @Override public Object intercept(Object targetObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before handle"); Object result = method.invoke(target, args); System.out.println("after handle"); return result; } }); proxy.add(10); }}
运行代码,得到如下效果
3.2.3 cglib代理补充说明
在使用cglib代码编码实现中,注意到在intercept方法参数中有一个Method的参数,这个参数是做什么用的呢?
不妨将代码修改成下面这样
public static void main(String[] args) { Target target = new Target(); //拿到代理的类 Target proxyTarget = (Target)Enhancer.create(Target.class, new MethodInterceptor() { @Override public Object intercept(Object proxyClass, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before...."); //Object result = method.invoke(target, args); Object result = methodProxy.invoke(target, args); System.out.println("after..."); return result; } }); //执行代理类的方法 proxyTarget.get(); }
再次执行,发现仍然可以得到正确的结果,为什么会这样呢?
cglib代理底层通过这种方式为调用者提供了多一种选择,当选择使用methodProxy的invoke方法时,将不反射,而是退化为直接使用原始目标对象去调用方法,某些情况下,可以获得比反射更高的性能。本文将详细介绍AOP的核心技术和底层实现原理。
4.2.1 语法结构
例1:对扫描包中的某个类的方法增强
execution(* com.xxx.xxxClass.方法名(..))
例2:对扫描包中的某个类的所有方法增强
execution(* com.xxx.xxxClass.* (..))
例3:对扫描包中所有类,类中所有方法增强
execution(* com.xxx.service.*.* (..))
例4:所有带有某个注解的方法或类
@annotation(com.xxx.annotation.xxxAnnotation)
上面举的例子都是去切入具体的某个方法、前言
spring 是一个流行的 Java 企业应用程序开发框架,其中的 aop(面向切面编程)是 spring 框架中一个非常重要的概念。比如,一个日志模块可以被称作日志的AOP切面。