说时迟那时快,一段操作猛如虎,代理模式其实就两种,jdk自带的代理模式,和cglib提供的代理模式
动态代理
啥叫做动态代理,可以理解为中介,比如我写了一个方法,然后张三需要,张三需要如果不找我就找中介,中介帮忙处理一下
,可以说等于又进了一层。可能中介再用这个方法,还给你加点小广告啥的。完了这就是动态代理
jdk动态代理
public class JdkProxy implements InvocationHandler {
//需要代理的目标对象
private Object target;
//定义获取代理对象方法
private Object getJDKProxy(Object targetObject) {
//为目标对象target赋值
this.target = targetObject;
//JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理,监听开始!");
Object result = method.invoke(target, args);
System.out.println("JDK动态代理,监听结束!");
return result;
}
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
user.addUser("admin", "123123");//执行新增方法
}
invoke 其实就算调用方法,实现InvocationHandler调用处理程序这个接口重写的方法,我们可以看看说明
InvocationHandler是由代理实例的调用处理程序实现的接口。
每个代理实例都有一个关联的调用处理程序。 在代理实例上调用方法时,该方法调用将被编码并分派到其调用处理程序的invoke方法。
自从:1.3
处理代理实例上的方法调用并返回结果。 在与之关联的代理实例上调用方法时,将在调用处理程序上调用该方法。
invoke 方法说明
参数:
proxy-在其上调用方法的代理实例
method –与在代理实例上调用的接口方法相对应的Method实例。 Method对象的声明类将是在其中声明该Method的接口,它可能是代理类通过其继承该方法的代理接口的超接口。
args –包含在代理实例的方法调用中传递的参数值的对象数组;如果接口方法不带参数,则为null 。 基本类型的参数包装在适当的基本包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
返回值:
从代理实例上的方法调用返回的值。 如果接口方法的声明的返回类型是原始类型,则此方法返回的值必须是对应的原始包装器类的实例;否则,返回true。 否则,它必须是可分配给声明的返回类型的类型。 如果此方法返回的值为null ,并且接口方法的返回类型为原始类型,则代理实例上的方法调用将抛出NullPointerException 。 如果此方法返回的值与上述接口方法的声明的返回类型不兼容,则对代理实例的方法调用将抛出ClassCastException 。
抛出:
Throwable –从代理实例上的方法调用引发的异常。 异常的类型必须可以分配给接口方法的throws子句中声明的任何异常类型,也可以分配给未经检查的异常类型java.lang.RuntimeException或java.lang.Error 。 如果此方法引发的检查异常无法分配给接口方法的throws子句中声明的任何异常类型,则包含该方法引发的异常的UndeclaredThrowableException将由该方法的调用引发。代理实例。
被代理的方法就比较简单了
public interface UserManager {
/**
* 新增用户抽象方法
*
*/
void addUser(String userName, String password);
/**
* 删除用户抽象方法
*
* @param userName
*/
void delUser(String userName);
}
public class UserManagerImpl implements UserManager {
/**
* 新增用户抽象方法
*
*/
@Override
public void addUser(String userName, String password) {
System.out.println("调用了新增的方法!");
System.out.println("传入参数为 userName: " + userName + " password: " + password);
}
/**
* 删除用户抽象方法
*
* @param userName
*/
@Override
public void delUser(String userName) {
System.out.println("调用了删除的方法!");
System.out.println("传入参数为 userName: " + userName);
}
核心代码了解一下
private Object target;//需要代理的目标对象
//定义获取代理对象方法
private Object getJDKProxy(Object targetObject) {
//为目标对象target赋值
this.target = targetObject;
//JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理,监听开始!");
Object result = method.invoke(target, args);
System.out.println("JDK动态代理,监听结束!");
return result;
}
中文翻译下, 如果要调用getJDKProxy方法,需要传被代理的实现类。 传入之后,赋值给变量需要代理的方法,然后代理类创建一个实例,传入类加载器,需要代理的接口名称,还有this也就是需要代理代理器,再本类所以就直接用this了
也就是说,如果用户调用getJDKProxy 方法,他就会被代理类进行处理,代理会除法invoke方法。然后回执行输出,然后方法进行调用,调用完成之后再输出 可以测试一下
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
user.addUser("admin", "123123");//执行新增方法
}
中间两个输出是方法输出, 上下两个是动态代理输出;
所以你想这个东西可以做什么,你想啊,写一个方法是不是要打印日志啊,看入参,方法结束之后是不是要看日志或者后处理,什么的,这不就用上了;
至于还有一种代理模式,也一起写了,大家看看 cglib
Cglib
// 很直白的跟你说,着是方法拦截器
public class CgLibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("JDK动态代理,监听开始!");
methodProxy.invokeSuper(o, objects);
System.out.println("JDK动态代理,监听结束!");
return null;
}
public static void main(String[] args) {
//创建Enhancer对象
Enhancer enhancer = new Enhancer();
//设置父类,即要代理的类
enhancer.setSuperclass(UserManagerImpl.class);
//设置回调对象
enhancer.setCallback(new CgLibProxy());
//创建代理类
UserManager userManager = (UserManager) enhancer.create();
//通过代理类调用目标方法
userManager.addUser("admin", "123123");//执行新增方法
}
}
参数解释 :
- Object var1 代表的是子类代理对象,
- Method var2代表的是要调用的方法反射对象,
- 第三个参数是传递给调用方法的参数,前三个参数和JDK的InvocationHandler接口的invoke方法中参数含义是一样的
- 第四个参数MethodProxy对象是cglib生成的用来代替method对象的,使用此对象会比jdk的method对象的效率要高如果使用method对象来调用目标对象的方法: method.invoke(var1, var3),则会陷入无限递归循环中, 因为此时的目标对象是目标类的子代理类对象
说下为啥要写一个增强剂Enhancer进行处理
MethodProxy 是代理对象,用来代替method对象的
Enhancer
生成动态子类以启用方法拦截。 此类开始是JDK 1.3附带的标准动态代理支持的替代,但该类允许代理除了实现接口外,扩展一个具体的基类。 动态生成的子类覆盖超类的非最终方法,并具有钩子,这些钩子回调到用户定义的拦截器实现。
最原始和最通用的回调类型是MethodInterceptor ,按AOP术语,它启用“周围建议”-也就是说,您可以在调用“ super”方法之前和之后都调用自定义代码。 另外,您可以在调用super方法之前修改参数,或者根本不调用它。
尽管MethodInterceptor具有足够的通用性,可以满足任何拦截需求,但它通常是过大的。 为了简化和提高性能,还提供了其他专用的回调类型,例如LazyLoader 。 通常一个回调将每个增强类可以使用,但你可以控制回调在每一方法的基础与使用CallbackFilter 。
此类的最常见用法体现在静态帮助器方法中。 对于高级需求,例如自定义要使用的ClassLoader ,您应该创建一个Enhancer的新实例。 CGLIB中的其他类遵循类似的模式。
除非使用setUseFactory显式禁用此功能,否则所有增强的对象均实现Factory接口。 Factory接口提供用于更改现有对象的回调的API,以及创建相同类型新实例的更快,更轻松的方法。
有关java.lang.reflect.Proxy的几乎直接替代品,请参见Proxy类。
也就是说创建增强剂,代理子类,设置要代理的方法,回调方法拦截,调用方法的时候,会进行回调到方法拦截器
CGLIB所创建的动态代理对象的性能比JDK所创建的动态代理对象的性能高很多,但创建动态代理对象时比JDK创建动态代理对象要花费更长的时间。
看一下返回的参数
总结
没啥区别,如果说cglib和jdk最大的区别,我觉得性能都是可以接收的,实现方式jdk是通过methed反射技术来处理的 都需要实现接口,一个是方法拦截器,一个是方法调用处理程序
同时哈,cglib实现字节码进行处理的,性能要好一点,但是创建动态代理要耗时一点,这个需要看源码就可以看到反色调用方法是循环处理的,cglib是字节码处理的
再则
jdk 动态代理,class文件缓存在内存,在运行时动态生成代理类进行处理的,只能基于接口进行代理。
cglib 动态代理,是通过继承代理,
还有就是jdk 只能接口进行代理,局限性
cglib 可以代理类
个人觉得局限性在与一个只能通过类进行代理类处理,但是呢cglib可以对方法,比如说一些逻辑组件处理,使用代理类处理。然后逻辑组件再用切面可以适配所有方法。我感觉很爽
所以一般我再写切面处理日志或者一些解耦的东西都是用cglib处理的