1. 代理模式的概述
1) 定义: 给某一个对象提供一个代理,并由代理对象控制对原对象的引用
2) 结构图:
3) 图中包含的角色:
1. Subject(抽象主题角色): 声明了真实主题和代理主题的共同接口,使得在任何使用真实主题的地方都可以使用代理主题,客户端针对抽象主题编程
2. Proxy(代理主题角色): 代理主题内部维持了一个真实主题的引用;代理主题角色负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束,以便客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作;
3. RealSubject(真实主题角色): 实现了真实业务操作;
2. 案例
1) 需求:
2) 解决方案
3) 代码实现:
package com.zach.proxy;
/**
* 抽象查询类
*
* @author Zach
* @date 2018年7月12日
* @title Searcher
*/
public interface Searcher {
public String doSearch(String userId,String keywork);
}
/**
* 真实搜索类
*
* @author Zach
* @date 2018年7月12日
* @title RealSearcher
*/
public class RealSearcher implements Searcher {
@Override
public String doSearch(String userId, String keywork) {
return "搜索的用户id为"+userId+", 关键词是:"+keywork;
}
}
/**
* 搜索代理类
*
* @author Zach
* @date 2018年7月12日
* @title ProxySerarcher
*/
public class ProxySerarcher implements Searcher {
private RealSearcher realSearcher = new RealSearcher();
private AccessValidator av = new AccessValidator();
private Logger logger = new Logger();
@Override
public String doSearch(String userId, String keywork) {
String result = null;
if (av.validate(userId)) {
System.out.println("验证通过");
result = realSearcher.doSearch(userId, keywork);
} else {
System.out.println("验证失败");
result = "未查询到结果";
}
logger.log(userId);
return result;
}
}
/**
* 客户端类
*
* @author Zach
* @date 2018年7月12日
* @title Client
*/
public class Client {
public static void main(String[] args) {
try {
Searcher searcher = (Searcher) Class.forName("com.zach.pattern.proxy.ProxySerarcher").newInstance();
String doSearch = searcher.doSearch("泷泽萝拉", "雅美蝶");
System.out.println(doSearch);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 验证类
*
* @author Zach
* @date 2018年7月12日
* @title AccessValidator
*/
public class AccessValidator {
private static final List<String> list = (List<String>) Arrays.asList("吉泽明步","波多野结衣","泷泽萝拉");
public boolean validate(String userId){
System.out.println("开始验证用户的id为"+userId+"是否为合法用户");
return list.contains(userId) ? true : false;
}
}
/**
* 日志记录类
*
* @author Zach
* @date 2018年7月12日
* @title Logger
*/
public class Logger {
public void log(String userId) {
System.out.println("用户id为:"+userId+", 完成了一次查询");
}
}
3. jdk动态代理
以上代理模式的特点是Proxy类通过编译器编译成class文件,当系统运行时,此class已经存在了,这种代理模式统称为静态代理模式,但是大量使用静态代理,会使我们系统内的类的规模增大,并且不易维护;而动态代理可以在运行的时候,在需要代理的地方,根据Subject和RealSubject,动态地创建一个Proxy,用完就销毁,避免了Proxy角色的calss在系统中的冗余,且可以实现对多个委托类的统一代理和集中控制
1) Proxy:运行时动态生成的代理类:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 参数说明: ClassLoader loader:类加载器 Class<?>[] interfaces: 得到全部接口 InvocationHandler h:得到InvocationHandler接口子类实例,这个类在业务委托类执行时,会先调用invoke方法,invoke方法再执行相应的代理操作 Interface: 对于JDK proxy,业务类是需要一个Interface的;这是不足之处 Method: 对于业务委托类的每个方法 |
2) InvocationHandler作用:
用于处理方法功能的实现,外界对Proxy角色中的每一个方法的调用,Proxy的角色都会交给Invocation来处理,而InvocationHandler则调用具体角色方法
3) 动态代理的实现
/**
* 接口
*
* @author Zach
* @date 2018年7月13日
* @title BookFacade
*/
public interface BookFacade {
public void addBook();
public void deleteBook();
}
/**
* 实际业务类
*
* @author Zach
* @date 2018年7月13日
* @title BookFacadeImpl
*/
public class BookFacadeImpl implements BookFacade {
@Override
public void addBook() {
System.out.println("add book logic is running...");
}
@Override
public void deleteBook() {
System.out.println("delete book logic is running...");
}
}
/**
* 动态代理类
*
* @author Zach
* @date 2018年7月13日
* @title BookFacadeProxy
*/
public class BookFacadeProxy implements InvocationHandler {
private Object target;
public Object bind(Object target){
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("Proxy start...");
System.out.println("method name: "+method.getName());
result = method.invoke(target, args);
System.out.println("Proxy end...");
return result;
}
}
/**
* 测试类
*
* @author Zach
* @date 2018年7月13日
* @title ProxyTest
*/
public class ProxyTest {
public static void main(String[] args) {
BookFacadeProxy proxy = new BookFacadeProxy();
BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
System.out.println("=============================");
bookProxy.deleteBook();
}
}
4. cglib子类代理
cglib采用字节码技术,通过目标类的字节码为一个类创建子类,并在子类中采用方法拦截技术拦截所有的父类的方法的调用,顺势织入横切逻辑;底层使用字节码处理框架ASM,来转换字节码并生成新的类;
代理类将目标类作为自己的父类并为其中的每个非final委托方法创建两个方法
a. 一个与目标方法签名相同的方法,它在方法中会通过super调用目标方法;
b. 另一个是代理类独有的方法,称之为Callback回调方法,它会判断这个方法是否绑定了拦截器(实现了MethodIntercept接口对象),若存在则将调用Intercept方法对目标方法进行代理,就是在前后加上一些增强逻辑,intercept中就会调用签名相同的方法.
1) cglib的实现
/**
* 被代理类,没有实现接口
*
* @author Zach
* @date 2018年7月13日
* @title BookFacadeImpll
*/
public class BookFacadeImpll {
public void addBook(){
System.out.println("增加图书的普通方法");
}
}
/**
* 使用cglib代理
*
* @author Zach
* @date 2018年7月13日
* @title BookFacadeCglib
*/
public class BookFacadeCglib implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
//回调方法
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
/**
* 回调方法
* obj 代理对象
* arg1 委托方法
* args 方法参数
* proxy 代理方法的MethodProxy对象
*/
@Override
public Object intercept(Object obj, Method arg1, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("事物开始");
proxy.invokeSuper(obj, args);
System.out.println("事物结束");
return null;
}
}
public class CglibTest {
public static void main(String[] args) {
BookFacadeCglib cglib = new BookFacadeCglib();
BookFacadeImpll bf = (BookFacadeImpll) cglib.getInstance(new BookFacadeImpll());
bf.addBook();
}
}
5. 对不同方法执行不同的回调逻辑
1) JDK动态代理的方式
通过InvocationHandler接口方法的调用对代理内的所有的方法都有效,我们可对Method的Name进行判断,然后针对不同的Method编写不同的逻辑;如:
class MyInvocationHandler implements InvocationHandler {
//具体的调用类
Object target;
public MyInvocationHandler(Object obj) {
target = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//当前所持有的proxy对象
//method表示当前别调用的方法
//args表示方法中传递的参数
System.out.println("method name:"+method.getName());
if(method.getName().equals("add")){
if(args[0].equals("apple")){
return false;
}
}
return method.invoke(target, args);
}
}
2) CGLib的方式
- 回调数组Callback[]:不同的Method的调用可以对应不同的回调函数。在动态代理中,回调函数就是拦截器需要执行的函数,所以我们说回调函数,可以约等于拦截器。拦截器通过实现
MethodInterceptor
接口定义。 - 回调过滤器CallbackFilter:
int accept(Method method)
返回的值为数字代表了Callback数组中的索引位置,即Method对应的Callback。
定义是吸纳过滤器CallbackFilter接口的类
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
public class TargetMethodCallbackFilter implements CallbackFilter {
/**
* 过滤方法
* 返回的值为数字,代表了Callback数组中的索引位置,要到用的Callback
*/
@Override
public int accept(Method method) {
if(method.getName().equals("method1")){
System.out.println("filter method1 ==0");
return 0;
}
if(method.getName().equals("method2")){
System.out.println("filter method2 ==1");
return 1;
}
if(method.getName().equals("method3")){
System.out.println("filter method3 ==2");
return 2;
}
return 0;
}
}
package com.zach.proxy.cglib;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import java.lang.annotation.Target;
/**
* @Classname TestCglib
* @Description: (1)MethodInterceptor:方法拦截器
* <p>
* (2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。
* <p>
* (3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
* @Date 2020/3/19 21:50
* @Created by Zach
*/
public class TestCglib {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetObject.class);
CallbackFilter callbackFilter = new TargetMethodCallbackFilter();
Callback noopCb = NoOp.INSTANCE;
Callback callback = new TargetInterceptor();
Callback fixedValue = new TargetResultFixed();
Callback[] callbacks = new Callback[]{callback, noopCb, fixedValue};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(callbackFilter);
TargetObject targetObject = (TargetObject) enhancer.create();
System.out.println(targetObject.method1(0));
System.out.println(targetObject.method2(1));
System.out.println(targetObject.method3(2));
}
}
import net.sf.cglib.proxy.FixedValue;
public class TargetResultFixed implements FixedValue{
/**
* 该类实现FixedValue接口,同时锁定回调值为999
* (整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。
*/
@Override
public Object loadObject() throws Exception {
System.out.println("锁定结果");
Object obj = 999;
return obj;
}
}
/**
* @Classname TargetObject
* @Description: 需要CGLIB动态代理的目标
* @Date 2020/3/19 22:13
* @Created by Zach
*/
public class TargetObject {
public int method1(int count) {
return count;
}
public int method2(int count) {
return count;
}
public int method3(int count) {
return count;
}
}
6. JDK动态代理与CGLib代理的区别:
1. JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2. JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3. JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
7. 参考资料: