代理模式:
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式作用:
在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
静态代理模式:
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
/**
* @author Administrator
* 服务类接口
* 买房交易
*/
public interface JiaoYi1 {
void maifang(String houseName,double money);
}
/**
* @author Administrator
* 实现服务接口
* 购房者
*/
@Slf4j
public class FangNu1 implements JiaoYi1{
@Override
public void maifang(String houseName, double money) {
log.info("FangNu1买房。");
}
}
/**
* @author Administrator
* 代理类
*/
@Slf4j
public class Proxy1 implements JiaoYi1{
private FangNu1 fangNu1;
public Proxy1(FangNu1 fangNu1){
this.fangNu1 = fangNu1;
}
@Override
public void maifang(String houseName, double money) {
log.info("静态代理前置内容 ... ");
fangNu1.maifang(houseName,money);
log.info("静态代理后置内容 ... ");
}
}
测试类以及结果:
//静态代理
@Test
public void staticProxy(){
//代理的真实对象
FangNu1 fangNu1 = new FangNu1();
//代理对象
Proxy1 proxy1 = new Proxy1(fangNu1);
proxy1.maifang("9-107",15000000);
}
2019-09-29 13:38:39.091 INFO 3612 --- [main] c.z.l.d.proxypattern.staticproxy.Proxy1 : 静态代理前置内容 ...
2019-09-29 13:38:39.091 INFO 3612 --- [main] c.z.l.d.p.staticproxy.FangNu1 : FangNu1买房。
2019-09-29 13:38:39.092 INFO 3612 --- [main] c.z.l.d.proxypattern.staticproxy.Proxy1 : 静态代理后置内容 ...
静态代理总结:
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
动态代理模式:
动态代理是在程序运行时通过反射机制动态创建的。
/**
* @author Administrator
* 买房交易
*/
public interface JiaoYi2 {
void maifang(String houseName,double money);
}
/**
* @author Administrator
* 购房者
*/
@Slf4j
public class FangNu2 implements JiaoYi2{
@Override
public void maifang(String houseName, double money) {
log.info("FangNu2买房。");
}
}
/**
* @author Administrator
* 代理类
*/
@Slf4j
public class Proxy2 implements InvocationHandler {
private Object object;
public Proxy2(Object object){
this.object = object;
}
/**
* @param o 真实角色的对象
* @param method 要调用的真实对象的方法
* @param objects 要调用的真实对象方法的参数列表
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
// 在代理真实对象前我们可以添加一些自己的操作
log.info("静态代理前置内容 ... ");
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object obj = method.invoke(object,objects);
// 在代理真实对象后我们也可以添加一些自己的操作
log.info("静态代理后置内容 ... ");
return obj;
}
}
测试类以及结果:
/**
*动态代理
* 第一步: 首选生成一个真实角色的对象
* 第二步: 生成一个代理类的对象(代理类一定是实现了InvocationHandler 接口的),用一个InvocationHandler 引用去指向它, 并且获得handle类的信息。
* 第三步: 获得真实角色对象所实现的全部接口,并且用数组存储表示
* 第四步: 调用Proxy类的 newProxyInstance方法,动态的获得一个代理实例化的对象,并且将其强制转化为 接口类型的对象
* 第五步: 使用动态生成的代理对象调用需要调用的方法(系统内部会自动调用到真实对象的方法)
*/
@Test
public void dynamicProxy(){
FangNu2 fangNu2 = new FangNu2();
InvocationHandler proxy = new Proxy2(fangNu2);
Class<?> classType = fangNu2.getClass();
Class<?>[] classInterface = fangNu2.getClass().getInterfaces();
JiaoYi2 jiaoYi2 = (JiaoYi2)Proxy.newProxyInstance(classType.getClassLoader(),classInterface,proxy);
jiaoYi2.maifang("8-108",15000000);
}
2019-09-29 14:47:42.430 INFO 3192 --- [main] c.z.l.d.p.dynamicproxy.Proxy2 : JDK代理前置内容 ...
2019-09-29 14:47:42.430 INFO 3192 --- [main] c.z.l.d.p.dynamicproxy.FangNu2 : FangNu2买房。
2019-09-29 14:47:42.430 INFO 3192 --- [main] c.z.l.d.p.dynamicproxy.Proxy2 : JDK代理后置内容 ...
Cglib代理:
- 静态代理和动态代理都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理——这就是 Cglib 代理。
- Cglib 代理也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将 Cglib 代理归属于动态代理。
- Cglib 是一个非常强大的高性能的代码生成包,它可以在运行期扩展 java 类与 实现 java 接口。它广泛的被许多 AOP 框架使用,例如:Spring AOP,实现方法的拦截。
- 在AOP编程中如何选择代理:目标对象需要实现接口,用 JDK 代理。目标对象不需要实现接口,用 Cglib 代理。
- Cglib 包在底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。
/**
* @author Administrator
* 购房者
*/
@Slf4j
public class FangNu3 {
public void maifang(String houseName,double money){
log.info("FangNu3买房");
}
}
/**
* @author Administrator
* cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,
* 并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
*/
@Slf4j
public class Proxy3 implements MethodInterceptor {
// 业务类对象,供代理方法中进行真正的业务方法调用
private Object object;
public Proxy3(Object o){
this.object = o;
}
// 相当于JDK动态代理中的绑定
public Object getProxyInstance(){
// 创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
// 为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(this.object.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 创建子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
log.info("CGLIB代理前置内容");
log.info("Method:" + method);
//调用业务类(父类中)的方法
Object obj = methodProxy.invokeSuper(o,objects);
log.info("CGLIB代理后置内容");
return obj;
}
}
//CGLIB代理
@Test
public void CglibProxy(){
FangNu3 fangNu3 = (FangNu3)new Proxy3(new FangNu3()).getProxyInstance();
fangNu3.maifang("2-365",1565856855);
}
2019-09-30 13:57:57.352 INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.Proxy3 : CGLIB代理前置内容
2019-09-30 13:57:57.352 INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.Proxy3 : Method:public void com.zhw.learning.designpattern.proxypattern.cglibproxy.FangNu3.maifang(java.lang.String,double)
2019-09-30 13:57:57.356 INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.FangNu3 : FangNu3买房
2019-09-30 13:57:57.357 INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.Proxy3 : CGLIB代理后置内容
1.静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,
用户通过代理类调用被包装过的业务方法;
2.JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
3.CGlib(Code Generation Library)动态代理(Spring)是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;