详解 动态代理
(请观看本人博文 —— 《详解 反射机制》)
(请观看本人博文 —— 《详解 代理模式》)
动态代理
概述:
- 代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
举例:春季回家买票让人代买- 动态代理:在程序运行过程中,产生的这个对象
而程序运行过程中产生对象,其实就是本人刚才反射讲解的内容,
所以,动态代理其实就是通过反射来生成一个代理
特点:
字节码 随用随创建,随用随加载
作用:
不修改源码的基础上对方法增强
分类:
- 基于接口的动态代理 —— Proxy代理模式
- 基于子类的动态代理 —— CGLib代理模式
注意:JDK给我们提供的动态代理,只能对接口进行代理
(即:Proxy代理模式)
那么,首先,本人来讲解下由JDK提供的Proxy代理模式:
Proxy代理模式:
如何创建代理对象:
使用Proxy类中的newProxyInstance方法
创建代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用
其实,Proxy代理模式 的 动态代理,主要应用了两个方法:
- Proxy类中创建动态代理类对象 的方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)参数:
- loader: 类加载器
- interfaces: 接口对应的一个Class数组
- InvocationHandler: 这个其实就是要代理对象所做的事情的一个类的封装
- 而这个方法,最终会调用InvocationHandler接口的方法:
InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
作用:执行被代理对象的任何接口方法都会经过该方法参数:
- proxy:代理对象的引用
- method:当前执行的方法
- args:当前执行方法所需的参数
返回值:和被代理对象方法有相同的返回值
那么,现在,本人就来展示下通过这些知识点,来编写一个动态代理小工具,并做下使用展示:
首先是 动态代理小工具:
package edu.youzg.about_reflact.core;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
//获取代理对象的方法
//参数:就是被代理对象(目标对象)
//返回值:代理对象
public static IUserDao getProxy(IUserDao userDao) {
IUserDao obj = (IUserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行目标对象的任何接口方法都会经过该方法
Object result = null;
if (method.getName().equals("delete")) { //这里仅仅对该接口的delete()方法做了增加了几个步骤
System.out.println("权限的校验 "); //增强的代码
result = method.invoke(userDao);//让目标对象中方法执行
System.out.println("记录日志"); //增强的代码
} else {
result = method.invoke(userDao);//让目标对象中方法执行
}
return result;
}
});
return obj; //返回代理对象
}
}
现在,本人来给出一个测试接口:
package edu.youzg.about_reflact.core;
public interface IUserDao {
void insert();
void delete();
void update();
void query();
}
本人再来给出一个测试接口的实现类:
package edu.youzg.about_reflact.core;
public class UserDaoImpl implements IUserDao{
@Override
public void insert() {
System.out.println("添加一个用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
@Override
public void query() {
System.out.println("查询用户");
}
}
现在,本人来给出一个测试类,来展示下这个小工具的使用:
package edu.youzg.about_reflact.core;
import com.sun.deploy.net.proxy.ProxyUtils;
public class Test {
public static void main(String[] args) throws Exception {
IUserDao userDao = new UserDaoImpl();
//我们采用动态代理的模式,在不修改原有代码的情况下,对类中的方法进行增强(增加一些额外功能)
//通过我们写的工具类,来获取一个代理对象
IUserDao proxy = ProxyUtil.getProxy(userDao);
proxy.insert();
System.out.println("----------------------");
proxy.delete();
System.out.println("----------------------");
proxy.update();
System.out.println("----------------------");
proxy.query();
}
}
那么,本人来展示下运行结果:
可以看到,只有删除操作,多了几个步骤。
现在,本人来讲解下 CGLib代理模式
CGLib代理模式:
CGLib代理模式不是由JDK所提供的,
所以,我们若是想要运用这种代理模式
就需要导cglib-nodep的Jar包
如何创建代理对象:
- 直接创建一个Enhancer类的对象
- 将被代理类的字节码对象(.class对象)通过参数,
由第一步所创建的Enhancer类对象的setSuperclass()方法设置进去
创建代理对象的要求:
被代理的类,必须实现接口;
CGLib代理的原理:
创建一个被代理类的子类对象
所以:
- 若被代理类本身是final类,则,不能被代理!
- 被代理类中的final方法是不能被代理的;
CGLib代理模式 的 动态代理 的 主要步骤:
主要步骤:
- 直接创建(new)一个Enhancer类的对象
- 将被代理类的字节码对象(.class对象)通过参数,
由第一步所创建的Enhancer类对象的setSuperclass()方法设置进去- 直接创建(new)一个MethodInterceptor()抽象类的对象,并实现其抽象方法:
public Object intercept(Object object, Method method, Object[] args,MethodProxy proxy) throws Throwable参数:
- object: 目标对象的对象
- method: 目标对象方法
- args: 目标对象方法的参数
- proxy:代理后自动调用的方法
那么,现在本人来展示下 CGLib代理模式 的小工具:
package com.mec.proxy.test;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibProxyUtil {
public CGLibProxyUtil() {
}
public static <T> T getProxy(T target) {
Class<?> klass = target.getClass();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object result = null;
if (method.getName().equals("update")) {
System.out.println("拦截前");
result = method.invoke(target, args);
System.out.println("拦截后");
} else {
result = method.invoke(target, args);//让目标对象中方法执行
}
return result;
}
});
return (T) enhancer.create();
}
}
本人还是用上面给出的接口和实现类 为根据,来给出一个测试类:
package com.mec.proxy.test;
public class Test {
public static void main(String[] args) {
UserDaoImpl target = new UserDaoImpl();
UserDaoImpl targetProxy = CGLibProxyUtil.getProxy(target);
targetProxy.insert();
System.out.println("----------------------");
targetProxy.delete();
System.out.println("----------------------");
targetProxy.update();
System.out.println("----------------------");
targetProxy.query();
}
}
那么,现在本人来展示下运行结果:
可以看到,只有修改操作,多了几个步骤。
那么,现在本人来讲解下这两种动态代理的区别:
Proxy代理模式 与 CGLib代理模式 的区别:
区别:
- Proxy代理模式:
产生的代理对象,其类型是目标接口的派生类类型对象
代理对象只能调用接口中的方法
(可以理解为:被代理对象的 兄弟对象)- CGLib代理模式:
产生的代理对象,是 被代理类的子类对象
代理对象可以调用 被代理类 中 除了final修饰 的其它 所有方法
(可以理解为:被代理对象的 子对象)
(本人 反射机制 总集篇博文链接:https:////www.cnblogs.com/codderYouzg/p/12419061.html)
(本人 代理模式 总集篇博文链接:https://www.cnblogs.com/codderYouzg/p/12801349.html)