一 设计模式概念及分类
设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。简单来说,方便我们编程的设计模式。设计模式共有23种。分为三类:创建型模式、结构型模式和行为型模式。代理设计模式是一种结构型模式。简化我们对结构的设计。
二 代理设计模式概念理解:
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不能或者不想直接引用一个对象,此时可以通过一个“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象中间起到中介的作业。并通过代理对象对目标对象原有功能增强或减弱。
代理模式角色 | 代理类 | 目标类 | 代理类和目标类的公共接口 |
三 代理设计模式场景使用
1.当目标对象需要受到保护时,可以采用代理设计模式
2.当目标对象的目标方法需要进行功能增强时,可以采用代理设计模式
3.当多个目标对象无法直接交互时,可以才去代理设计模式
四 代理模式之动态代理
在程序运行阶段,在内存中动态生成代理类,被称为动态代理。目的为了减少代理类的数量,提高代码复用
a 动态代理之JDK动态代理
JDK动态代理只能代理接口,无法代理类,示例如下。以下代码对方法消耗时长进行统计为增强目的。
目标接口有俩个方法,登录登出
/**
* 目标接口
*/
public interface userService {
void userLogin(String uName , String password) ;
void userLoginOut() ;
}
目标接口实现类:
public class userServiceImpl implements userService {
/***
* 目标方法
* @param uName
* @param password
*/
@Override
public void userLogin(String uName, String password) {
if ("admin".equals(uName) && "132465".equals(password)){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("用户名密码正确,即将跳转首页.....");
}
}
/***
* 目标方法
*/
@Override
public void userLoginOut() {
System.out.println("即将退出登录...");
}
}
假设我们需要目标类进行userServiceImpl功能增强,则需要去创建此类的代理类来帮助我们增强原有类功能。
import java.lang.reflect.Proxy;
public class client {
/***
* JDK动态代理例子
* @param args
*Proxy.newProxyInstance()三个参数讲解:
* ClassLoader loader 要求代理对象的类加载器与目标对象的类加载器必须一致。
* Class<?>[] interfaces 目标类和代理类要实现的同一个接口或同一些接口。
* InvocationHandler h 调用处理器,是一个接口。实现增强的接口。编写增强代码增强程序
*/
public static void main(String[] args) {
//目标对象
userService target = new userServiceImpl() ;
//返回目标对象的代理对象
userService o = (userService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TimerInvocationHandler(target));
o.userLogin("admin","132465");
}
}
再进行Proxy.newProxyInstance创建时第三个参数为调用处理器,编写TimerInvocationHandler(统计消耗时长处理器)去实现InvocationHandler接口。实现接口必须实现接口中的invoke方法。invoke方法不由我们编写程序者调用,由Jdk后台自动调用。
/**
* 负责计时的调用处理器
*/
public class TimerInvocationHandler implements InvocationHandler {
private Object target ;
public TimerInvocationHandler(Object target) {
this.target = target ;
}
/***
*强制实现invoke的目的就是为了在实现的方法里实现增强的步骤。
*invoke方法并不会在Proxy.newProxyInstance时被执行,而是在代理对象调用目标方法时, invoke才会被调用。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk" + "已调用invoke...");
//前置增强
long begin = System.currentTimeMillis();
//调用目标对象的目标方法
//调用方法四要素:哪个对象,哪个方法,传什么参数,返回什么东西
//Object 返回值 = 目标方法.invoke(目标对象,参数);
Object retValue = method.invoke(target,args);
//后置增强
long last = System.currentTimeMillis();
System.out.println("耗时:"+(last-begin)+"毫秒");
return retValue ;
}
}
如果不在触发器中的invoke方法中调用Object retValue = method.invoke(target,args); 那么我们在客户端调用代理对象生成的方法(o.userLogin("admin","132465");)程序也不会调用我们的目标方法,只会执行我们的增强invoke方法。
执行结果:
b 动态代理之cglib动态代理
与上JDK动态代理方案例相同
引入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
客户端API
@Test
public void cglibTest(String[] args){
//创建字节码增强器对象
//这个对象是cglib库当中的核心对象,就是依靠他来生成代理类.
Enhancer enhancer = new Enhancer();
//指定cjlib的父类是谁?
enhancer.setSuperclass(userService.class);
//设置回调(等同于JDK动态代理当中的调用处理器(IncocationHandler))
//在cglib当中不是IncocationHandler接口,是方法拦截器接口,MethodInterceptor
enhancer.setCallback(new TimerInvocationHandlerCGLIB());
//创建代理对象
userService proxy = (userService) enhancer.create();
//调用代理对象的代理方法.
proxy.userLogin("admin","123");
}
处理器编写,增强接口
/**
* CGLIB负责计时的调用处理器
*/
public class TimerInvocationHandlerCGLIB implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//前置增强
long before = System.currentTimeMillis();
Object retValue = methodProxy.invokeSuper(target, objects);
//后置增强
long last = System.currentTimeMillis();
System.out.println("耗时:"+(last-before)+"毫秒");
return retValue;
}
}
五 二者区别:
第一来源:JDK动态代理:有原生JavaJdk提供支持。而cglib动态代理需要额外提供依赖。
第二代理形式:JDK动态代理只能去代理接口。而cglib接口和类都支持。注意, CGLIB 是通过继承的方式做的动态代理,因此如果某个 类被标记为 final ,那么它是无法使用 CGLIB 做动态代理的。
学习笔记,如上诉表达有误区,请指导