赞
踩
实现有2种,1.基于Java反射机制 2.基于native方法。
Java动态代理是基于接口的,如果对象没有实现接口则选择用CGLIB方式实现动态代理。
实现步骤:
1.首先实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法。
2.然后在需要使用真实对象的时候,通过JDK动态代理获取代理对象。
目录
接口:SayHello.java
- package com.sid.proxy.jdk;
-
- public interface SayHello {
- String sayHello();
- }
真实的处理:SayHelloImp.java
- package com.sid.proxy.jdk;
-
- /**
- * 真实的处理
- * */
- public class SayHelloImp implements SayHello {
- public String sayHello() {
- System.out.println("hello word");
- return "done";
- }
- }
代理类:HelloHandler.java
- package com.sid.proxy.jdk;
-
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
-
- /**
- * 这是代理类,代理了SayHello接口的所有实现类
- * */
- //1.首先实现一个InvocationHandler接口,方法调用会被转发到该类的invoke()方法。
- public class HelloHandler implements InvocationHandler {
-
- SayHello sayHello;
-
- public HelloHandler(SayHello sayHello) {
- super();
- this.sayHello = sayHello;
- }
-
- /**
- * 所有的流程控制都在这里面处理
- * 调用真实的SayHello接口的实现类的所有方法,都会先进这个invoke方法,
- * 由method.invoke去真正的调用SayHello的方法
- *
- * 入参proxy是代理类,mehtod表示调用真实类的哪个方法,args表示调用真实类的方法的入参
- * */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("前置方法");
- // obj是调用真实类方法的返回值
- Object obj = method.invoke(sayHello, args);
- System.out.println(obj);
- System.out.println("后续方法");
- return obj;
- }
- }
Main方法调用
- package com.sid.proxy;
-
- import com.sid.proxy.jdk.HelloHandler;
- import com.sid.proxy.jdk.SayHello;
- import com.sid.proxy.jdk.SayHelloImp;
-
- import java.lang.reflect.Proxy;
-
- public class MainClass {
- public static void main(String[] args) {
- //2.然后在需要使用SayHello的时候,通过JDK动态代理获取SayHello的代理对象。
- //newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法
- SayHello proxy = (SayHello) Proxy.newProxyInstance(
- ClassLoader.getSystemClassLoader(),//1. 类加载器
- new Class[]{SayHello.class},//2. 代理需要实现的接口,可以有多个
- new HelloHandler(new SayHelloImp()) // 3. 方法调用的实际处理者,代理对象的方法调用都会转发到这里
- );
- proxy.sayHello();
-
- }
- }
运行结果
基于ASM的字节码生成库,它允许在运行时对字节码进行修改和动态生成。
CGLIB动态代理的原理就是用Enhancer生成一个被代理类的子类,并且设置好callback到proxy, 当被代理类的每个方法被调用就会转为调用实现了MethodInterceptor接口的proxy代理类的intercept() 函数。
优点:1.CGLIB通过继承方式实现代理。2.效率比JDK的代理要高。
缺点:CGLIB只能代理所有非final的类、方法。
实现步骤
1.首先实现一个MethodInterceptor接口,方法调用会被转发到该类的intercept()方法。
2.创建Enhancer对象,给enhancer设置父类(就是被代理的目标类),给enhancer设置callback(callback就是第一步实现的MethodInterceptor,就是拦截到目标类的方法后要做的代理逻辑),enhancer.create创建代理类。
设置callback的时候可以设置一组callback,即是有多个不同的MethodInterceptor实现,然后设置回调过滤器CallbackFilter来指定不同的真实方法使用不同的代理逻辑
CGLIB包对callback接口有一些简单实现:
1.FixedValue固定值,MethodInterceptor需要实现FixedValue接口。这会忽略被代理目标类调用的方法的返回值,使用固定值来替换。
2.NoOp.INSTANCE,直接调用真实被代理类的方法,在代理中对该方法不做任何逻辑处理。
3.LazyLoader,继承了Callback接口,有一个loadObject的方法,LazyLoader只有在第一次调用时会执行loadObject获取对象。
4.Dispatcher,继承了Callback接口,有一个loadObject的方法,每次调用时都触发loadObject方法。
目录
需要引包
pom.xml
- <dependency>
- <groupId>cglib</groupId>
- <artifactId>cglib</artifactId>
- <version>3.1</version>
- </dependency>
真实类SayHelloImp.java
- public class SayHelloImp {
-
- public String sayHello1() {
- System.out.println("hello word 1");
- return "done";
- }
-
- public String sayHello2() {
- System.out.println("你好! 2");
- return "完成";
- }
-
- public void sayHello3() {
- System.out.println("我会被代理,但是callbackFilter里面设置的处理sayHello3方法是不做任何代理逻辑,直接调用真实方法. 3");
- }
-
- public final void sayHello4() {
- System.out.println("我是final方法,用cglib方式代理的时候,我不会被代理. 4");
- }
- }
代理处理逻辑 SayHelloMethodInterceptor1.java 和SayHelloMethodInterceptor2.java
- public class SayHelloMethodInterceptor1 implements MethodInterceptor {
- @Override
- public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- System.out.println("SayHelloMethodInterceptor1 do. param:"+ Arrays.toString(args) );
- System.out.println("before...");
- //通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象
- Object obj = methodProxy.invokeSuper(o, args);
- System.out.println(obj);
- System.out.println("after...");
- return obj;
- }
- }
-
-
- public class SayHelloMethodInterceptor2 implements MethodInterceptor {
- @Override
- public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- System.out.println("SayHelloMethodInterceptor do.param:"+ Arrays.toString(args) );
- System.out.println("前置方法");
- //通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象
- Object obj = methodProxy.invokeSuper(o, args);
- System.out.println(obj);
- System.out.println("后续方法");
- return obj;
- }
- }
设置不同方法用不同代理逻辑CallbackFilterImpl.java
- public class CallbackFilterImpl implements CallbackFilter {
- // 这里设置真实对象不同的方法被调用,使用不同的MethodInterceptor代理逻辑来吹了
- @Override
- public int accept(Method method) {
- String methodName = method.getName();
-
- if ("sayHello1".equals(methodName)) {
-
- return 0; // sayHello1()方法使用callbacks[0]对象拦截。
-
- } else if ("sayHello2".equals(methodName)) {
-
- return 1; // sayHello2()方法使用callbacks[1]对象拦截。
-
- }else if ("sayHello3".equals(methodName)) {
-
- return 2; // sayHello3()方法使用callbacks[2]对象拦截。
-
- }
-
- return 0;
- }
- }
Main方法调用
- public class MainClassCglib {
- public static void main(String[] args) {
- // 2. 然后在需要使用真实对象的时候,通过CGLIB动态代理获取代理对象。
- //通过CGLIB的Enhancer来指定要代理的目标对象
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(SayHelloImp.class);
- //指定实际处理代理逻辑的对象 这样只设置了一个MethodInterceptor,真实类所有的方法都被同一个MethodInterceptor处理
- //enhancer.setCallback(new SayHelloMethodInterceptor1());
-
- // 回调实例数组
- Callback[] callbacks = new Callback[] { new SayHelloMethodInterceptor1(), new SayHelloMethodInterceptor1(), NoOp.INSTANCE };
- enhancer.setCallbacks(callbacks);
- enhancer.setCallbackFilter(new CallbackFilterImpl());
-
- //通过调用create()方法得到代理对象
- //对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法
- SayHelloImp star = (SayHelloImp)enhancer.create();
- star.sayHello1();
- star.sayHello2();
- star.sayHello3();
- star.sayHello4();
- }
- }
结果
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
FastClass机制的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。 这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。