代理模式
概念
代理(Proxy)是一种设计模式,通过代理对象去访问目标对象,不直接操作目标对象,可以增强功能。
场景
aop,拦截器,中介
组成
目标对象:被代理的对象,做最终的决定。
代理对象:目标对象的扩展,并会调用目标对象,通常引用目标对象。
用户对象:调用代理对象。
图来源:https://www.cnblogs.com/leeego-123/p/10995975.html
静态代理
在代理之前,目标对象是明确的。
// 目标接口
public interface Ilog {
void print(String s);
}
//目标类
public class MyLog implements Ilog {
@Override
public void print(String s) {
}
}
//静态代理类
public class StaticProxyMyLog implements Ilog {
private Ilog log;
public StaticProxyMyLog(Ilog log) {
this.log = log;
}
@Override
public void print(String s) {
// 方法增强 do something
log.print(s);//执行目标方法
// do something
}
//测试
public static void main(String[] args) {
Ilog target = new MyLog();//目标对象
StaticProxyMyLog proxy = new StaticProxyMyLog(target);//代理对象持有目标引用
proxy.print("--目标日志开始打印——");//执行的是增强的目标对象方法
}
}
静态代理总结
1.在不修改目标对象方法的前提下,可以对方法增强。
2.因为代理对象需要实现接口所有方法,在修改接口或者代理类较多的情况下不利于维护,所以出现了动态代理模式。
动态代理
在代理之前,目标对象是不明确的,通过接口调用。
/**
* 动态代理
*/
public class DynamicProxy {
private Ilog target;
public DynamicProxy(Ilog target) {
this.target = target;
}
public Ilog getInstace(){
ClassLoader classLoader = this.getClass().getClassLoader();
Ilog proxyLog = (Ilog)Proxy.newProxyInstance(classLoader, this.getClass().getInterfaces(),(proxy,method,args)->{
Object obj = method.invoke(args);
return obj;
});
return proxyLog;
}
//测试
public static void main(String[] args) {
// 代理对象持有目标对象引用
DynamicProxy dynamic = new DynamicProxy(new MyLog());
// 代理对象执行增强目标方法
dynamic.getInstace().print("动态代理,代理类不需要实现目标接口");
}
}
动态代理总结
1.代理对象不需要实现接口方法
2.目标对象需要实现接口,否则无法代理(cglib不需要)
3代理对象,是通过jdk的api构建了代理对象并加载到jvm中(需要指定代理的目标对象)
4.所以代理对象又被称为jdk代理,接口代理
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用Proxy#newProxyInstance方法
/*
* loader 指定当前目标对象使用类加载器,获取加载器的方法是固定的
* interfaces 目标对象实现的接口的类型,使用泛型方式确认类型
* h 事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
*/
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
Cglib
目标对象没有实现接口。目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理。
Cglib子类代理实现方法
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-x.x.x.jar
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
/**
* 目标对象,没有实现任何接口
*/
public class UserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
/**
* 测试类
*/
public class App {
@Test
public void test(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
}