专业的JAVA编程教程与资源

网站首页 > java教程 正文

被说烂了的三种Java代理模式到底是怎样实现的?

temp10 2024-10-24 17:21:47 java教程 16 ℃ 0 评论

当一个对象不适合或者不能被其他对象直接引用时,可以给该对象生成一个代理对象。代理对象具有被代理对象的所有功能,这样其他对象使用代理对象时一样可以达到自己的需求。

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

被说烂了的三种Java代理模式到底是怎样实现的?

首先定义接口

public interface BaseDao {
 public void add();
}

被代理对象

public class UserDao implements BaseDao {
 @Override
 public void add() {
 System.out.println("add User");
 }
}

静态代理对象

public class UserDaoStaticProxy implements BaseDao {
 private UserDao userDao;
 public UserDaoStaticProxy() {
 userDao = new UserDao();
 }
 @Override
 public void add() {
 System.out.println("执行前准备...");
 userDao.add();
 System.out.println("执行后处理...");
 }
}

使用方式

public class Run {
 public static void main(String[] args) {
 UserDaoStaticProxy userDaoStaticProxy = new UserDaoStaticProxy();
 userDaoStaticProxy.add();
 }
}

直接使用代理对象UserDaoStaticProxy来代替UserDao来使用,因为代理对象和被代理对象有着相同的父类,所以他们有着相同的功能。

静态代理的缺点是必须为每个被代理对象生成一个代理对象,如果有几十个被代理对象的话,明显不适合用这种方式。此时我们必须使用动态代理来解决这个问题。

动态代理

动态代理有两种实现方式,一种是使用jdk自带的方式,一种是使用cglib的方式。下面我们具体演示下两种方式具体如何实现。

jdk方式实现动态代理

代理工厂类

public class DynamicProxyFactory_java {
 private Object object;
 //被代理对象
 public DynamicProxyFactory_java(Object object) {
 this.object = object;
 }
 public Object getProxyInstance() {
 return Proxy.newProxyInstance(
 object.getClass().getClassLoader(),
 object.getClass().getInterfaces(),//所以这种模式必须实现接口
 new InvocationHandler() {
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 System.out.println("执行前准备...");
 Object invoke = method.invoke(object, args);
 //执行被代理对象的方法
 System.out.println("执行后处理...");
 return invoke;
 }
 }
 );
 }
}

在代理工厂的构造方法中我们传入需要被代理的对象,再通过调用getProxyInstance()方法即可获取代理对象,使用方式如下

public class Run {
 public static void main(String[] args) {
 BaseDao baseDao = new UserDao();
 BaseDao userDao = (BaseDao) new DynamicProxyFactory_java(baseDao).getProxyInstance();
 userDao.add();
 System.out.println("代理前对象:" + baseDao.getClass().getName());
 System.out.println("代理后对象:" + userDao.getClass().getName());
 }
}

执行后结果

执行前准备...
add User
执行后处理...
代理前对象:proxy.UserDao
代理后对象:com.sun.proxy.$Proxy0

我们使用代理工厂,就可以生产出所有符合需求的代理对象,不需要为每个被代理对象创建一个代理对象。

但是这种动态代理不足之处在于被代理对象必须要实现一个接口。如果有一个需要被代理的对象没有实现接口,怎么办呢?往下看!

cglib动态代理

cglib动态代理并不是jdk自带的方式,因此我们必须引入第三方jar包(比如:cglib-3.0.12.jar)。

cglib动态代理工厂

public class DynamicProxyFactory_cglib implements MethodInterceptor {
 private Object object;
 //被代理对象
 public DynamicProxyFactory_cglib(Object object) {
 this.object = object;
 }
 /**
 * 创建代理对象
 *
 * @return
 */
 public Object getProxyInstance() {
 //工具类
 Enhancer enhancer = new Enhancer();
 //设置父类
 enhancer.setSuperclass(object.getClass());
 //设置回调函数
 enhancer.setCallback(this);
 //创建对象并返回
 return enhancer.create();
 }
 @Override
 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
 System.out.println("执行前准备...");
 Object invoke = method.invoke(object, args);
 System.out.println("执行后处理...");
 return invoke;
 }
}

假设我们有一个需要被代理的对象,但是没有实现任何的接口

//没有实现任何的接口
public class MenuDao {
 public void add() {
 System.out.println("add Menu");
 }
}

cglib动态代理使用方式

public class Run {
 public static void main(String[] args) {
 MenuDao dao = new MenuDao();
 MenuDao menuDao = (MenuDao) new DynamicProxyFactory_cglib(dao).getProxyInstance();
 System.out.println("代理前对象:" + dao.getClass().getName());
 System.out.println("代理后对象:" + menuDao.getClass().getName());
 menuDao.add();
 }
}

执行后结果如下

代理前对象:proxy.MenuDao
代理后对象:proxy.MenuDao$EnhancerByCGLIB$bbcb6715
执行前准备...
add Menu
执行后处理...

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

既然有两种动态代理模式,那么到底使用哪种方式呢?这里我们看下springAop中是如何实现的

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
 @Override
 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
 Class<?> targetClass = config.getTargetClass();
 if (targetClass == null) {
 throw new AopConfigException("TargetSource cannot determine target class: " +
 "Either an interface or a target is required for proxy creation.");
 }
 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
 return new JdkDynamicAopProxy(config);
 }
 return new ObjenesisCglibAopProxy(config);
 } else {
 return new JdkDynamicAopProxy(config);
 }
 }
}

上述代码中,我们只需要关注判断条件:targetClass.isInterface(),看方法名字就知道判断这个类是不是接口。如果是接口,那么就使用jdk动态代理来实现,否则就使用Cglib动态代理来实现。

Java肖先生:专注于Java开发技术的研究与知识分享!

————END————

  • 点赞(感谢)
  • ...
  • 转发(感谢)
  • ...
  • 关注(感谢)
  • ...

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表