代理模式(Proxy) : 代理模式就是多一个代理类出来,替原对象进行一些操作.对方法进行加强.而代理类充当一个中介的角色.我们不直接调用原方法.而是通过代理类进行调用,中间可进行自定义处理.Spring Aop中的通知就是由此而来.代理分为静态代理,动态代理(Jdk代理),cglib代理三种,下文将进行阐述.
作者是个二吊子,如果描述有误请指出.
使用场景
我们突然接到一个需求,需要对某些具有特殊身份的数据进行验证,验证其合法性.这个时候传统操作是直接改动原有代码,增加相应处理逻辑.于是乎我们一顿操作发现有三个地方需要验证.于是脸滚键盘对着三个地方进行代码改动.一顿输出后,需求解决,测试,上线,完美闭环.但是第二天我们又接到需求,需要对普通身份的数据也进行验证.这个时候我们就发现,我们又要操作三个地方.可真是新三年,旧三年,缝缝补补又三年.长此以往我们代码的可读性越来越差,维护起来要命.于是引出了代理模式.
静态代理
uml图
抽象接口: ITeacherDao, 实现类: TeacherDao, 代理类: TeacherDaoProxy ,客户端 : Client
/**
* @Description: 接口类
* @Author: gaofan
* @Date: 2020/3/24 12:02
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public interface ITeacherDao {
public void teach();
}
/**
* @Description: 被代理类
* @Author: gaofan
* @Date: 2020/3/24 12:03
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println("王老师正在讲课");
}
}
/**
* @Description: 代理类
* @Author: gaofan
* @Date: 2020/3/24 12:03
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class TeacherDaoProxy implements ITeacherDao{
private TeacherDao teacherDao;
@Override
public void teach() {
System.out.println("打开课件");
teacherDao.teach();
System.out.println("关闭课件");
}
public TeacherDaoProxy(TeacherDao teacherDao) {
this.teacherDao = teacherDao;
}
}
/**
* @Description: TODO
* @Author: gaofan
* @Date: 2020/3/24 12:05
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class Client {
public static void main(String [] args){
TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
proxy.teach();
}
}
类型: 静态代理
原理: 实现类与代理类均实现抽象类接口,对其方法进行实现.代理类中聚合被代理类,其实现方法中通过聚合代理类调用目标方法.可以在代理类方法中加上我们需要增强的功能与处理.
缺点: 代理类需要实现接口,代码编译时已经确定了.
动态代理
通过jdk实现,核心是InvocationHandler , Proxy.newProxyInstance().代理类实现InvocationHandler invoke()方法,我们的增强方法也在这个方法中.通过Proxy.newProxyInstance()产生代理实例进行调用.
uml图
动态代理与静态代理的差别: 我们在代理类中并没有去实现接口,而是通过聚合的方式来实现.动态扩展,让类与类之间耦合度降低
/**
* @Description: 接口类
* @Author: gaofan
* @Date: 2020/3/24 12:02
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public interface ITeacherDao {
void teach();
void say();
}
/**
* @Description: 具体实现
* @Author: gaofan
* @Date: 2020/3/24 12:03
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println("王老师正在讲课");
}
@Override
public void say() {
System.out.println("王老师开始吹牛了");
}
}
/**
* @Description: 代理类
* @Author: gaofan
* @Date: 2020/3/24 12:15
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class ProxyFactory implements InvocationHandler {
private Object object;
public ProxyFactory(Object object) {
this.object = object;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass()
.getInterfaces(),this);
// return object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(!method.getName().equals("say")){
System.out.println("打开书本");
}
Object obj = method.invoke(object, args);
if(!method.getName().equals("say")){
System.out.println("关闭书本");
}
return obj;
}
}
/**
* @Description: TODO
* @Author: gaofan
* @Date: 2020/3/24 12:17
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class Client {
public static void main(String [] args){
ProxyFactory factory = new ProxyFactory(new TeacherDao());
ITeacherDao proxyInstance = (ITeacherDao)factory.getProxyInstance();
proxyInstance.say();
proxyInstance.teach();
}
}
类型: 动态代理
原理: 通过Proxy.newProxyInstance()产生实例,自定义实现InvocationHandler的invoke()方法进行增强.
缺点: 依赖接口实现,必须要有接口类
cglib代理
通过非jdk自带jar实现,能够对类进行加强,并不仅限于接口
uml图
动态代理依赖接口实现,而cglib则是直接对类下手了,内部实现应该是字节码.留着后边探索了
/**
* @Description: 被代理类
* @Author: gaofan
* @Date: 2020/3/24 12:47
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class TeacherDao {
public void teach() {
System.out.println("王老师正在讲课");
}
public void say() {
System.out.println("王老师开始吹牛了");
}
}
/**
* @Description: 代理类
* @Author: gaofan
* @Date: 2020/3/24 12:48
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class ProxyFactory implements MethodInterceptor {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance(){
// 创建工具类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(target.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 代理开始了");
Object obj = method.invoke(target, objects);
System.out.println("cglib 代理结束了");
return obj;
}
}
/**
* @Description: TODO
* @Author: gaofan
* @Date: 2020/3/24 12:48
* @Copyright: 2020 www.withu.top Inc. All rights reserved.
**/
public class Client {
public static void main(String[] args) {
TeacherDao teacherDao = new TeacherDao();
ProxyFactory factory = new ProxyFactory(teacherDao);
TeacherDao dao = (TeacherDao) factory.getProxyInstance();
dao.teach();
dao.say();
}
}
原理: 代理类实现MethodInterceptor ,实现其intercept 方法(拦截器),通过Enhancer 工具类设置父类,设置回调,创建子类对象并返回实例.客户端通过返回实例进行调用.
优点: 直接对类增强.对接口没有依赖
本文借鉴尚硅谷Java设计模式,韩顺平图解java,传送门