代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的优缺点
优点
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
- 代理对象可以扩展目标对象的功能
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度
缺点
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢
- 增加了系统的复杂度
代理模式的结构
- Subject(共同接口):客户端使用的现有接口
- RealSubject(真实对象):真实对象的类
- ProxySubject(代理对象):代理类
静态代理
实现过程
-
为要被代理的目标类型设计一个接口
-
目标类型实现这个接口 但是只关注核心业务
-
代理类型 同样实现这个接口
举例说明
Subject(共同接口)为要被代理的目标类型设计一个Teacher接口
public interface Teacher{
void teach();
}
RealSubject(真实对象) 目标类型实现这个接口 但只需要关注核心业务(周老师 : 只需要使劲上课)
public class JayZhou implements Teacher{
@Override
public void teach(){
System.out.println("核心业务:使劲上课");
}
}
ProxySubject(代理对象) 代理类型同样实现这个接口(助教大白: 上课前帮老师收手机上课后帮老师看自习)
public class BigWhite implements Teacher{
Teacher tea;//代理类型应当持有一个目标类型的对象
public BigWhite(Teacher tea){
this.tea = tea;
}
@Override
public void teach(){
System.out.println("前置非核心:收手机");
tea.teach();//邀请核心执行 大白邀请周老师讲课
System.out.println("后置非核心:看自习");
}
}
客户端
public class Test{
public static void main(String[] args){
JayZhou jay = new JayZhou();
BigWhite db = new BigWhite(jay);
db.teach();
}
}
动态代理
-
静态代理已经足以指向核心和非核心编码层面的解耦
-
但是 编码层面付出很大 假如 要被代理的是100个类 当中的500个方法 写死你!
基于JDK的动态代理
public interface Teacher {
Object teach();
}
public class JayZhou implements Teacher{
@Override
public Object teach(){
System.out.println("核心业务:使劲上课~");
return null;
}
}
public class TestJDKDynamicProxy {
public static void main(String[] args) {
JayZhou jay = new JayZhou();//要被代理的目标对象
//代理工具类.新建一个代理类的实例
// 1st.ClassLoader对象 = 类加载器对象
// jay.getClass().getClassLoader()
// 2nd.Class[] 代理类要实现哪些接口
// new Class[]{Teacher.class} //愚蠢的写法
// jay.getClass().getInterfaces()
// 3rd.InvocationHandler 调用控制器
Teacher db = (Teacher)Proxy.newProxyInstance(
jay.getClass().getClassLoader(),
jay.getClass().getInterfaces(),
new InvocationHandler(){
@Override
public Object invoke(Object obj, Method m, Object[] args)
throws Throwable {
System.out.println("==前置非核心:收手机==");
Object ok = m.invoke(jay, args);
System.out.println("==后置非核心:看自习==");
return ok;
}
});
Object obj = db.teach();
}
}
基于CGLIB 的动态代理
需要引入JAR包
public class TestCGLIBDynamicProxy {
public static void main(String[] args) {
JayZhou jay = new JayZhou();
Enhancer en = new Enhancer();
en.setSuperclass(jay.getClass());
en.setCallback(new MethodInterceptor(){
@Override
public Object intercept(Object obj, Method m, Object[] args,
MethodProxy mp) throws Throwable {
System.out.println("==前置非核心:收手机==");
//Object ok = m.invoke(jay, args);
Object ok = mp.invokeSuper(obj, args);
System.out.println("==后置非核心:看自习==");
return ok;
}
});
jay = (JayZhou)en.create();
jay.teach();
}
}
JDK动态代理与CGLIB 动态代理的区别(面试题)
- JDK 动态代理只能针对接口实现类生成代理实例,而不能针对类;也就是说它是面向接口的
- CGLIB 是针对类实现代理,主要是对指定的类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成 final,对于 final 类或方法,是无法继承的
JDK动态代理与CGlab的动态代理类加载的过程