小蔡学Java

Spring知识点总结三

2024-01-01 20:27 1192 0 SSM / SpringBoot 框架篇 Spring

8、代理模式

什么是代理模式

通过代理对象访问目标对象,这样可以在目标对象的基础上增强额外的功能,如添加权限、访问控制和审计等功能 相当于中介或者律师

代理模式的作用

不修改或者没有办法修改原有代码的情况下,增强对象功能,使用代理对象代替原来的对象去完成功能进而达到拓展功能的目的

静态代理

静态代理中代理类与被代理类都需要实现同一接口,这就说明一个静态代理类只能代理一个类,并且还要事先知道我们要代理哪个类才能写代理类,如果我们有其他类还想使用代理那就必须再写一个代理类。然而在实际开发中我们是可能有非常多的类需要被代理的,并且事先我们可能并不知道我们要代理哪个类,所以如果继续使用静态代理反而会增加许多的工作量,并且效率低下,代码复用率也不高

动态代理

动态代理可以针对一些不特定的类或者一些不特定的方法进行代理,我们可以在程序运行时动态的变化代理的规则代理类在运行时才创建的代理模式称为动态代理,这种情况下,代理类并不是在Java代码中定义好的,而是在程序运行时根据我们在Java代码中的“指示”动态生成的 Proxy动态代理:JDK动态代理(面向接口) cglib动态代理:第三方动态代理(面向父类)

JDK动态代理

面向接口

代理对象和目标对象实现同样的接口,认兄弟;

  • 通过factory.getProxy()创建好了代理对象后,代理对象proxy就可以执行被代理类实现的接口的方法;当通过代理类的对象发起对被重写的方法的调用时,都会转换为对调用处理器实现类中的invoke方法的调用,invoke方法中就可以对被代理类进行功能增强并通过反射调用被代理的同名方法

1.一定要有接口和实现类的存在,

2.代理对象只能增强接口中的定义的方法

3.代理对象只能读取到接口中方法上的注解

	public class Test {
		public static void main(String[] args) {
			Dinner dinner = new Person("张三");

			ClassLoader classLoader = dinner.getClass().getClassLoader();
			Class[] interfaces = dinner.getClass().getInterfaces();
			InvocationHandler invocationHandler = new InvocationHandler() {
				@Override
				//当我们让代理对象调用任何方法时,都会触发invoke方法执行
				//(1)Object proxy,代理对象,这里是dinnerProxy
				//(2)Method method,被代理的方法,这里是eat方法或者drink方法
				//(3)Object[] args,被代理方法运行时的实参,这里是“饭”或者“茶”
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					//接收方法的返回值
					Object res = null;
					//我们这里对eat方法进行增强,对drink方法不增强
					if (method.getName().equals("eat")){
						System.out.println("饭前洗手");
						//执行原有的eat方法
						res = method.invoke(dinner,args);
						System.out.println("饭后刷牙");
					}else{
						//只用执行原本的drink方法
						res = method.invoke(dinner,args);
					}
					return res;
				}
			};

			//通过Proxy动态代理获得一个代理对象,在代理对象中,对某个方法进行增强
			//(1)ClassLoader loader,被代理对象的类加载器,这里为dinner的类加载器
			//(2)Class<?>[] interfaces,被代理对象所实现的所有接口,这里为Dinner接口
			//(3)InvocationHandler h,执行处理器对象,专门用于定义增强规则,在这里写增强的策略
			Dinner dinnerProxy = (Dinner)Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);

			dinnerProxy.eat("饭");
			dinnerProxy.drink("茶");
		}
	}

	@AllArgsConstructor
	class Person implements Dinner{
		private String name;
		@Override
		public void eat(String food) {
			System.out.println(name+"正在吃"+food);
		}
		@Override
		public void drink(String food) {
			System.out.println(name+"正在喝"+food);
		}
	}

	interface Dinner{
		void eat(String food);
		void drink(String food);
	}

cglib动态代理

面向父类

(需要被被代理类)被(代理类)继承,认义父,实际执行的是子类中的方法

  • 动态代理类重写的方法中,(Enhancer)又会去调用 MethodInterceptor接口的匿名实现类中重写的 intercept() 方法对父类方法的调用进行拦截【即:在子类中采用方法拦截的技术拦截父类所有的方法调用】,最后通过method.invoke()反射技术来调用父类中的方法

1.和接口没有直接关系

2.不仅仅可以增强接口中定义的方法还可以增强类中定义的方法

3.可以读取父类中方法上的所有注释

	public class Test2 {
		public static void main(String[] args) {
			Student student = new Student("张三");

			//1.获得一个Enhancer对象
			Enhancer enhancer = new Enhancer();
			//2.设置父类字节码
			enhancer.setSuperclass(student.getClass());
			//3.获取MethodInterceptor对象,用于定义增强规则
			MethodInterceptor methodInterceptor = new MethodInterceptor() {
				//Object o,生成之后的代理对象,就是我们接下来的studentProxy
				//Method method,父类中原本要执行的方法,也就是Student里的eat方法
				//Object[] objects,方法调用时传入的实参数组
				//MethodProxy methodProxy,子类中重写父类的方法,也就是studentProxy里的eat方法
				@Override
				public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
					//接收方法的返回值
					Object res = null;
					//我们这里对eat方法进行增强,对drink方法不增强
					if (method.getName().equals("eat")){
						System.out.println("饭前洗手");
						//执行原有的eat方法
						res = methodProxy.invokeSuper(o,objects);
						System.out.println("饭后刷牙");
					}else{
						//只用执行原本的drink方法
						res = methodProxy.invokeSuper(o,objects);
					}
					return res;
				}
			};
			//4.设置MethodInterceptor
			enhancer.setCallback(methodInterceptor);
			//4.获得代理对象
			 Student studentProxy = (Student)enhancer.create();
			//5.使用代理对象完成功能
			studentProxy.eat("饭");
			studentProxy.drink("茶");
		}
	}

	@NoArgsConstructor
	@AllArgsConstructor
	class Student {
		private String name;
		public void eat(String food){
			System.out.println(name+"正在吃"+food);
		}
		public void drink(String food){
			System.out.println(name+"正在喝"+food);
		}
	}

评论( 0 )

  • 博主 Mr Cai
  • 坐标 河南 信阳
  • 标签 Java、SpringBoot、消息中间件、Web、Code爱好者

文章目录