深入浅出设计模式——代理模式
1.代理模式介绍
2.用代码演示代理模式
3.总结
1.代理模式介绍
定义:
1)代理模式,为代理对象提供一个替身,以控制这个对象的访问,就是通过代理对象来访问对象。这样做的好处是:可以在目标对象的基础上,再增加额外的增强操作。
2)被代理的对象可以是远程对象,创建开销大的字段和需要安全控制的字段
3)代理模式有三种不同的形式,静态代理,动态代理,和cglib代理。
描述:
代理最著名的使用场景就是人尽皆知的AOP,就拿我们很熟悉的事务注解@Transactional来说,我们知道加上了这个注解后,函数方法就会成为事务操作。
我们都知道,用sql写事务的时候, 如果我们要进行事务操作,需要写两个这样的sql语句:
start transaction;
//中间执行业务逻辑commit;
这时我们写好了中间的业务逻辑,加上注解之后,相当于把这个对象交给了一个代理对象,让代理对象执行开启事务和结束时的逻辑,我们只要编写业务逻辑就好了。同时这个方法也得到了增强,有了前置和后置方法,有了事务的效果,增强额外的功能操作。
2.用代码演示依代理模式
1)静态代理
静态代理模式的基本介绍:
静态代理在使用的时候,需要定义接口或者父类,被代理对象(目标对象)与代理对象一起实现共同的接口或者是继承相同的父类。
具体要求
1)定义一个接口ITeacherDao
2)目标对象TeacherDAO实现接口ITeacherDAO
3)定义一个代理对象TeacherProxy,也实现ITeacherDao
4)调用的时候通过调用代理对象的方法来调用目标对象
5)代理对象与目标要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。
代码展示
ITeacherDao:
public interface ITeacherDao {void teach();
//授课的方法
}
TeacherDao:
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println("老师开始上课了!");
}
}
TeacherProxy:
public class TeacherProxy implements ITeacherDao {//先把要代理的对象传进来
private ITeacherDao target = new TeacherDao();
public TeacherProxy(ITeacherDao target) {
this.target = target;
}//执行代理方法,在方法前后进行增强
@Override
public void teach() {
System.out.println("代理开始");
target.teach();
System.out.println("代理结束");
}
}
public static void main(String[] args) {
//被代理对象
ITeacherDao iTeacherDao = new TeacherDao();
//代理对象
TeacherProxy teacherProxy = new TeacherProxy(iTeacherDao);
//执行代理对象的方法
teacherProxy.teach();
}
优点:符合开闭原则,能对目标对象进行扩展。
缺点:为每一个需要增强的服务都需要创建类,容易形成类爆炸。
【深入浅出设计模式——代理模式】2)动态代理
动态代理基本介绍:
1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能使用
2)代理对象的生成,是利用jdk的api,动态的在内存中构建代理对象
3)动态代理也叫作:jdk代理,接口代理
代码实现:
ITeacherDao:
public interface ITeacherDao {void teach();
//授课的方法
}
TeacherDao:
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println("老师开始上课了!");
}
}
ProxyFactory:
public class ProxyFactory {//维护一个目标对象
private Object target;
//创建构造器对一个目标对象进行初始化
public ProxyFactory(Object target) {
this.target = target;
}public Object getProxyInstance() {
//当前对象使用的目标类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
//目标对象实现的接口类型,使用泛型的方式确认类型
Class>[] classes = target.getClass().getInterfaces();
//执行目标对象的方法时,会出发事情处理方法,会把当前执行的对象当做参数传入
InvocationHandler invocationHandler = null;
return Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强逻辑
System.out.println("JDK代理开始");
//原方法执行逻辑,执行完之后返回值
Object invoke = method.invoke(target, args);
System.out.println("JDK代理结束");
return invoke;
}
});
}
}
要注意一下Proxy.newProxyInstance() 方法有三个参数,
Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
Class>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
只需要按照上述的注释传入就可以了,用法也是十分简单。
优点:动态代理大大减少了我们的开发任务,同时减少了业务接口依赖。
缺点:始终无法摆脱interface代理的桎梏,无法实现对class的动态代理。
3)cglib代理
1)需要引入cglib的依赖
2)通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
代码展示:
TeacherDao:
public class TeacherDao {public void teach() {
System.out.println("老师开始上课了!");
}}
CglibProxyFactory:
public class CglibProxyFactory implements MethodInterceptor {
//要增强的目标类
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}public Object getProxyInstance() {
// 创建工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类对象,即代理对象
return enhancer.create();
}//重写intercept,会调用目标对象的方法
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//增强的逻辑
System.out.println("cglib开始");
//执行完毕后返回的参数
Object invoke = methodProxy.invokeSuper(obj, args);
//增强的逻辑
System.out.println("cglib结束");
//返回参数
return invoke;
}}
public static void main(String[] args) {
//目标代理对象
TeacherDao target = new TeacherDao();
//代理工厂
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(target);
//从代理工厂返回代理对象
TeacherDao proxyInstance = (TeacherDao) cglibProxyFactory.getProxyInstance();
//执行代理对象的方法
proxyInstance.teach();
}
优点:能对class进行增强,CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高。
缺点:CGLIB创建代理对象时所花费的时间比JDK多得多,而且编码不是非常方便,同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
3.总结
代理模式就是spring底层AOP的实现方法,目的就是为了把一些增强切入到业务逻辑代码里,并将这个切面统一管理。
推荐阅读
- 急于表达——往往欲速则不达
- 慢慢的美丽
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 2019-02-13——今天谈梦想()
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- Ⅴ爱阅读,亲子互动——打卡第178天
- 低头思故乡——只是因为睡不着
- 取名——兰
- 每日一话(49)——一位清华教授在朋友圈给大学生的9条建议
- 广角叙述|广角叙述 展众生群像——试析鲁迅《示众》的展示艺术