艺术来源于生活,程序亦是如此。
房产中介在我们的生活中广泛存在,这就是代理模式在生活中非常明显的例子;一般情况下,买房子,租房子都是找中介,在合理合法的范围内,能够降低交易过程中的各种成本。
静态代理
首先我们需要明白两个概念:代理对象和目标对象。
在买房子的过程中,目标对象就是最终的交易物:房子,代理对象就是房产中介,代理对象会帮我们达到买房的目的,同时会对买房子的过程进行相应的过程增强,比如怎么批贷款更加方便,哪家贷款的利息更加低之类的。
回到程序,在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类
举个例子来说明这个过程,打印日志是开发过程中非常常见的操作,这里我们就模拟打印日志的操作
日志接口
public interface ILogger {
//日志打印方法
void logger();
}
目标对象
public class Logger implements ILogger {
@Override
public void logger() {
System.out.println("打印日志");
}
}
代理对象
public class ProxyLogger implements ILogger {
private ILogger logger;
public ProxyLogger(Logger logger) {
this.logger = logger;
}
@Override
public void logger() {
System.out.println("查询前执行的逻辑:查看当前的参数");
logger.logger();
System.out.println("查询后执行的逻辑:保存查询的参数");
}
}
测试类
public class Test {
public static void main(String[] args) {
Logger logger = new Logger();
ProxyLogger proxyLogger = new ProxyLogger(logger);
proxyLogger.logger();
//运行结果
//查询前执行的逻辑:查看当前的参数
//打印日志
//查询后执行的逻辑:保存查询的参数
}
}
总结
静态代理的优点:可以对目标对象进行增强,不会修改原有的逻辑。
缺点:因为代理对象需要实现跟目标对象一样的接口,所以会产生很多代理类;
如果接口或者父类增加方法,目标对象和代理对象都需要维护,非常麻烦。
动态代理
怎么避免静态代理里面的缺点呢?
动态代理就可以解决这个问题!
jdk动态代理
特点
在程序运行时,通过反射机制动态生成;
代理对象不需要实现接口,但是目标对象需要实现一个接口。
代理工厂类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
//维护一个目标对象
private Object target;
//通过构造方法传入目标对象
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理方法前");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("动态代理方法后");
return returnValue;
}
}
);
}
}
日志类、接口
public interface ILogger {
//日志打印方法
void logger();
}
public class Logger implements ILogger {
@Override
public void logger() {
System.out.println("打印日志");
}
}
测试类
public class Test {
public static void main(String[] args) {
Logger logger = new Logger();
ILogger proxy = (ILogger) new ProxyFactory(logger).getProxyInstance();
proxy.logger();
//运行结果
//动态代理方法前
//打印日志
//动态代理方法后
}
}
cglib动态代理
有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截)
注意
- 代理的类不能为final,否则报错
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
代码示例
目标对象
public class Logger {
public void logger() {
System.out.println("打印日志");
}
}
代理工厂
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
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 o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("打印日志前操作");
Object result = method.invoke(target, objects);
System.out.println("打印日志后操作");
return result;
}
}
测试类
public class Test {
public static void main(String[] args) {
Logger logger = new Logger();
Logger proxyInstance = (Logger) new ProxyFactory(logger).getProxyInstance();
proxyInstance.logger();
//结果示例
//打印日志前操作
//打印日志
//打印日志后操作
}
}
总结
代码来源于生活,同时又高于生活
代理模式中比较重要的就是动态代理,jdk动态代理和cglib动态代理各有优缺点,需要自己对比去使用。
jdk动态代理需要目标对象实现一个接口,我们可以在接口里面自定义各种方法,符合面向接口编程规范。
cglib动态代理需要引入三方jar包,有点就是不需要实现接口,而是动态的为目标对象生成一个子类,再对子类做方法增强,因为是继承,所以目标对象的方法不能被final修饰,也不能被static修饰,不然不能做方法增强。
从效率上来说,当调用次数少的时候,也就是大部分情况下,jdk动态代理的性能优于cglib动态代理。