参考资料:https://www.cnblogs.com/ygj0930/p/6542259.html
一:静态代理
业务实现类、业务代理类 两部分组成。业务实现类 负责实现主要的业务方法,业务代理类负责对调用的业务方法作拦截、过滤、预处理,主要是在方法中首先进行预处理动作,然后调用业务实现类的方法,还可以规定调用后的操作。我们在需要调用业务时,不是直接通过业务实现类来调用的,而是通过业务代理类的同名方法来调用被代理类处理过的业务方法。
1:提供接口Human.java
package com.example.invocation;
/**
* Created by hui.yunfei@qq.com on 2019/4/22
*/
public interface Human {
public void eat(String food);
}
2:提供接口实现类HumanImpl.java
package com.example.invocation;
/**
* Created by hui.yunfei@qq.com on 2019/4/22
*/
public class HumanImpl implements Human {
@Override
public void eat(String food) {
System.out.println("eat food:"+food);
}
}
3:提供静态代理类HumanProxy
package com.example.invocation;
/**
* 静态代理类
* 也需要实现接口
* Created by hui.yunfei@qq.com on 2019/4/22
* 添加代理类HumanProxy实现Human
* 提供Human的私有对象
* 提供实例化HumanImpl对象的返回HumanProxy类型构造器
* 在HumanProxy重载方法中调用Human对象操作另一个实例类来操作
*/
public class HumanProxy implements Human{
private Human human;
public HumanProxy(){
human=new HumanImpl();
}
@Override
public void eat(String food) {
before();
human.eat(food);
after();
}
public void before(){
System.out.println("before...");
}
public void after(){
System.out.println("after...");
}
public static void main(String[] args) {
Human human=new HumanProxy();
human.eat("noods");
}
}
接口提供方法、实现类提供方法具体实现、代理类中创建一个业务实现类对象来调用具体的业务方法;并且可以在执行方法前后做其他逻辑操作。
静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。
二:JDK动态代理(就是jdk里边提供的动态代理模式)
JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。
提供jdk代理实现类
package com.example.invocation.jdkProxy;
import com.example.invocation.Human;
import com.example.invocation.HumanImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by hui.yunfei@qq.com on 2019/4/22
* jdk动态代理类
* 1:实现InvocationHandler接口,实现invoke方法
* 2:提供绑定业务对象(定义private Object target,提供构造器this.target=target)方法和返回代理类方法(可以二合一一个build方法)
* 3:客户端直接调用build方法传入业务对象(new HumanImpl),获取代理对象Human
* 4:代理对象直接调用接口方法
*/
public class DynamicProxy implements InvocationHandler {
private Object target;//这其实业务实现类对象,用来调用具体的业务方法
/**
* 绑定业务对象并返回一个代理类
*/
public Object build(Object target){
this.target=target;//接收业务实现类对象参数
//通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
//创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("预处理操作——————");
Object object= method.invoke(target,args);
System.out.println("调用后处理——————");
return object;
}
public static void main(String[] args) {
DynamicProxy dynamicProxy=new DynamicProxy();
Human humanProxy= (Human) dynamicProxy.build(new HumanImpl());
// Human humanProxy= (Human) Proxy.newProxyInstance(
// human.getClass().getClassLoader(),
// human.getClass().getInterfaces(),
// dynamicProxy
// );
humanProxy.eat("noods");
}
}
JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。
三:CGlib动态代理(springframework)
package com.example.invocation.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglib动态代理类
* 可以代理非实现接口类
* final修饰的方法不能被代理
* Created by hui.yunfei@qq.com on 2019/4/22
* 1:实现MethodInterceptor接口,intercept
* 2:提供绑定业务对象(定义private Object target,提供构造器this.target=target)方法和返回代理类方法(可以二合一一个getInstance方法)
* 3:客户端直接调用getInstance方法传入业务对象(new Person),获取代理对象Person
* 4:代理对象直接调用接口方法
*/
public class MyCglibInterceptor implements MethodInterceptor {
private Object target;//业务对象
//相当于JDK动态代理中的绑定
public Object getInstance(Object target){
//绑定业务对象
this.target=target;
//创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(this.target.getClass());
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
/**
* 实现方法回调
* o: cglib生成的代理对象
* method: 目标方法
* objects: 目标方法的参数
* methodProxy:代理方法
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//这里调用切面方法before
System.out.println("方法执行前");
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object object = method.invoke(this.target,objects);//调用目标类的目标方法
System.out.println("方法执行后");
//这里调用切面方法after
return object;
}
public static void main(String[] args) {
MyCglibInterceptor myInterceptor = new MyCglibInterceptor();
Person personProxy = (Person) myInterceptor.getInstance(new Person());
personProxy.eat("noods");
personProxy.sayOthers("1");
// //实践证明final方法没有被代理
// 方法执行前
// i'm a person ,i'm eatting noods
// 方法执行后
// i'm a person ,i'm eatting other 1
}
}
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
总结:
静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
JDK动态代理实现InvocationHandler接口,是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;是通过反射机制调用,直接写Class字节码
CGlib动态代理是实现MethdInterceptor接口,底层是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;因为采用的是继承,所以不能对final修饰的类进行代理。是通过FastClass机制直接调用方法,使用ASM框架写Class字节码,生成代理类比JDK效率低。执行效率更高。