
Java强大的动态代理 ,给与Java强大的活力 ,例如AOP 的实现 , 其实就是基于java的动态代理来实现的, 获取一个增强后的对象, 动态代理依靠字节码技术 , 可以运行时进行代码生成, 而不入侵原有的代码,
例如Spring的AOP , Mybatis 的 Intercept 许多都是基于这个实现的 ,
为什么要说拦截器 , 拦截器可以让用户拓展功能实现, 在运行关键期可以可以实现可见性 , 让开发者可以更加轻松的完成各种操作
1. 设计一个简单的拦截器
1. 开始
我们简易设计了一个拦截器对象
public interface Interceptor {
/**
* 方法参数
*
* @param args
*/
void before(Object[] args);
/**
* 方法调用完成后
*
* @param result 方法的返回结果
* @return Object 增强后的结果
*/
Object after(Object result);
/**
* 执行出现异常
*
* @param e 异常
*/
void error(Exception e);
}
设计一个代理类
public class MyProxy implements InvocationHandler {
// 拦截器对象
private final Interceptor interceptor;
// 增强的类
private final Object target;
public MyProxy(Interceptor interceptor, Object target) {
this.interceptor = interceptor;
this.target = target;
}
// 返回一个代理对象
public static Object getInstance(Object target, Interceptor interceptor) {
MyProxy proxy = new MyProxy(interceptor, target);
return Proxy.newProxyInstance(proxy.target.getClass().getClassLoader(), proxy.target.getClass().getInterfaces(),
proxy);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object methodResult = null;
Object proxyResult = null;
try {
// 事前
interceptor.before(args);
methodResult = method.invoke(target, args);
} catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
// 异常
interceptor.error(e);
} finally {
//事后
proxyResult = interceptor.after(methodResult);
}
return proxyResult;
}
}
实现接口
public interface IEchoService {
String echo(String msg);
}
我们的实现方法
public class EchoServiceImpl implements IEchoService {
@Override
public String echo(String msg) {
return null;
}
}
我们的测试类 :
public class Demo {
public static void main(String[] args) {
IEchoService echoService = new EchoServiceImpl();
IEchoService instance = (IEchoService) MyProxy.getInstance(echoService, new Interceptor() {
@Override
public void before(Object[] args) {
for (Object arg : args) {
System.out.printf("args : %s \t", arg);
}
System.out.printf("\r\n");
}
@Override
public Object after(Object result) {
if (result == null) {
return "hello world";
}
return result;
}
@Override
public void error(Exception e) {
System.out.println("exception : "+(e == null ? "null" : e));
}
});
String result = instance.echo("tom");
System.out.println(result);
}
}
输出 :
args : tom
hello world
2. 总结
我们设计的有什么优点和缺点 :
优点 : 可拓展性很强 , 对于一个方法的参数和返回值,异常等都进行了拓展
缺点 ; 没有泛型支持 , 对于方法没有进行一个抽象封装 , Java默认提供的几个参数我们需要加以封装变得更加实用性 , 拓展不是用户管理而是我们管理 ,我们需要进一步优化 ,
2. 继续改进
1. 主体改造部分
1. 设计一个 Invocation 类
public class Invocation<T> {
/**
* 增强的对象
*/
private final T target;
/**
* 方法对象
*/
private final Method method;
/**
* 方法参数
*/
private final Object[] args;
public Invocation(T target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public T getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
/**
* 如果你不想修改可以 .... ,或者 try - catch 都可以
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
2. 接口类
public interface Interceptor<T> {
Object intercept(Invocation<T> invocation) throws Throwable;
}
3. 代理实现类
package com.intercept.java;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* TODO
*
* @date:2019/12/12 13:10
* @author: <a href='mailto:fanhaodong516@qq.com'>Anthony</a>
*/
public class MyProxy<T> implements InvocationHandler {
private final Interceptor<T> interceptor;
private final T target;
// 私有是因为不能开发者进行实例化
private MyProxy(Interceptor<T> interceptor, T target) {
this.interceptor = interceptor;
this.target = target;
}
// 写成V 是因为让大家区分开 静态方法的泛型和非静态的不同(类声明的T) ,
public static <V> V getInstance(final V target, final Interceptor<V> interceptor) {
// 不是接口直接返回
Class<?>[] interfaces = target.getClass().getInterfaces();
if (interfaces == null) {
return target;
}
final MyProxy<V> proxy = new MyProxy<V>(interceptor, target);
return (V) Proxy.newProxyInstance(proxy.target.getClass().getClassLoader(), proxy.target.getClass().getInterfaces(),proxy);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return interceptor.intercept(new Invocation<T>(target, method, args));
}
}
2. 简单使用
接口
public interface EchoService { void echo(String msg);}
接口实现
public class EchoServiceImpl implements EchoService{
@Override
public void echo(String msg) {
// 模拟异常
int x = 1 / 0;
// 输出
System.out.println(msg);
}
}
测试类 :
public class Demo {
public static void main(String[] args) {
EchoService echoService = new EchoServiceImpl();
Interceptor<EchoService> interceptor = new Interceptor<EchoService>() {
@Override
public Object intercept(Invocation<EchoService> invocation) throws Throwable {
try {
invocation.getTarget().echo((String) invocation.getArgs()[0]);
} catch (Exception e) {
System.out.println("异常信息 : " + e.getMessage());
}
return null;
}
};
EchoService proxyEchoService = MyProxy.<EchoService>getInstance(echoService, interceptor);
proxyEchoService.echo("hello");
}
}
结果 :
异常信息 : / by zero
3. 总结
我们发现如果接口有多个实现类, 如果我们的拦截器是面向方法实现的改如何做呢 ?
比如我这个拦截器只拦截这个方法, 其他方法不拦截 , 那么我们改如何做 ?
其实很简单 ,当我们生成代理对象的时候对于invoke 那里进行判断就行了 , 我简单实现一个例子
接口有两个方法
public interface EchoService {
void echo(String msg);
void say(String msg);
}
接口实现类 :
public class EchoServiceImpl implements EchoService {
@Override
public void echo(String msg) {
System.out.println("echo : " + msg);
}
@Override
public void say(String msg) {
System.out.println("say : " + msg);
}
}
代理类 : 修改这里 MyProxy<T> #invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("echo")) {
return method.invoke(target, args);
}
return interceptor.intercept(new Invocation<T>(target, method, args));
}
测试类 ;
public class Demo {
public static void main(String[] args) {
EchoService echoService = new EchoServiceImpl();
Interceptor<EchoService> interceptor = new Interceptor<EchoService>() {
@Override
public Object intercept(Invocation<EchoService> invocation) throws Throwable {
System.out.println("time : " + new Date());
return invocation.proceed();
}
};
EchoService proxyEchoService = MyProxy.<EchoService>getInstance(echoService, interceptor);
System.out.println("=========echo===========");
proxyEchoService.echo("hello");
System.out.println("=========say===========");
proxyEchoService.say("hello world");
}
}
执行结果 :
=========echo===========
echo : hello
=========say===========
time : Thu Dec 12 14:41:53 CST 2019
say : hello world
发现只增强了 say
4. 再次总结
我整个是基于 Mybatis的拦截器实现的 ,基本上和他的思想是一样的 , 我的MyProxy类对应的就是他的 Plugin 类
我的 Interceptor 和 Invocation 跟他的一样 , 所以颗粒度的降低需要再次细化,比如注解之类的.... 所以Java的强大还是很厉害的
3. 总结
其实拦截器无法就是动态代理去实现, 他可以实现无入侵式的进行方法的拦截, 增强之类的,