为什么一把年纪的我还在坚持学习?因为贫穷。
1 代理
Java 中的代理和生活的代理是很相似的,毕竟软件是用来描述生活的。好比租房子,可以自己找房源,也可以通过中介找。
2 静态代理
每种业务对应一个接口,每个接口对应一个实实在在存在的代理类
(1)IRentingHouse 租房接口
/**
* 接口:租房
* 接口用来定义要做什么事
*/
public interface IRentingHouse {
void rentHosue();
}
(2)RentingHouseImpl
/**
* 自己找房源就要实现这个接口
*/
public class RentingHouseImpl implements IRentingHouse {
@Override
public void rentHosue() {
System.out.println("我要租用一室一厅的房子");
}
}
(3)租房代理类 RentingHouseProxy
/**
* 中介去实现接口
* 租房接口对应存在一个的租房代理类,
* 租车接口对应存在一个租车的代理类,
* 每种业务都对应一个代理类,代理类里写增强逻辑,使用的时候都要去new一个代理对象,
* 称为静态代理
*/
public class RentingHouseProxy implements IRentingHouse {
private IRentingHouse rentingHouse;
public RentingHouseProxy(IRentingHouse rentingHouse) {
this.rentingHouse = rentingHouse;
}
@Override
public void rentHosue() {
// 除了原接口的租房方法,还增强了功能,租房前后都可以干点别的事情
System.out.println("中介(代理)收取服务费3000元");
rentingHouse.rentHosue();
System.out.println("客户信息卖了3毛钱");
}
}
(4)测试
public class Test {
public static void main(String[] args) {
// 明确自己的需求
IRentingHouse rentingHouse = new RentingHouseImpl();
// 自己去租用一个一室一厅的房子
// rentingHouse.rentHosue();
// 带着需求去找中介
RentingHouseProxy rentingHouseProxy = new RentingHouseProxy(rentingHouse);
// 抛头露面的是代理对象了,而不是自己了
rentingHouseProxy.rentHosue();
}
}
3 动态代理
不再需要为每个业务创建代理类
3.1 公共部分
(1)IRentingHouse 接口
/**
* 接口:租房
* jdk动态代理/cglib动态代理
*/
public interface IRentingHouse {
void rentHosue();
}
(2)RentingHouseImpl 接口实现类
/**
* 委托方(委托对象)
*/
public class RentingHouseImpl implements IRentingHouse {
@Override
public void rentHosue() {
System.out.println("我要租用一室一厅的房子");
}
}
3.2 JDK 动态代理
要求委托对象必须实现接口,因为创建Jdk代理对象需要传参:委托对象的接口
public class JdkProxy {
public static void main(String[] args) {
IRentingHouse rentingHouse = new RentingHouseImpl(); // 委托对象---委托方
// 通过 JDK 动态代理生成代理对象,需要提供委托方对象,委托方想要执行的方法,方法的参数
IRentingHouse jdkProxy = (IRentingHouse) Proxy.newProxyInstance(rentingHouse.getClass().getClassLoader(), rentingHouse.getClass().getInterfaces(),
(proxy, method, args1) -> {
// 增强逻辑
System.out.println("中介(代理)收取服务费3000元");
// 调用原有业务逻辑
Object result = method.invoke(rentingHouse);
System.out.println("客户信息卖了3毛钱");
return result;
});
// 从代理对象工厂获取代理对象
//IRentingHouse jdkProxy = (IRentingHouse) ProxyFactory.getInstance().getJdkProxy(rentingHouse);
// 帮委托方执行方法
jdkProxy.rentHosue();
}
}
代理看似没省代码?只有一个业务,确实没省。如果是上千百万个业务,那就棒棒的了。
3.3 Cglib 动态代理
需要引入 cglib 依赖
<!--引入cglib依赖-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
不要求委托对象必须实现接口,因为创建Cglib代理对象时不需要用到接口
public class CglibProxy {
public static void main(String[] args) {
// 委托对象
IRentingHouse rentingHouse = new RentingHouseImpl();
// 获取rentingHouse对象的代理对象,
// Enhancer类似于JDK动态代理中的Proxy,Enhancer 增强器
// 通过实现接口MethodInterceptor能够对各个方法进行拦截增强,类似于JDK动态代理中的InvocationHandler
IRentingHouse cglibProxy = (IRentingHouse) Enhancer.create(rentingHouse.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> {
Object result;
System.out.println("中介(代理)收取服务费3000元");
result = method.invoke(rentingHouse, objects);
System.out.println("客户信息卖了3毛钱");
return result;
});
// 使用工厂来获取代理对象
// IRentingHouse cglibProxy = (RentingHouseImpl) ProxyFactory.getInstance().getCglibProxy(rentingHouse);
cglibProxy.rentHosue();
}
3.4 通过工厂生成代理对象
/**
* 代理对象工厂:生成代理对象的
*/
public class ProxyFactory {
// 构造函数私有化
private ProxyFactory(){}
// 创建静态工厂对象
private static ProxyFactory proxyFactory = new ProxyFactory();
public static ProxyFactory getInstance() {
return proxyFactory;
}
/**
* Jdk动态代理
* @param obj 委托对象
* @return 代理对象
*/
public Object getJdkProxy(Object obj) {
// 获取代理对像,第 3 个参数为 InvocationHandler接口,采用了匿名内部类去实例化
// 第 2 个参数为委托对象的接口,所以委托对象必须实现接口,这是其与cglib的区别之一
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
(proxy, method, args) -> {
Object result;
// 写增强逻辑
System.out.println("中介(代理)收取服务费3000元");
// 调用原有业务逻辑
result = method.invoke(obj,args);
System.out.println("客户信息卖了3毛钱");
return result;
});
}
/**
* 使用cglib动态代理生成代理对象
* @param obj 委托对象
* @return 代理对象
*/
public Object getCglibProxy(Object obj) {
// 参数只需获取委托对象的class,不需要委托对象实现接口
return Enhancer.create(obj.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> {
Object result;
System.out.println("中介(代理)收取服务费3000元");
result = method.invoke(obj,objects);
System.out.println("客户信息卖了3毛钱");
return result;
});
}
}
4 代理模式在 MyBatis 中的体现
代理模式是 MyBatis 的核心使用模式,正式由于这个模式,我们只需编写 Mapper.java 接口,不需要编写实现类,直接由 MyBatis 后台帮我们完成具体的 SQL 的执行。
当我们使用 Configuration 的 getMapper 方法时,会调用 mapperRegistry.getMapper
5 代理模式在事务中的使用
方法内部调用的方法的事务会失效,与动态代理有关