1.代理模式
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。(结构型模式)
2.优缺点
优点:
- 代理模式能将代理对象与真实对象被调用的目标对象分离。
- 一定程度上降低了系统的耦合度,扩展性好。
- 保护目标对象。
- 增强目标对象。
缺点:
- 代理模式会造成系统设计中类的数目的增加。
- 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
- 增加了系统的复杂度。
3.代理模式分类
- 静态代理
- JDK动态代理
- CGLIB动态代理
4.简单实现例子
静态代理
用歌手,经纪人的例子举例:
静态代理.jpg
静态代理必须实现同一个接口
代码:
public interface Sing {
void singSongs();
}
//歌手
public class Singer implements Sing {
@Override
public void singSongs() {
System.out.println("singer can sing songs");
}
}
//经纪人
public class Agent implements Sing {
private Sing sing = null;
@Override
public void singSongs() {
sing.singSongs();
}
public Agent(Sing sing) {
this.sing = sing;
System.out.println("代理人接收到唱歌邀请");
}
public void before(){
System.out.println("判断是否能去,金钱,时间安排,对歌手有没影响");
}
public void after(){
System.out.println("邀请单位给出评价,相约下次合作");
}
}
演出开始
public class Performance {
public static void main(String[] args) {
Singer singer = new Singer();
Agent agent = new Agent(singer);
agent.before();
agent.singSongs();
agent.after();
}
}
result
代理人接收到唱歌邀请
判断是否能去,金钱,时间安排,对歌手有没影响
singer can sing songs
邀请单位给出评价,相约下次合作
总结
从上面的代码中可以看出静态代理类,将代码写死了,如果需要这个代理类代理多个类那么就需要为每个类编写代码,这样做不利于程序的扩展
JDK动态代理
JDKAgent
public class JDKAgent implements InvocationHandler {
private Object target;
public JDKAgent(Object target) {
this.target = target;
}
public Object creatAgent(){
// 1.得到目标对象的类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
// 2.得到目标对象的实现接口
Class<?>[] interfaces = target.getClass().getInterfaces();
// 3.第三个参数需要一个实现invocationHandler接口的对象
Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, this);
return newProxyInstance;
}
// 第一个参数:代理对象.一般不使用
// 第二个参数:需要增强的方法
// 第三个参数:方法中的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invoke = method.invoke(target, args);
after();
return invoke;
}
public void before(){
System.out.println("判断此次演出准备工作是否到位");
}
public void after(){
System.out.println("演出是否完美收官");
}
}
测试类
public class JDKDemo {
public static void main(String[] args) {
Singer singer = new Singer();
JDKAgent jdkAgent = new JDKAgent(singer);
Sing sing = (Sing)jdkAgent.creatAgent();
sing.singSongs();
}
}
result
判断此次演出准备工作是否到位
singer can sing songs
演出是否完美收官
总结
-
Interface:对于JDK Proxy,业务类是需要一个Interface的,这是一个缺陷;
-
Proxy:Proxy类是动态产生的,这个类在调用Proxy.newProxyInstance()方法之后,产生一个Proxy类的实力。实际上,这个Proxy类也是存在的,不仅仅是类的实例,这个Proxy类可以保存在硬盘上;
-
Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来
-
InvocationHandler:这个类在业务委托类执行时,会先调用invoke方法。invoke方法在执行想要的代理操作,可以实现对业务方法的再包装。
CGLIB动态代理
CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.如果你要单独使用CGLIB,那么需要导入cglib的jar包还需要一个asm相关jar包,但是spring框架的spring-core.jar包中已经集成了cglib与asm.
CGLib是针对类来实现代理的,他的原理是对指定的目标生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理.
注意:
jdk的动态代理只可以为接口去完成操作,而cglib它可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理
Dancer
public class Dancer {
public void dance(){
System.out.println("dancer can dance");
}
}
代理类
public class CglibAgent implements MethodInterceptor {
//得到目标对象
private Object target;
//使用构造方法传递目标对象
public CglibAgent(Object target) {
super();
this.target = target;
}
//创建代理对象
public Object createProxy(){
//1.创建Enhancer
Enhancer enhancer = new Enhancer();
//2.传递目标对象的class
enhancer.setSuperclass(target.getClass());
//3.设置回调操作
enhancer.setCallback(this);
return enhancer.create();
}
@Override
//参数一:代理对象;参数二:需要增强的方法;参数三:需要增强方法的参数;参数四:需要增强的方法的代理
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 增强前
System.out.println("这是增强方法前......");
Object invoke = methodProxy.invoke(target, args);
// 增强后
System.out.println("这是增强方法后......");
return invoke;
}
}
测试
public class CglibDemo {
public static void main(String[] args) {
Singer singer = new Singer();
CglibAgent cglibAgent = new CglibAgent(singer);
Sing sing = (Sing)cglibAgent.createProxy();
sing.singSongs();
System.out.println("--------separator--------");
Dancer dancer = new Dancer();
CglibAgent dancerAgent = new CglibAgent(dancer);
Dancer proxy = (Dancer) dancerAgent.createProxy();
proxy.dance();
}
}
result
这是增强方法前......
singer can sing songs
这是增强方法后......
--------separator--------
这是增强方法前......
dancer can dance
这是增强方法后......
总结
方式 | 优点 | 缺点 |
---|---|---|
静态代理 | 简单,需实现统一接口 | 扩展性差,性能 |
JDK动态代理 | 耦合低,需实现统一接口 | 性能 |
CGLIB动态代理 | 不需实现统一接口,代理类不能是final | 性能 |
代码 proxy-demo
网友评论