Java中代理模式(三种)
代理模式概念
代理模式是一种结构性设计模式,代理对象做为一个中间层,对真实对象的操作进行拦截或者增添额外的功能,提高系统间接性和灵活性。
抽象概念分解如下:
- 抽象主题:一个接口或者抽象类,定义了真实对象和代理对象的方法。
- 真实主题:实现抽象主题接口的具体类
- 代理:在真实主题基础上增添功能
JAVA代理模式代码
1.静态代理
public interface StaticDao {
/**
* 静态类初始化方法
*/
default void staticMethod(){
System.out.println("静态类初始化方法");
}
}
public class StaticDaoProxy implements StaticDao{
private final StaticDao staticDao;
public StaticDaoProxy(StaticDao staticDao) {
this.staticDao = staticDao;
}
@Override
public void staticMethod(){
System.out.println("静态方法拓展");
staticDao.staticMethod();
}
}
public class StaticProxyTest {
public static void main(String[] args) {
// 创建动态代理对象. 匿名内部类
StaticDaoProxy staticDaoProxy = new StaticDaoProxy(new StaticDao() {
@Override
public void staticMethod() {
StaticDao.super.staticMethod();
}
});
// 调用方法
staticDaoProxy.staticMethod();
}
}
2.jdk动态代理
public interface JdkDao {
default void jdkMethod(){
System.out.println("jdk动态代理初始化方法");
}
}
public class JdkProxy implements InvocationHandler {
/**
* 正在被此实例代理的目标对象。
*/
private Object target;
/**
* 构造一个新的{@code JdkProxy}实例,用于给定的目标对象。
*
* @param target 其方法将被拦截并增强的对象。
*/
public JdkProxy(Object target) {
this.target = target;
}
/**
* 返回一个代理实例,该实例将方法调用委托给底层目标对象,
* 同时允许通过{@link #invoke(Object, Method, Object[])}方法拦截并
* 修改这些方法的行为。
*
* @return 为目标对象生成的代理实例。
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 当代理实例进行方法调用时实际执行的方法。
* 在此方法中,可在调用原方法前后添加额外操作(如:打印日志、执行验证等)。
* 最终通过`method.invoke(target, args)`调用被代理对象的原方法并返回结果。
*
* @param proxy 代理类
* @param method 被代理方法
* @param args 方法参数
* @return 原方法调用后的返回结果
* @throws Throwable 如果原方法调用过程中抛出异常,此方法将抛出该异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理拓展");
return method.invoke(target, args);
}
}
public class JdkProxyTest {
public static void main(String[] args) {
// 创建一个JdkProxy实例,传入待代理的JdkDao对象。这里使用匿名内部类继承JdkDao并重写其jdkMethod方法。
JdkProxy jdkProxy = new JdkProxy(new JdkDao() {
@Override
public void jdkMethod() {
// 调用父类(即JdkDao接口默认实现)的jdkMethod方法
JdkDao.super.jdkMethod();
}
});
// 通过JdkProxy获取代理实例,并将其强制转换为JdkDao类型
JdkDao jdkDao = (JdkDao) jdkProxy.getProxyInstance();
// 调用代理对象的jdkMethod方法,此时会触发JdkProxy中的invoke方法进行动态代理逻辑
jdkDao.jdkMethod();
}
}
3.cglib动态代理
public class CglibDao {
public void cglibMethod(){
System.out.println("cglib动态代理初始化");
}
}
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxyInstance() {
// 1. 创建Enhancer工具类实例
Enhancer enhancer = new Enhancer();
// 2. 设置需要创建子类(代理对象)的父类(被代理对象的类)
enhancer.setSuperclass(target.getClass());
// 3. 设置回调方法(代理逻辑),即将CglibProxy自身作为回调处理器
enhancer.setCallback(this);
// 4. 生成并返回代理对象
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("cglib动态代理拓展方法");
// 调用原始方法并返回结果
return method.invoke(target, args);
}
}
public class CglibProxyTest {
public static void main(String[] args) {
// 创建一个CglibProxy实例,传入待代理的CglibDao对象
CglibProxy cglibProxy = new CglibProxy(new CglibDao());
// 通过CglibProxy获取代理实例,并将其强制转换为CglibDao类型
CglibDao proxyInstance = (CglibDao) cglibProxy.getProxyInstance();
// 调用代理对象的cglibMethod方法,此时会触发CglibProxy中的intercept方法进行动态代理逻辑
proxyInstance.cglibMethod();
}
}
动态代理对比
JDK动态代理:
- 实现原理:基于Java反射和InvocationHandler接口,当目标类实现了至少一个接口时,JDK动态代理可以通过创建一个实现了所有目标接口的新代理类来工作。
- 特点:
- 只能针对实现了接口的类创建代理。
- 不需要引入第三方库,直接由JDK的标准库提供支持。
- 创建代理的速度相对较慢,因为涉及到接口方法的反射调用。
- 由于是基于接口,因此代理对象不持有目标类的引用,而是持有目标接口的引用,故不能访问到目标类的非接口方法或者私有方法。
CGLIB动态代理:
- 实现原理:基于ASM库来操作字节码,能够通过继承目标类并生成其子类的方式来实现代理,即使目标类没有实现任何接口也能适用。
- 特点:
- 不仅能代理接口,还能代理未实现接口的普通类,应用范围更广。
- 需要引入CGLIB库,不是Java标准库的一部分。
- 生成代理类的速度理论上可能比JDK代理稍快,因为它是在编译期生成的字节码。
- 由于是继承方式,可以访问并覆盖目标类的所有方法(包括非公开方法),但如果目标类是final类,则无法对其进行代理。
Spring框架默认优先使用JDK动态代理,若目标类不满足接口条件则退而使用CGLIB。