jdk动态代理
创建一个人类接口:两个功能 说话 奔跑
public interface Person {
void speak();
@MethodName("run") //注解表明对所需的方法进行拦截(后面使用)
void run();
}
创建王先生类继承Person并实现了上述两个方法
public class Wang implements Person {
@Override
public void speak() {
System.out.println("You are so nice!");
}
@Override
public void run() {
System.out.println("Running is good for you!");
}
}
创建代理类:
- 代理类继承InvocationHandler接口
- Proxy.newInstance()方法创建代理类,参数含义:
- 目标对象的ClassLoader
- 需要代理的接口
- InvocationHandler对象
- invoke方法,使用反射执行target对象的方法,方法前后可以加上我们需要切入的操作
public class TargetProxyA implements InvocationHandler{
private Object target;
public TargetProxyA(Object target) {
this.target = target;
}
public static Object bind(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new TargetProxyA(target));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("思考一波!");
return method.invoke(target, args);
}
}
调用TargetProxyA创建代理类对象p,执行speak(),发现动态加入了思考的操作
public class Client {
public static void main(String[] args) {
Person p = new Wang();
p = (Person) TargetProxyA.bind(p);
p.speak();
}
}
优化上述代码
不足之处:
- 拦截逻辑被写死在invoke方法里面
- 接口中不是所有的方法都需要拦截
根据设计模式的迪米特法则,我们将目标类target,它的方法method, 参数args封装到Invocation,让代码更有结构和层次:
public class Invocation {
private Object target;
private Method method;
private Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
}
将拦截方法所需要的操作写到新的接口中, Invocation作为参数传入
拦截器上加注解,指定要拦截的方法:
@MethodName("speak")
public interface Interceptor {
Object think(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
}
在拦截器的实现类中切入我们需要的操作
public class InterceptorImpl implements Interceptor {
@Override
public Object think(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
System.out.println("我是拦截器!");
return invocation.proceed();
}
}
代理类:
public class TargetProxy implements InvocationHandler{
private Object target;
private Interceptor interceptor;
public TargetProxy(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
public static Object bind(Object target, Interceptor interceptor) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new TargetProxy(target, interceptor));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取拦截器上的注解,指定要拦截的方法
MethodName methodName = this.interceptor.getClass().getAnnotation(MethodName.class);
String name = methodName.value();
//比较拦截方法名是否与拦截器指定方法相同
if(name.equals(method.getName())) {
return interceptor.think(new Invocation(target,
method, args));
}
return method.invoke(target, args);
}
}
public class Client {
public static void main(String[] args) {
Interceptor interceptor = new InterceptorImpl();
Person p = new Zhang();
//创建代理对象,加上拦截器
p = (Person) TargetProxy.bind(p, interceptor);
p.speak();
}
}
注:如果不在拦截器加注解指定要拦截的方法,那么可以在被拦截的方法上加注解
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取目标方法注解
String name = method.getAnnotation(MethodName.class).value();
//与目标方法名比较
if(name.equals(method.getName())) {
return interceptor.think(new Invocation(target,
method, args));
}
return method.invoke(target, args);
}
public class Client {
public static void main(String[] args) {
Interceptor interceptor = new InterceptorImpl();
Person p = new Zhang();
//创建代理对象,加上拦截器
//p = (Person) TargetProxy.bind(p, interceptor);
p = (Person) TargetProxy1.bind(p, interceptor);
//p.speak();
p.run();
}
}
cglib动态代理
原理: 对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
JDK代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制性要求。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。
在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。
生成代理类:
public class TargetProxy implements MethodInterceptor {
//Enhancer是CGLIB的字节码增强器
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("前置代理:思考下");
Object result = methodProxy.invokeSuper(o, args);
System.out.println("后置代理:吃饭了");
return result;
}
}
生成代理类对象的步骤:
- 生成代理类的二进制字节码文件
- 加载二进制字节码,生成Class对象(使用反射)
- 通过反射机制获得实例构造,并创建代理类对象
public class Target {
public void run() {
System.out.println("中午吃啥?");
}
}
public class Client {
public static void main(String[] args) {
TargetProxy targetProxy = new TargetProxy();
Target proxy = (Target) targetProxy.getProxy(Target.class);
proxy.run();
}
}