定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
1. 静态代理
创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
功能结构:
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
// 接口类
public interface Hello {
void sayHello();
}
// 被代理类
public class MyHello implements Hello{
@Override
public void sayHello() {
System.out.println("nihaoa !");
}
}
// 代理类
public class StaticHello implements Hello{
Hello hello = new MyHello();
@Override
public void sayHello() {
hello.sayHello();
}
}
// 测试调用
public class TestDemo {
public static void main(String[] args) {
Hello hello = new StaticHello();
hello.sayHello();
}
}
//输出
nihaoa !
1.1 总结
优点
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
缺点
使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
2. jdk动态代理
// 接口类
public interface Hello {
void sayHello();
}
// 被代理类
public class MyHello implements Hello{
@Override
public void sayHello() {
System.out.println("nihaoa !");
}
}
// 代理类
public class MyInvocationHandler implements InvocationHandler {
private Object object;
public MyInvocationHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置执行:" + method.getName());
method.invoke(object, args);
System.out.println("后置执行:" + method.getName());
return null;
}
}
// 测试调用
public class TestDemo {
public static void main(String[] args) {
// jdk动态代理
Hello myHello = new MyHello();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(myHello);
Hello hello = (Hello) Proxy.newProxyInstance(myHello.getClass().getClassLoader(), myHello.getClass().getInterfaces(), myInvocationHandler);
hello.sayHello();
}
}
// 执行结果
前置执行:sayHello
nihaoa !
后置执行:sayHello
2.1 总结
-
在静态代理中我们需要对哪个接口和哪个被代理类创建代理类,所以我们在编译前就需要代理类实现与被代理类相同的接口,并且直接在实现的方法中调用被代理类相应的方法;但是动态代理则不同,我们不知道要针对哪个接口、哪个被代理类创建代理类,因为它是在运行时被创建的。
-
静态代理是通过直接编码创建的,而动态代理是利用反射机制在运行时创建代理类的。
-
源码分析大佬博客:https://www.jianshu.com/p/9bcac608c714
3. cglib 动态代理
CGLIB包的底层是通过使用一个小而快的字节码处理框架
ASM
,来转换字节码并生成新的类实现原理:
- 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
- 然后在需要使用的时候,通过CGLIB动态代理获取代理对象。
// 被代理类
public class HelloService {
public HelloService() {
System.out.println("HelloService构造");
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
// 代理类
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 前置操作
Object o1 = methodProxy.invokeSuper(o, objects);
// 后置操作
return o1;
}
}
// 测试调用
public class TestDemo {
public static void main(String[] args) {
// cglib 动态代理
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(HelloService.class);
// 设置enhancer的回调函数
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理对象
HelloService helloService = (HelloService) enhancer.create();
helloService.sayHello();
}
}
// 执行结果
HelloService构造
HelloService:sayHello
3.1 总结
- JDK代理要求被代理的类必须实现接口,有很强的局限性。
- 而CGLIB动态代理则没有此类强制性要求。简单的说,
CGLIB
会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。 - 生成的代理类继承被代理类。在这里我们需要注意一点:如果委托类被final修饰,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理
- 源码分析大佬博客:https://mp.weixin.qq.com/s/GT1-yrxJ5KF0xeMydbJDCQ#