代理模式
一、静态代理
1.1 代理的三要素
a、有共同的⾏为(结婚) - 接⼝
b、⽬标⻆⾊(新⼈) - 实现⾏为
c、代理⻆⾊(婚庆公司) - 实现⾏为 增强⽬标对象⾏为
1.2 静态代理的特点
1、⽬标⻆⾊固定
2、在应⽤程序执⾏前就得到⽬标⻆⾊
3、代理对象会增强⽬标对象的⾏为
4、有可能存在多个代理 引起"类爆炸"(缺点)
1.3 静态代理的实现
1.3.1 定义行为(共同) 定义接口
/**
* a、有共同的行为(结婚) - 接口
* b、目标角色(新人) - 实现行为
* c、代理角色(婚庆公司) - 实现行为 增强目标对象行为
*
* 定义行为
*/
public interface Marry {
public void toMarry();
}
1.3.2 目标对象(实现行为)
/**
* 静态代理 ——> 目标对象
*/
public class You implements Marry{
// 实现行为
@Override
public void toMarry() {
System.out.println("我要结婚了!");
}
}
1.3.3 代理对象(实现行为、增强目标对象的行为)
/**
* 静态代理 ——> 代理对象
*/
public class MarryCompany implements Marry{
// 目标对象
//private You you; 这里用Marry接口更好,万一是其他人结婚,这里写you的话,就又要换成别人。
private Marry marry;
/* public MarryCompany(You you) {
this.you = you;
}*/
public MarryCompany(Marry marry) {
this.marry = marry;
}
@Override
public void toMarry() {
// 增强行为
before();
//执行目标对象中的方法
marry.toMarry();
//增强行为
after();
}
/**
* 增强行为
*/
private void after() {
System.out.println("新婚快乐,早生贵子!");
}
/**
* 增强行为
*/
private void before() {
System.out.println("场地正在布置中...");
}
}
1.3.4 通过代理对象实现目标对象的功能
public class Test {
public static void main(String[] args) {
// 目标对象
You you = new You();
// 构造代理角色同时传入真实角色
MarryCompany marryCompany = new MarryCompany(you);
// 通过代理对象调用目标对象中的方法
marryCompany.toMarry();
}
}
二、动态代理
2.1 动态代理的特点
- ⽬标对象不固定
- 在应⽤程序执⾏时动态创建⽬标对象
- 代理对象会增强⽬标对象的⾏为
2.2 JDK动态代理
注:JDK动态代理的⽬标对象必须有接⼝实现
2.2.1 准备目标对象
Marry接口
public interface Marry {
public void toMarry();
}
RentHouse接口
public interface RentHouse {
public void toRentHouse();
}
接口的实现类User
public class User implements Marry,RentHouse{
@Override
public void toMarry() {
System.out.println("我要结婚了!");
}
@Override
public void toRentHouse() {
System.out.println("我要租房子!");
}
}
没有接口实现类的目标对象User2
public class User2 {
public void test(){
System.out.println("测试JDK动态代理...");
}
}
2.2.2 获取代理对象
1.定义一个抽象的目标角色,作为私有的成员变量。
2.通过带参构造,传递目标对象
3.定义一个方法,得到代理对象。而得到代理对象需要三个参数(类加载器,标对象实现的接口数组,InvocationHandler接口)。
4.InvocationHandler接口的实现:用匿名内部类,new一个InvocationHandler接口。调用目标对象中的方法 invoke (反射的方法)Object object = method.invoke(target, args);
5.最终返回代理 return proxy;
/**
* JDK动态代理
* 通过Proxy类中newProxyInstance,返回一个代理对象
*
* 注:使用JDK动态代理,需要要有接口实现
*/
public class JdkProxy {
/**
* 目标角色 没有具体的目标角色,目标角色不固定,
* 到时候传进来什么,目标角色就是什么,
* 比如:结婚啊,租房啊,打官司啊。。。
*/
private Object target;
// 通过带参构造,传递目标对象
public JdkProxy(Object target) {
this.target = target;
}
/**
* 得到代理对象
* newProxyInstance 返回一个指定接口的代理类的实例方法调用分派到指定的调用处理程序。 (返回代理对象)
* loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
* interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
* h:一个InvocationHandler接口,表示代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法(传入InvocationHandler接口的子类)
* @return
*/
public Object getProxy(){
//得到类加载器
ClassLoader classLoader = this.getClass().getClassLoader();
//得到目标对象实现的接口数组
Class[] interfaces = target.getClass().getInterfaces();
//用匿名内部类,new一个InvocationHandler接口
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* 当代理实例中对应的方法被调用时,invoke方法就会执行一次
* 1、调用目标对象的方法(返回Object)
* 2、增强目标对象的行为
* @param proxy 调用该实例的代理对象
* @param method 目标对象的方法
* @param args 目标对象的方法的形参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("invoke...");
// 目标对象的方法名
// System.out.println(method.getName());
// 目标对象的形参
// System.out.println(args);
//目标增强
System.out.println("方法执行前...");
// 调用目标对象中的方法 invoke (反射的方法)
Object object = method.invoke(target, args);
//方法执行后
System.out.println("方法执行后...");
// 方法的返回值 (目标方法的返回值)
return object;
}
};
// 得到代理对象(代理实例)
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
//返回代理
return proxy;
}
}
2.2.3 通过代理对象实现目标对象的功能
public class Test {
public static void main(String[] args) {
//目标对象
RentHouse target = new User();
// 获取代理类
JdkProxy jdkProxy = new JdkProxy(target);
// 调用代理类中的方法,得到代理对象
RentHouse rentHouse = (RentHouse) jdkProxy.getProxy();
//调用方法
rentHouse.toRentHouse();
Marry target2 = new User();
JdkProxy jdkProxy1 = new JdkProxy(target2);
Marry marry = (Marry) jdkProxy.getProxy();
marry.toMarry();
// 没有接口实现类的目标对象
/*User2 user2 = new User2();
JdkProxy jdkProxy2 = new JdkProxy(user2);
User2 proxy = (User2)jdkProxy2.getProxy();
proxy.test();*/ //报错了
}
}
注:JDK的动态代理依靠接⼝实现,如果有些类并没有接⼝实现,则不能使⽤JDK代理。
2.3 CGLIB 动态代理
cglib是针对类来实现代理的,它的原理是对指定的⽬标类⽣成⼀个⼦类,并覆盖其中⽅法
实现增强,但因为采⽤的是继承,所以不能对final修饰的类进⾏代理。
2.3.1 添加依赖
在pom.xml⽂件中引⼊cglib的相关依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
2.3.2 准备目标对象
RentHouse接口:
public interface RentHouse {
public void toRentHouse();
}
User实现类:
public class User implements Marry,RentHouse{
@Override
public void toMarry() {
System.out.println("我要结婚了!");
}
@Override
public void toRentHouse() {
System.out.println("我要租房子!");
}
}
没有继承接口的实现类User2
public class User2 {
public void test(){
System.out.println("测试JDK动态代理...");
}
}
2.3.3 获取代理对象
1.定义一个抽象的目标角色,作为私有的成员变量。
2.通过带参构造,传递目标对象
3.得到Enhancer对象
4.设置父类 (将目标对象设置为当前生成类的父类)
enhancer.setSuperclass(target.getClass());
5.获取拦截器 通过匿名内部类 new MethodInterceptor,重写未实现方法。
调用目标对象的方法
Object object = methodProxy.invoke(target, objects);
6.设置拦截器
enhancer.setCallback(methodInterceptor);
7.通过调用Enhancer的create()方法,生成一个类(代理对象)
/**
* CGLIB动态代理
* 采用是继承思想,生成一个子类继承目标对象。(目标类不能用final修饰)
*
*/
public class CglibProxy {
//目标对象
private Object target;
// 通过带参构造,传递目标对象
public CglibProxy(Object target) {
this.target = target;
}
/**
* 得到代理对象
* @return
*/
public Object getProxy(){
// 得到Enhancer对象,通过调用Enhancer的create()方法,生成一个类(代理对象)
Enhancer enhancer = new Enhancer();
// 设置父类 (将目标对象设置为当前生成类的父类)
enhancer.setSuperclass(target.getClass());
// 获取拦截器 通过匿名内部类 new MethodInterceptor
MethodInterceptor methodInterceptor = new MethodInterceptor() {
/**
* 每当代理实例中的方法被调用时,intercept方法就会执行
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//System.out.println("intercept...");
// 增强行为
System.out.println("方法执行前");
//调用目标对象的方法
Object object = methodProxy.invoke(target, objects);
// 增强行为
System.out.println("方法执行后...");
return object;
}
};
//设置拦截器
enhancer.setCallback(methodInterceptor);
//返回生成的代理类
return enhancer.create();
}
}
2.3.4 调用方法
public class Test {
public static void main(String[] args) {
//目标对象
RentHouse target = new User();
//得到代理类
CglibProxy cglibProxy = new CglibProxy(target);
// 得到目标对象的代理对象
RentHouse proxy = (RentHouse)cglibProxy.getProxy();
// 通过代理对象调用目标对象的方法
proxy.toRentHouse();
// 没有接口实现类的目标对象
User2 user2 = new User2();
CglibProxy cglibProxy2 = new CglibProxy(user2);
User2 proxy2 = (User2) cglibProxy2.getProxy();
proxy2.test();
}
}
2.4 JDK代理与CGLIB代理的区别
- JDK动态代理实现接⼝,Cglib动态代理继承思想
- JDK动态代理(⽬标对象存在接⼝时)执⾏效率⾼于Ciglib
- 如果⽬标对象有接⼝实现,选择JDK代理,如果没有接⼝实现选择Cglib代理