代理模式

文章目录

  • 一、静态代理
  • 二、jdk的动态代理
    • 1、实现jdk动态代理
    • 2、使用要点
      • (1)为什么jdk动态代理只能针对于接口?
      • (2)为什么调用InvocationHandler会自动执行invoke()函数?
      • (3)为什么匿名内部类使用局部变量需要定义为final?
  • 三、cglib的动态代理
    • 1、实现cglib动态代理
  • 四、参照博客

一、静态代理

1、比如买房子,首先实现通用接口,buyhouse()
2、目标类实现该接口
3、代理类实现该接口,并且传入目标类对象实例,实现buyhouse方法(调用目标类方法的基础上添加功能
( 比较简单,不过多赘述)
引用博客:https://www.cnblogs.com/daniels/p/8242592.html

代理的意义:最近在学习Spring AOP,那么就以面向切面编程作为理解的基础,面向切面编程的基础是动态代理
1、为什么面向切面编程?
是针对目标类的部分方法再扩充,但是不能改变原始目标类,因此增强类(切入类),将原始方法扩充,最终通过代理类将目标类与切入类融合(织入),用户只与代理类交互。
2、AOP使用场景:事务管理、性能监视、安全检查、缓存 、日志等
3、AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码,使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性

二、jdk的动态代理

1、实现jdk动态代理

(1)设计抽象接口类

public interface TargetInterface {
	public void add();
	public void update();
	public void delete();

}

(2)实际目标实现类

public class TargetImp implements TargetInterface {

	@Override
	public void add() {
		// TODO Auto-generated method stub
		System.out.println("jdk add");
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		System.out.println("jdk update");
	}

	@Override
	public void delete() {
		// TODO Auto-generated method stub
		System.out.println("jdk delete");
	}

}

(3)设计设计切入类(添加切入方法)

public class MyAspect {
	public void before() {
		System.out.println("前置");
	}
	public void after() {
		System.out.println("后方");
	}

}

(4)设计动态代理工厂类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//代理类
public class ProxyFactory {
	public static TargetInterface createTarget() {
		final TargetInterface target = new TargetImp();// 目标类,由于使用匿名内部类,所以定义为final,jdk1.8中可以不会报错
		final MyAspect aspect = new MyAspect();// 切面类
		/**
		 * jdk动态代理 将切面类与目标类结合-->切面(一般用来事务管理、性能监控、日志记录等)
		 */
		 /*  
		 * 	Proxy.newProxyInstance
		 * 		参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
		 * 			一般情况:当前类.class.getClassLoader();
		 * 					目标类实例.getClass().get...
		 * 		参数2:Class[] interfaces 代理类需要实现的所有接口
		 * 			方式1:目标类实例.getClass().getInterfaces()  ;注意:只能获得自己接口,不能获得父元素接口
		 * 			方式2:new Class[]{UserService.class}   
		 * 			例如:jdbc 驱动  --> DriverManager  获得接口 Connection
		 * 		参数3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
		 * 			提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
		 * 				参数31:Object proxy :代理对象
		 * 				参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
		 * 					执行方法名:method.getName()
		 * 					执行方法:method.invoke(对象,实际参数)
		 * 				参数33:Object[] args :方法实际参数
		 * 
		 */
		TargetInterface targetInterface = (TargetInterface) Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(),
				new Class[] { TargetInterface.class }, new InvocationHandler() {

					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						aspect.before();
						Object object = method.invoke(target, args);
						aspect.after();
						return object;
					}
				});
		return targetInterface;// 返回代理对象
	}

}

(5)测试

public class Test {
	@org.junit.Test
	public void test11() {
		TargetInterface target = ProxyFactory.createTarget();
		target.add();
		target.update();
		target.delete();
	}

}

2、使用要点

jdk动态代理使用前提:接口+实现类

(1)为什么jdk动态代理只能针对于接口?

动态代理生成的代理类反编译后会发现该类继承Proxy类,由于java自身的单继承性,导致了只能通过实现接口来完成动态代理

(2)为什么调用InvocationHandler会自动执行invoke()函数?

在动态代理类的定义中,构造函数是含参的构造,参数就是我们invocationHandler 实例,而每一个被代理接口的方法都会在代理类中生成一个对应的实现方法,并在实现方法中最终调用invocationHandler 的invoke方法,这就解释了为何执行代理类的方法会自动进入到我们自定义的invocationHandler的invoke方法中,然后在我们的invoke方法中再利用jdk反射的方式去调用真正的被代理类的业务方法,而且还可以在方法的前后去加一些我们自定义的逻辑。

(3)为什么匿名内部类使用局部变量需要定义为final?

a. 匿名内部类的生命周期可能比外部的类要长,因此访问外部局部变量有可能是访问不到的
b. Java语言为了实现这种特性, 只好将外部的局部变量偷偷的赋值了一份给匿名内部类。那这样匿名内部类就可以肆无忌惮的访问外部局部变量了。
c. 通过赋值的形式有一个缺陷,匿名内部类不可以修改“原来的局部变量”,因为是一份“复制品”,修改复制品对原变量没什么影响啊。
d. Java语言干脆强制要求被匿名内部类访问的外部局部变量必须是final的,什么意思呢?就是“一刀切”,不让修改了

代理模式_第1张图片

三、cglib的动态代理

采用字节码增强,没有接口只有实现类,在运行时创建目标类的子类,从而对目标类进行增强。

1、实现cglib动态代理

(1)目标类

public class TargetImp  {

	public void add() {
		// TODO Auto-generated method stub
		System.out.println(" cglib add");
	}

	public void update() {
		// TODO Auto-generated method stub
		System.out.println(" cglib update");
	}

	public void delete() {
		// TODO Auto-generated method stub
		System.out.println(" cglib delete");
	}

}

(2)切入类

public class MyAspect {
	public void before() {
		System.out.println("前置");
	}
	public void after() {
		System.out.println("后方");
	}

}

(3)代理工厂类

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;



//代理类
public class ProxyFactory {
	public static TargetImp createTarget() {
		final TargetImp target = new TargetImp();//目标类
		final MyAspect aspect = new MyAspect();//切面类
		/**
		 * CGlib 代理
		 */
		//1.代理类工厂
		Enhancer enhancer = new Enhancer();
		//2、设置父类
		enhancer.setSuperclass(TargetImp.class);
		//3.设置拦截方法
		enhancer.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				aspect.before();
				Object object = method.invoke(target, args);
				//找到代理对象的父类方法,与上边方法具有相同的作用
				methodProxy.invokeSuper(obj, args);
				aspect.after();
				return object;
			}
		});
		TargetImp proxy = (TargetImp)enhancer.create();
		return proxy;
		
	}

}

(4)测试类

public class Test {
	@org.junit.Test
	public void test11() {
		TargetImp target = ProxyFactory.createTarget();
		target.add();
		target.update();
		target.delete();
	}

}

四、参照博客

【1】jdk动态代理invoke方法:https://www.jianshu.com/p/ee9582c00eda
【2】匿名内部类使用外部变量定义为final:https://www.jianshu.com/p/609ca1c584ac

你可能感兴趣的:(设计模式)