什么是代理
代理就是通过代理对象去访问实际的目标对象,比如买火车票,我们可以去火车站买票,也可以从代售点买票,通过代售点的方式就是代理。在java中,代售点就是就是代理类,不仅可以实现目标对象,还可以增加一些额外的功能。据我所知java中的代理方式有两种,一种是静态代理,一种是动态代理。
什么是静态代理
静态代理相当于是多写了一个代理类,在调用的时候调用的是代理类,在代理类中的处理还是原生的处理逻辑,不过在前后添加上需要添加的代码。
缺点
因为静态代理在代码运行之前就已经存在代理类了,所以每一个代理对象都需要去建一个代理类去代理,当需要代理的对象有很多时,就需要创建很多的代理类,降低了程序可维护性。所以有了动态代理。
特点
- 代理角色和真实角色都需要实现同一个接口
- 真实角色专注于自己的事情
- 代理角色目的就是帮助真实角色完成一件事情
- 多线程的实现方式2:实现一个接口Runnable使用的就是"静态代理"的思想
买火车票为例子,在代码中先创建一个通用的买票接口
代码语言:javascript复制/**
* 卖票接口
*
*/
public interface SellTickets {
/**
* 卖票方法
*/
void sell();
}
然后需要一个真实的类
代码语言:javascript复制/**
* 火车站 具有卖票功能,所以需要实现SellTickets接口
*
*/
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票......");
}
}
最后需要一个代理类
代码语言:javascript复制/**
* 代售点
*
*/
public class ProxyPoint implements SellTickets {
private TrainStation station = new TrainStation();
@Override
public void sell() {
System.out.println("代售点收取一些费用......");
station.sell();
}
}
代理类可以在不更改被代理对象的情况下去增加功能
代码语言:javascript复制public class Client {
public static void main(String[] args) {
//创建代售点对象
ProxyPoint proxyPoint = new ProxyPoint();
//调用方法进行买票
proxyPoint.sell();
}
}
/**
代售点收取一些费用......
火车站卖票......
**/
什么是动态代理
动态代理指代理类不写在代码中,而是在运行过程中产生,java提供了两种动态代理,分别是jdk的动态代理和基于Cglib的动态代理。
JDK动态代理
要想用jdk动态代理,可以先写一个ProxyHandler类,这个类实现了InvocationHandler接口,并重写里面的invoke方法,invoke方法里有被代理对象的需要被增强的方法,到这里只是定义了被代理类的通用增强方法,要想真正使用的话,需要通过Proxy.newProxyInstance,它的目的是在运行期间生成代理类,它的参数是类加载器,目标类实现的接口,InvocationHandler对象,要注意的是Proxy.newProxyInstance的返回值需要用被代理类所实现的接口来接,接住的就是代理对象,如果代理对象中的方法被调用,就会调用InvocationHandler实现类中的invoke方法,这里面有需要增强的内容。
通用接口
代码语言:javascript复制/**
* 卖票接口
*
*/
public interface SellTickets {
/**
* 卖票方法
*/
void sell();
}
具体类
代码语言:javascript复制/**
* 火车站 具有卖票功能,所以需要实现SellTickets接口
*
*/
public class TrainStation implements SellTickets {
@Override
public String sell() {
System.out.println("火车站卖票......");
return "我正在被打印";
}
}
ProxyHandler类
代码语言:javascript复制public class ProxyHandler implements InvocationHandler {
Object object;
public ProxyHandler(Object object) {
this.object = object;
}
/**
* @param proxy 代理对象
* @param method 要实现的方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("注册账号......");
Object invoke = method.invoke(object, args);
return invoke;
}
}
代码语言:javascript复制public class ProxyTest {
public static void main(String[] args) {
TrainStation trainStation = new TrainStation();
InvocationHandler proxyHandler = new ProxyHandler(trainStation);
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(trainStation.getClass().getClassLoader()
, trainStation.getClass().getInterfaces()
, proxyHandler);
String sell = sellTickets.sell();
System.out.println(sell);
}
}
/**
注册账号......
火车站卖票......
**/
动态代理的优势
静态代理的话需要对每一个被代理对象都创建一个代理对象,因为静态代理是在项目运行前就写好的。但是动态代理不是,动态代理是在运行期间才创建代理类,所以只需要创建一个动态代理类就可以。比如我在写一个被代理的对象招聘员工。
首先先写一个通用接口
代码语言:javascript复制/**
* 招聘接口
*
*/
public interface Recruit {
/**
* 招聘方法
*/
void find();
}
再写一个被代理对象的类
代码语言:javascript复制public class TrainStationCenter implements Recruit {
@Override
public void find() {
System.out.println("火车站总部在招人......");
}
}
不写代理类源代码,直接用动态代理生成
代码语言:javascript复制public class ProxyTest {
public static void main(String[] args) {
TrainStationCenter trainStationCenter = new TrainStationCenter();
InvocationHandler proxyHandler1 = new ProxyHandler(trainStationCenter);
Recruit proxyInstance = (Recruit) Proxy.newProxyInstance(trainStationCenter.getClass().getClassLoader()
, trainStationCenter.getClass().getInterfaces()
, proxyHandler1);
proxyInstance.find();
}
}
/**
注册账号......
火车站总部在招人......
**/
通过动态代理,就可以使用一个动态代理类,去代理多个对象。
cglib的动态代理
动态代理只能代理接口,要想代理类,可以使用cglib的动态代理类
首先写一个真实的类,比如火车站类,里面有卖票的方法
代码语言:javascript复制public class TrainStation {
/**
* 卖票方法
*/
public void sell() {
System.out.println("火车站卖票......");
}
}
然后写一个ProxyHandler,它实现了MethodInterceptor接口,重写了intercept方法,类似于jdk中的invoke方法
代码语言:javascript复制public class ProxyHandler implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("注册......");
Object o1 = methodProxy.invokeSuper(o, objects);
return o1;
}
}
不写代理类源代码,直接用cglib动态生成字节码
代码语言:javascript复制public class ProxyTest {
public static void main(String[] args) {
//创建Enhancer对象,类似于JDK动态代理中的Proxy类,下一步就是设置几个参数
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(TrainStation.class);
//设置回调函数
enhancer.setCallback(new ProxyHandler());
//创建代理对象
TrainStation trainStation = (TrainStation) enhancer.create();
trainStation.sell();
}
}
/**
注册账号......
火车站卖票......
**/
哪些地方用到了动态代理
spring中的AOP思想就是使用了动态代理,AOP通过动态代理对目标方法进行了增强,比如拦截器。
总结
静态代理总结:
1)代理类的信息在jvm运⾏之前就已经⽣成,逻辑由开发者实现;
2)代理类与⽬标类的定义应该严格参照规范,定义公共接⼝并实现它,需要代理的⽅法在接⼝中都要定义好;
静态代理原理:在代理类中包含⼀个⽬标类的对象引⽤,然后在使⽤时创建⼀个⽬标类对象并且创建⼀个代理类对象,并把⽬标类对象传给代 理类对象,然后将它赋予代理类中的⽬标类对象引⽤,然后代理类所代理的⽅法中通过其所包含的⽬标类对象引⽤调⽤⽬标类的⽅法,从⽽实现通过代理调⽤⽬标类⽅法的效果。
动态代理总结:
1) 动态代理是指 在java程序运⾏过程(程序已经启动在运⾏了)由jvm⽣成代理类的class信息,该class信息⽣成后是直接处于内存中的,并没有写⼊磁盘保存起来;然后通过反射⽅式实例化代理类对象,因为代理类的class信息已经存在于内存中,所以可以通过反射⽅式实例化。
这个应该怎么理解呢?
可以跟上⾯讲过的静态代理对⽐下,静态代理是需要开发⼈员⾃⼰实现代理类的逻辑的,且代理类的class信息是在程序运⾏之前就已经可以获取到的.java⽂件经过编译后可以得到.class⽂件;
⽽动态代理是不需要开发⼈员⾃⼰实现代理类的,也就是说使⽤动态代理⽅式的话,项⽬代码中是不存在代理类的.java⽂件的,既然代理类未由开发者实现,那么程序经过编译之后肯定也不会有代理类的.class⽂件,
也就是说经过编译之后程序未启动运⾏之前,关于代理类的信息我们⼀⽆所知,它是在程序运⾏过程中需要⽤到的时候才会由jvm动态⽣成的,⽽且⽣成之后也只存在于内存中,不会写到磁盘保存成.class⽂件,更加不会保存为.java⽂件;
在程序重启或者说发⽣了gc,这个代理类的class信息从内存中被卸载之后,关于这个代理类的信息就没有了,只有当代码再次访问到代理对象时,才⼜会重新⽣成代理类的class信息。
2)动态代理与静态代理的区别是什么?
上⾯已经讲述,不再赘述。
3)为什么需要引⼊动态代理?
这就不得不说到静态代理的弊端,我们引⼊新事物,必定是因为旧事物存在不合理之处,所以才引⼊新的事物来弥补它的缺陷。
那静态代理有什么缺陷呢?
我们知道静态代理是需要开发者⾃⼰实现代理类逻辑的,也就是说要对某个类进⾏代理的话,需要实现这个类相应的代理类;
如果⽬标类的数量很多的话,代理类的实现也必然得很多,可能会造成代码量过于庞⼤,可能会增加代码的冗余度…
再者,如果⽬标类需要代理的⽅法很多的话,代理类需要对这些⽅法⼀⼀实现代理逻辑,代理类的实现也将会很庞⼤。
考虑到这些问题,催⽣了动态代理这种⽅式,它相⽐于静态代理来说,由于不需要开发者⾃⼰再实现代理类了,所以在实际⼤型项⽬中可能代码量会⼤⼤减少。