java 静态代理和动态代理
在学习java过程中有看到资料,什么 final 类没法动态代理之类的标注,动态代理这个词经常看到,好奇心,顺便研究了下关于java的代理,关于final这个具体在编译和运行期要情况,后面再整理个文章出来,以做收藏。
代理,顾名思义,跟委托意思差不多,比如你需要将你的网站申请网上ICP经营许可证,但你不懂具体怎么搞,可以找第三方中介机构(因为有中介是专门做这个的,比你自己申请要多要好省),委托他们帮你去做这件事。当然,不管谁去申请ICP,做法都是一样的,相关单位预设了相应接口(如需要准备什么资料、法人信息、公司经营许可、在哪里申请、申请流程等),只要附合这些接口需要提供的东西,就可以申请到许可证。
上面是一个代理(委托)的过程。
如有一个客户系统,两个功能,客户跟进和信息修改,看代码:
做法一
package com.blueinfinite;
import lombok.extern.slf4j.Slf4j;
/**
* 客户业务 例1
*/
@Slf4j
public class Custom1 {
/**
* 客户跟进
*/
public void follow() {
log.info("通用功能:验证是否有权限跟进");
log.info("业务A:客户跟进");
log.info("通用功能:记录日志");
}
/**
* 客户信息修改
*/
public void edit() {
log.info("通用功能:验证是否有权限跟进");
log.info("业务B:客户信息修改");
log.info("通用功能:记录日志");
}
}
测试代码:
@Test
public void test1() {
Custom1 c = new Custom1();
c.follow();
c.edit();
}
弊端:两个业务功能跟权限和日志记录混合到一起,原有业务职责不明确、偶合度高且维护难
做法二,简单封装
package com.blueinfinite;
import lombok.extern.slf4j.Slf4j;
/**
* 客户业务 例2
*/
@Slf4j
public class Custom2 {
/**
* 客户跟进
*/
public void follow() {
CustomUtils.verifyPermissions();
log.info("业务A:客户跟进");
CustomUtils.addlog();
}
/**
* 客户信息修改
*/
public void edit() {
CustomUtils.verifyPermissions();
log.info("业务B:客户信息修改");
CustomUtils.addlog();
}
}
package com.blueinfinite;
import lombok.extern.slf4j.Slf4j;
/**
* 客户功能封装
*/
@Slf4j
public class CustomUtils {
public static void verifyPermissions() {
log.info("通用功能:验证是否有权限跟进");
}
public static void addlog() {
log.info("通用功能:记录日志");
}
}
测试代码:
@Test
public void proxy2() {
Custom2 c = new Custom2();
c.follow();
c.edit();
}
虽然进行了常用功能的封装,但在很多业务场景中出还是有代码冗余(如方法调用)
做法三,静态代理
package com.blueinfinite;
/**
* 静态代理
*/
public interface Custom3Proxy {
/**
* 客户跟进
*/
public void follow();
/**
* 客户信息修改
*/
public void edit();
}
package com.blueinfinite;
import lombok.extern.slf4j.Slf4j;
/**
* 客户业务 例3
*/
@Slf4j
public class Custom3 implements Custom3Proxy {
/**
* 客户跟进
*/
public void follow() {
log.info("业务A:客户跟进");
}
/**
* 客户信息修改
*/
public void edit() {
log.info("业务B:客户信息修改");
}
}
package com.blueinfinite;
import lombok.extern.slf4j.Slf4j;
/**
* 静态代理实现
*/
@Slf4j
public class Custom3ProxyImpl implements Custom3Proxy {
private Custom3Proxy proxy;
public Custom3ProxyImpl(Custom3Proxy proxy){
this.proxy = proxy;
}
/**
* 客户跟进
*/
public void follow() {
CustomUtils.verifyPermissions();
proxy.follow();
CustomUtils.addlog();
}
/**
* 客户信息修改
*/
public void edit() {
CustomUtils.verifyPermissions();
proxy.edit();
CustomUtils.addlog();
}
}
测试代码:
@Test
public void proxy3() {
Custom3Proxy c = new Custom3ProxyImpl(new Custom3());
c.follow();
c.edit();
}
通过使用静态代理,我们发现客户业务功能(Custom3)相比之前纯洁多了,功能职责不混乱,职责更加单一,清爽。在更好的解偶的情况下,且不修改原有业务代码(Custom3)实现了功能增强。
弊端是实现类和代理类都需要共同的接口,增加代码重复。
做法四,动态代理
package com.blueinfinite;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理 例4
*/
public class DynamicProxy {
public static T getObj(final Object target) {
return (T)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//验证是否有权限跟进
CustomUtils.verifyPermissions();
//具体业务
Object result = method.invoke(target, args);
//记录日志
CustomUtils.addlog();
return result;
}
}
);
}
}
测试代码:
@Test
public void proxy4() {
Custom3Proxy proxy = DynamicProxy.getObj(new Custom3());
proxy.follow();
proxy.edit();
}
通过jdk动态代理,弥补了静态代理的不足。
java中动态代理主要分两种,除了jdk动态代理,cglib也是可实现动态代理。
jdk动态代理通过反射机制,找到对象的接口和元数据,可强制执行对象的动作。代理主要用到了两个类,java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy,后续将讲解这两个类是怎么实现动态代理、以及cglib的实现原理。
通过代理,我们可以将代码无侵入增强(即无侵入式编程),spring的AOP可以通过动态代理实现无侵入编程。
源码地址:https://github.com/blueinfinite/javaproxy