java中静态代理和动态代理

文章详细介绍了代理模式,包括静态代理和动态代理的概念和应用场景。静态代理中,通过代理类实现目标对象的功能增强,但不利于代码扩展。动态代理分为JDK动态代理和CGLIB动态代理,允许在运行时动态生成代理类,增强了代码的灵活性和可扩展性。JDK动态代理基于接口实现,而CGLIB则通过继承目标类来创建代理,两者分别适用于不同的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

  • 代理模式:属于结构型的模式。指对象本身不做实际的操作,而是通过其他对象(代理对象)来实现得到自己想要的结果(增强处理)
  • 意义:目标对象只需要关注自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能。

静态代理:

静态代理:所谓静态就是在程序运行前已经存在代理类了,代理类有程序员创建或者工具生成,代理类和被代理类在程序运行前的关系已经确定。

案例:

/**
 * 定义一个学生行为的接口
 * @author user
 *
 */
public interface SturentAction {
	//买书的行为
	public void buyBook();

}
//被代理对象学生
public class Sturent  implements SturentAction{

	@Override
	public void buyBook() {
		System.out.println("学生:"+"学生买到了书");
		
	}

}
//学生的代理类 可看成书店
public class ProxySturent implements SturentAction{
	
	private Sturent sturent;
	
	public ProxySturent(Sturent Sturent) {
		this.sturent=Sturent;
	}

	@Override
	public void buyBook() {
		System.out.println("书店:"+"书店到了学生想要的书");
		sturent.buyBook();
		
	}

}
//测试类
public class SturentTest {
	public static void main(String[] args) {
		Sturent sturent=new Sturent();
		ProxySturent proxySturent=new ProxySturent(sturent);
		proxySturent.buyBook();
	}

}

 结果:书店代理学生找到了自己要的书

书店:书店到了学生想要的书
学生:学生买到了书
 

 静态代理的问题:

  • 不利于代码的扩展,如在接口中添加一个方法,它的实现类都要添加这个方法,否则报错。
  • 如果对被代理对象增加其他的增强操作,可能要创建很多代理类

 动态代理:

动态代理:动态代理类是有在程序运行期间有JVM根据反射等机制动态生成的,代理类和被代理类的关系是在程序运行时确定的。在不改变原有的功能代码的前提下,能够动态的实现方法的增强。

可以在不改变方法源码的情况下,实现对方法功能的增强,提高代码复用性。

简化了编程工作,提高开发效率,同时提高软件系统的可扩展性

可以为被代理对象的所有方法做代理

非常灵活 支持任意接口类型的实现类对象做代理,也可以直接接本身做代理

动态代理分为:jdk动态代理和Cglib动态动态代理

jdk动态代理:

jdk动态代理:是使用反射包中类和接口实现动态代理

实例:

1.创建接口:

 2.创建实现类(需要代理的类)

 3.创建增强类

 4.创建代理类(方法拦截器),这要实现InvocationHandler接口,重写invoke方法,这里可以对方法进行增强。

  • InvocationHandler接口,用来做方法拦截

  •  invoke方法中的的参数介绍 

            。Proxy:代理实例,可以通过newProxyInstance创建代理实例

            。Method:执行的目标方法,反射通过调用invoke方法执行

             。args:参数数组

public class TransactionHandler implements InvocationHandler {
	//增强类对象
	private DaoTransaction transaction;
	//需要代理的目标对象
	private Object obj;
	//构造方法
	public TransactionHandler(DaoTransaction transaction,Object o) {
		this.transaction=transaction;
		this.obj=o;
		
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object ret=null;
		//判断当前方法是否是save方法,是才做事务操作
		if ("save".equals(method.getName())) {
			
			transaction.before();//增强操作
			ret=method.invoke(obj, args);
			transaction.after();
			
		}else {
			//不做增强处理
		}
		
		return ret;
	}

}

测试


public class TransactionTest {

	public static void main(String[] args) {				
        //增强类 
		DaoTransaction transaction=new DaoTransaction();
		//目标类,要实现接口
		IStudentService studentService=new IStudentServiceImple();
		
		//方法拦截器 代理类,要实现InvocationHandler接口,重写invoke方法,在方法中实现增强处理
		TransactionHandler transactionHandler=new TransactionHandler(transaction, studentService);
		//创建代理类对象(代理实例)
		IStudentService serviceProxy=(IStudentService)Proxy.newProxyInstance(studentService.getClass().getClassLoader(), studentService.getClass().getInterfaces(), transactionHandler);
		//调用方法
		serviceProxy.save();
		
	}

}

运行结果:

开启事务操作
保存学生信息
关闭事务

 这就完成了一个简单的动态代理

JDK动态代理这一步最最重要:

//创建代理类对象(代理实例)
		IStudentService serviceProxy=(IStudentService)Proxy.newProxyInstance(studentService.getClass().getClassLoader(), studentService.getClass().getInterfaces(), transactionHandler);

生成动态代理的对象

newProxyInstance方法源码:

  • ClassLoader类加载器:直接通过需要代理的类获取
  • Class[]:目标类实现的所有接口
  • InvocationHandler :方法拦拦截处理器,可以在里面实现方法的增强处理。

newProxyInstance方法要传入,类加载器,接口,和InvocationHandler 

   @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

JDK动态代理原理:

通过生成代理类对象字节码文件学习:

自己没有生成。。只能看别人的啦。

生成的动态代理大概这样:

 $Proxy1就是生成的动态代理对象,它继承了Proxy类和实现了目标接口IStudentService。

super就是Proxy,.h就是InvocationHandler接口,.invoke方法调用的就是我们设置的代理类(方法拦截处理器)TransactionHandler 中的invoke方法。

Proxy类源码:

方法原理执行的过程:

 JDK动态代理主要就是通过生成代理实例,这个代理实例继承了一个代理类(Proxy类)并且实现了目标接口,在代理实例中通过反射获取目标接口中的方法。在代理实例生成的方法中通过调用设置的代理类(实现了InvocationHandler接口的类)中的invoke方法来实现对目标方法的增强处理。

注意:jdk动态代理生成的代理实例是通过实现目标接口,通过反射获取目标方法的,因此jdk动态代理的目标类必须要实现接口。

CGLIB动态代理:

    为什么会有CGLIB动态代理:jdk动态代理有一个前提,是需要的代理的类必须要实现接口,如果没有实现接口,只能通过CGLIB来实现,其实CGLIIB是对JDK动态代理的一个补充。

 

CGLIB创建代理类(方法拦截器),这要实现MethodInterceptor接口,重写intercept方法,这里可以对方法进行增强。

//cglib代理类 方法拦截器
public class CglibInterceptor implements MethodInterceptor {
	
	//增强类对象
	private DaoTransaction transaction;

	//构造方法
	public CglibInterceptor(DaoTransaction transaction) {
		this.transaction=transaction;	
	}
    /**
     * 在此方法中做目标方法的增强处理
     */   
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {		
		//调用增强处理方法		
		transaction.before();		
		//调用目标类的目标方法
		Object ret = proxy.invokeSuper(obj, args);
		transaction.after();
		//调用增强处理方法
		return ret;
	}

}

测试:

public class CglibInterceptorTest {
	
	public static void main(String[] args) {
		//得到方法拦截器
		CglibInterceptor cglibInterceptor=new CglibInterceptor(new DaoTransaction());
		//使用CGLIB框架生成目标类的子类(代理类)实现增强
		Enhancer enhancer=new Enhancer();
		//设置父类字节码
		enhancer.setSuperclass(IStudentServiceImple.class);
		//设置拦截器
		enhancer.setCallback(cglibInterceptor);
		//创建代理实例
		IStudentService servcie=(IStudentService)enhancer.create();
		servcie.save();
		
		
	}

}

结果:

开启事务操作
保存学生信息
关闭事务

CGLIB原理:

生成的代理实例字节码;

 生成的代理实例继承了目标方法。方法save是重写了目标类中的save方法。在方法中调用了设置的方法拦截器(实现MethodInterceptor接口的类)中的intercept方法。在方法拦截器中的intercept方法中做了对目标方法的增强处理

方法执行关系:

 CGLIB生成的代理实例是通过继承目标类的方式生成的一个目标类的子类,在代理实例的方法中会去调用传入的方法拦截器(实现MethodInterceptor接口的类)中的intercept方法,在intercept方法中做了对目标类的增强处理。

注意:

CGLIB动态代理:它的目标类和目标方法不能final修饰,因为CGLIB动态代理主要通过继承的方式获取目标类的方法,final修饰的类和方法不能不继承。

动态代理总结:

JDK动态代理:

1.首先要自定义方法拦截,通过实现InvocationHandler接口,在invoke方法中实现对目标方法的增强处理。

2.JDK动态代理生成的代理实例, 主要通过实现目标接口的方式,并通过反射获取接口中的方法,在代理实例中的方法会调用方法拦截(实现InvocationHandler接口的类)中的invoke方法,以此达到对方法的增强

3.JDK代理的目标类必须实现接口

CGLIB动态代理:

1.自定义方法拦截,通过实现MethodInterceptor接口,在intercept方法中事项对目标方法的增强处理

2.CGLIB动态代理生成的代理实例,是通过继承目标类重写目标类中的方法,在从写的方法中会调用传入的MethodInterceptor中的intercept方法,已达到对目标方法的增强。

3.CGLIB动态代理的目标类和方法不能被final修饰,因为final修饰的类和方法不能被继承

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值