代理模式概念
- 什么是代理模式
代理模式是指为其他对象提供一种代理,达到控制这个对象的访问;简单理解,现在有一个目标对象,客户端需要用到这个目标对象,但是客户端不能直接调用目标对象,这个时候,定义一个代理对象,客户端通过代理对象,访问到目标对象;实际上代理对象和目标对象之间起的是中介作用。
- 举例说明代理模式的概念
比如现在张三想要买李四的房子,但是张三没有李四的联系方式,不能直接找李四买房子,这个时候,张三就可以找房屋中介,由中介去联系张三,在这个例子中,可以把李四看作是目标对象,房屋中介是代理对象;
静态代理
- 静态代理概念
由上面的概念可以得出,在代理模式中,需要有目标对象和代理对象,静态代理的关键在于,需要定义顶层接口,并且目标对象和代理对象都需要实现该接口,以此来实现静态代理模式。
- 实现静态代理事例
1)定义一个顶层接口
public interface StaticProxy {
//定义一个抽象方法
public void insertData();
}
2)定义一个目标类对象,目标对象需要实现上面的接口StaticProxy
public class Target implements StaticProxy{
@Override
public void insertData() {
System.out.println("目标对象正在执行新增数据的方法");
}
}
3)定义一个代理对象,根据静态代理的需求,代理对象需要和目标对象实现同一个接口
public class Agent implements StaticProxy{
//声明一个目标对象
private Target target;
//使用构造方法初始化目标对象
public Agent(Target target){
this.target = target;
}
@Override
public void insertData() {
System.out.println("代理对象,在对新增数据方法进行局部扩展");
//在这里调用目标对象中的方法
target.insertData();
}
}
4)测试静态代理方式
public class Test {
public static void main(String[] args) {
//创建目标对象
Target target = new Target();
//创建代理对象
Agent agent = new Agent(target);
//通过代理对象调用新增数据的方法insertData
agent.insertData();
}
}
5)测试结果
- 静态代理说明
代码设计:在目标对象中的insertData方法,只是输出了“目标对象正在执行新增的方法”这句话,然后在通过代理对象后,因为都实现了StaticProxy接口,因此代理对象也需要重写insertData方法,只不过这里的insertData比起目标对象中的方法,做了扩展,之后再由传入的目标对象调用insertData方法
测试方法:在测试方法的时候,需要将目标对象传递给代理对象,然后通过代理对象调用insertData方法。
测试效果:既调用了代理对象扩展后的方法,也执行了目标对象中的代码
- 静态代理作用
静态代理作用就是代理模式的作用,在不修改原目标中代码的情况下,对目标对象的已有方法进行扩展,而且还能不影响原目标对象的作用。
- 静态代理的缺点
从上述代码可以看出,静态代理要求目标对象和代理对象都需要实现同一个接口,那么对于没有实现接口的类,不能代理;再者,当原目标较为复杂,代理对象就难以操作。
JDK封装的代理模式
- JDK的代理模式概念
在Java的动态代理机制中,有两个重要的接口和类,接口是InvoactionHandler,类是Proxy;jdk的动态代理模式中,代理类都必须实现InvocationHandler这个接口,并且么个代理类的实例都关联到一个Handler,通过代理对象调用一个方法的时候,这个方法的调用会转发由InvocationHandler这个接口的invoke方法来调用。
- 代码演示动态代理
1)定义一个顶层接口DataMapper
public interface DataMapper {
//定义抽象方法新增数据
public void insertData();
}
2)定义目标对象,需要实现接口DataMapper
public class Target implements DataMapper{
@Override
public void insertData() {
System.out.println("目标对象新增数据");
}
}
3)定义代理对象,需要实现InvocationHandler接口
public class Agent implements InvocationHandler {
//声明接口
private DataMapper dataMapper;
//返回代理对象
public Object getInstance(DataMapper dataMapper) throws Exception{
this.dataMapper = dataMapper;
Class<?> clazz = dataMapper.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
/**
* 执行invoke方法
* @param proxy 代理对象
* @param method 目标对象方法
* @param args 目标对象方法的参数列表
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用前置增强
before();
//这是执行目标对象
Object obj = method.invoke(this.dataMapper,args);
//嗲用后置增强
after();
return obj;
}
//自定义
public void before(){
System.out.println("代理对象进行前置增强,比如新增数据之前先开启事务");
}
//自定义
public void after(){
System.out.println("代理对象进行后置增强,比如新增数据之后,提交事务");
}
}
4)测试类
public class Test {
public static void main(String[] args) throws Exception{
//创建目标对象
DataMapper dataMapper = (DataMapper) new Agent().getInstance(new Target());
dataMapper.insertData();
}
}
- 测试结果
- jdk动态代理说明
上面的事例中,DataMappr是顶层接口;Target是目标类;Agent类是代理类;Test作为测试类,可以从结果集中可以看出,创建的是代理对象Agent,将目标对象传递给代理对象,通过代理对象调用insertData方法,除了将目标对象中代码执行了,也将前置增强和后置增强执行了。
CGLIB动态代理
- CGLIB动态代理概念
CGLIB动态代理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术,拦截所有的父类方法的调用,
- cglib动态代理事例
1)定义一个目标类,
public class Target{
public void insertData() {
System.out.println("目标对象新增方法");
}
}
2)定义一个代理类,需要实现cglib的接口MethodInterceptor
public class Agent implements MethodInterceptor {
//获取代理对象
public Object getInstance(Class clazz) throws Exception{
//创建字节码增强器,用来对被代理的类扩展
Enhancer enhancer = new Enhancer();
//通知cglib,生成的子类需要继承那个父类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
//创建对象
Object obj = enhancer.create();
return obj;
}
/**
*
* @param obj 生成的子类对象
* @param method 被拦截的方法
* @param args 被拦截方法的参数
* @param proxy 触发父类方法的对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
//这里是调用invokeSuper()方法,不能调用invoke方法
Object object = proxy.invokeSuper(obj, args);
after();
return object;
}
//前置增强方法
public void before(){
System.out.println("前置增强方法");
}
//后置增强方法
public void after(){
System.out.println("后置增强方法");
}
}
3)测试类
public class Test {
public static void main(String[] args) throws Exception {
Target target = (Target) new Agent().getInstance(Target.class);
target.insertData();
}
}
- 测试结果
代理模式总结
- 三种代理模式的区别
1)静态代理:静态代理要求目标对象和代理对象都实现同一个接口,并且如果目标对象的上层接口代码较为复杂,则代理对象也会变得复杂
2)jdk动态代理:在静态代理的基础上,jdk的实现只需要要求目标对象必须有实现的接口即可,如果是普通类,jdk则不能完成代理
3)cglib动态代理:比起jdk动态代理,cglib代理可以代理任何类,对目标类没有特殊的要求,运用较为灵活
以上是三种代理模式的简单实现,至于原理和源码分析,本篇文章暂不做更多内容,后续再继续了解! 关注公众号:源码说