首先我们要理解,什么是代理,这可真是一个难题,我们这里要讲的代理,更像服务器的反向代理,我们访问反向代理,再由反向代理把我们的请求发送至真实服务器,由服务器返回结果。我们这要讲的代理,也是一个代理类,我们访问代理对象,代理对象不处理我们的请求,而是由实际对象(被代理对象)处理。
然而,这有什么好处呢?使用代理,我们可以在不修改类源码的情况下,(在代理中)对其功能进行增强。比如,我们常见的记录日志,权限控制,参数检测等。
1静态代理
静态代理是由程序员编写,实实在在存在的代理类,原理是代理类实现被代理类相同的接口,在代理类中的方法中,调用被代理类的同名方法。我们以图书馆借书为例:
定义接口
// 定义接口
public interface IBorrowBooks {
public void enter() throws Exception;
public void register(String book) throws Exception;
public void leave() throws Exception;
}
借阅者
// 借阅者
public class Borrower implements IBorrowBooks{
public String name;
public Borrower(String name){
this.name=name;
}
@Override
public void enter() {
System.out.println(this.name+"进入图书馆");
}
@Override
public void register(String book) {
System.out.println(this.name+"借阅"+book);
}
@Override
public void leave() {
System.out.println(this.name+"离开图书馆");
}
}
执行借书过程
public class Test {
public static void main(String[] args){
// 直接运行借书流程
Borrower borrower = new Borrower("张三");
borrower.enter();
borrower.register("《射雕英雄传》");
borrower.leave();
}
}
运行结果:
张三进入图书馆
张三借阅《射雕英雄传》
张三离开图书馆
过了一段时间,图书馆改造升级,安装了门禁,进出都需要刷借阅卡,借书也要刷卡登记,如果我们用代理实现,就可以在不改变原有业务代码的情况下,加上权限检测,当然这里举的例子有点牵强,因为我们改代理类也是改,改原来的代码也是改,那是因为我们这里只是代理了这一种对象,如果该接口有多个实现,那么我改代理类就比改原有业务代码划算了。
借阅者代理类:
// 借阅者代理
public class BorrowerProxy implements IBorrowBooks{
private Borrower borrower;
public BorrowerProxy(String name){
this.borrower = new Borrower(name);
}
@Override
public void enter() throws Exception {
if(!this.checkPermissions()){
throw new Exception("没有权限");
}
this.borrower.enter();
}
@Override
public void register(String book) throws Exception {
if(!this.checkPermissions()){
throw new Exception("没有权限");
}
this.borrower.register(book);
}
@Override
public void leave() throws Exception {
if(!this.checkPermissions()){
throw new Exception("没有权限");
}
this.borrower.leave();
}
public boolean checkPermissions(){
System.out.println("刷借阅卡,权限通过");
return true;
}
}
通过代理执行借书流程:
public class Test {
public static void main(String[] args){
// 通过借阅者代理执行借书流程
BorrowerProxy bp = new BorrowerProxy("张三");
try{
bp.enter();
bp.register("《神雕侠侣》");
bp.leave();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
运行结果:
刷借阅卡,权限通过
张三进入图书馆
刷借阅卡,权限通过
张三借阅《神雕侠侣》
刷借阅卡,权限通过
张三离开图书馆
静态代理,使我们的项目代码变的臃肿,并且,实现起来耗时耗力,所以就有了动态代理的出现。
2动态代理
2.1 jdk动态代理(基于接口)
java动态代理是利用反射机制生成一个实现被代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,所以,被代理类必须实现接口。
Proxy.newProxyInstance(classLoader,interfaces,invocationHandler)
;
在上面静态代理的例子中,我们已经有了接口,和被代理类(借阅者),我们还少一个invocationHandler
创建一个InvocationHandler 其实就是个拦截器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
public class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行了"+method.getName()+"-----"+Arrays.toString(args));
Object res = method.invoke(target,args);
return res;
}
}
实现一个代理工厂类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyFactory {
public static Object createProxy(Object object){
ClassLoader classLoader = object.getClass().getClassLoader();
Class<?>[] interfaces = object.getClass().getInterfaces();
InvocationHandler invocationHandler = new MyInvocationHandler(object);
Object o = Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
return o;
}
}
运行一下试试:
public class Test {
public static void main(String[] args){
// 通过jdk动态代理执行
try{
// 注意,这里强转要用接口的类型
IBorrowBooks borrower1 = (IBorrowBooks)ProxyFactory.createProxy(new Borrower("张三"));
borrower1.enter();
borrower1.register("《天龙八部》");
borrower1.leave();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
运行结果:
执行了enter-----null
张三进入图书馆
执行了register-----[《天龙八部》]
张三借阅《天龙八部》
执行了leave-----null
张三离开图书馆
2.2 cglib动态代理(基于子类)
原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。由于是基于子类,所以对于final方法,无法进行代理。
首先通过maven安装cglib库(https://mvnrepository.com/artifact/cglib/cglib/)
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
在上面例子的基础上,我们只需重写拦截器和代理工厂类即可:
拦截器:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 自己实现一个拦截器
public class CglibInterceptor implements MethodInterceptor {
private Object target;
public CglibInterceptor(Object target){
this.target=target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib动态代理,监听开始!这就是切面");
//target 目标对象 objects 参数数组
Object invoke = method.invoke(target, objects);
System.out.println("Cglib动态代理,监听结束!这就是切面");
return invoke;
}
}
代理工厂类:
import net.sf.cglib.proxy.Enhancer;
public class ProxyCglibFactory {
public static Object createProxy(Object target){
Enhancer enhancer = new Enhancer();
// 从这里可以看出,cglib 代理实现的方式是创建了目标对象的子类,所以目标对象要能被继承
enhancer.setSuperclass(target.getClass());
// 设置回调,就是设置拦截器
enhancer.setCallback(new CglibInterceptor(target));
Object result = enhancer.create();
return result;
}
}
运行一下看看效果:
public class Test {
public static void main(String[] args){
// 通过cglib动态代理执行
try{
IBorrowBooks borrower2 = (IBorrowBooks) ProxyCglibFactory.createProxy(new Borrower("张三"));
borrower2.enter();
borrower2.register("《倚天屠龙记》");
borrower2.leave();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
运行结果:
Cglib动态代理,监听开始!这就是切面
张三进入图书馆
Cglib动态代理,监听结束!这就是切面
Cglib动态代理,监听开始!这就是切面
张三借阅《倚天屠龙记》
Cglib动态代理,监听结束!这就是切面
Cglib动态代理,监听开始!这就是切面
张三离开图书馆
Cglib动态代理,监听结束!这就是切面
注意:
这种方式要求被代理类有一个无参构造
如果报错Unable to make protected final java.lang.Class java.lang.ClassLoader.defineC,需要在idea中配置jvm启动参数
–add-opens java.base/java.lang=ALL-UNNAMED