面试中经常会被问到代理的问题,今天我们说说代理,后期在并发文章中可能会被用到,这里先做一个铺垫
代理
代理就是设置一个中间代理来控制访问原目标对象,以达到增强原对象和简化访问的方式
分类
静态代理:由程序员创建或工具生成的代理类的源码,在编译代理类,即代理和委托类的关系在程序运行前就已经存在了
动态代理:运行期间使用动态生成字节码形式,动态创建代理类,使用工具JDK代理,CGLIB代理
静态代理
代理和被代理类实现同一个接口,在代理类的构造函数定义一个代理类的对象
EatServer类
public interface EatServer {
//吃饭
public void eat();
}
被代理类 EatServerImp,实现EatServer
public class EatServerImp implements EatServer {
@Override
public void eat() {
System.out.println("下班了要进行吃饭了");
}
}
代理类EatServerProxy,同样实现EatServer,且引入被代理类EatServer
public class EatServerProxy implements EatServer {
private EatServer eatServer;
public EatServerProxy(EatServer eatServer) {
this.eatServer = eatServer;
}
@Override
public void eat() {
System.out.println("吃饭前洗手");
eatServer.eat();
System.out.println("吃饭后洗完");
}
}
public static void main(String[] args) {
EatServer eatServer = new EatServerImp();
EatServerProxy eatServerProxy = new EatServerProxy(eatServer);
eatServerProxy.eat();
}
运行结果
吃饭前洗手
下班了要进行吃饭了
吃饭后洗碗
JDK动态代理
使用JDK API,动态代理不需要实现接口,但是目标对象必须实现接口
目标对象EatServerImp,实现EatServer
public class EatServerImp implements EatServer {
@Override
public void eat() {
System.out.println("下班了要进行吃饭了");
}
}
动态代理对象EatServerJDKProxy,实现InvocationHandler接口
public class EatServerJDKProxy implements InvocationHandler {
//目标对象
private Object target;
public EatServerJDKProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("吃饭前洗手");
//通过反射调用,目标对象的方法
method.invoke(target, args);
System.out.println("吃饭后洗碗");
return null;
}
//获取代理对象
public Object getProxyInstance() {
//第一个参数,目标对象的类加载器
//第二参数,目标对象实现的接口
//第三个参数,就是代理对象,每次需要调用目标对象的方法时候
//就需要使用代理对象的invoke方法调用
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new EatServerJDKProxy(target));
}
}
public static void main(String[] args) {
EatServer eatServer = new EatServerImp();
EatServerJDKProxy jdkProxy = new EatServerJDKProxy(eatServer);
EatServer proxyInstance = (EatServer)jdkProxy.getProxyInstance();
proxyInstance.eat();
}
吃饭前洗手
下班了要进行吃饭了
吃饭后洗碗
CGLIB动态代理
如果目标对象没有实现接口的类,就可以使用CGLIB代理,CGLIB是一个强大的高性能的代码生成包,他可以在运行期扩展java类与实现java的接口,底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.
目标对象EatServerImp,不用实现接口
public class EatServerImp {
public void eat() {
System.out.println("下班了要进行吃饭了");
}
}
代理对象,实现MethodInterceptor
public class EatServerCGLIBProxy implements MethodInterceptor {
//目标对象
private Object target;
public EatServerCGLIBProxy(Object target) {
this.target = target;
}
//为目标对象生成代理类
public Object getTargetInstance(){
//工具
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("吃饭前洗手");
method.invoke(target,objects);
System.out.println("吃饭后洗碗");
return null;
}
}
public static void main(String[] args) {
EatServerCGLIBProxy cglibProxy = new EatServerCGLIBProxy(new EatServerImp());
EatServerImp eatServer1 = (EatServerImp)cglibProxy.getTargetInstance();
eatServer1.eat();
}
吃饭前洗手
下班了要进行吃饭了
吃饭后洗碗
总结