目录
前言
这里可以添加本文要记录的大概内容:简单的讲解了一下设计模式中的代理模式
文章为学习笔记,不足之处,欢迎补充....
一、代理模式的概念
代理模式在 Java 开发中是一种比较常见的设计模式。设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用。
那什么是代理呢?举个生活中的例子,就好比某商品公司要请明星来代言,但不会亲自去和明星去交接,而是明星的经纪人来谈,明星只需要负责代言就好了,其他业务都交由经纪人来交接,此时经纪人就起到一个代理的作用。
二、静态代理
由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
(一个代理类只能代理一个目标对象)接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy
package com.javaxl.design.proxy.staticproxy; /** * @author 代码世界里的小李 * @site www.javaxl.com * @company * <p> * 目标类 */ public class TeacherDAO implements ITeacherDao { public void teach() { System.out.println("老师传授知识"); } } //目标接口 interface ITeacherDao { void teach(); } //代理类 class TeacherDAOProxy implements ITeacherDao { private ITeacherDao teacherDAO; public TeacherDAOProxy(ITeacherDao teacherDAO) { this.teacherDAO = teacherDAO; } @Override public void teach() { System.out.println("老师正式授课前的准备工作,如学生全部签到..."); teacherDAO.teach(); System.out.println("老师结束授课,如下课铃声响起..."); } } public class Client { public static void main(String[] args) { TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO()); proxy.teach(); } }
细节 代理对象与目标对象要实现相同的接口 调用的时候通过调用代理对象的方法来调用目标对象
静态代理的特点
1、目标角色固定
2、在应用程序执行前就得到目标角色
3、代理对象会增强目标对象的行为
4、有可能存在多个代理 引起"类爆炸"(缺点)
三、动态代理
相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法,即满足生产需要的同时又达到代码通用的目的。
动态代理的两种实现方式:JDK代理和Cglib代理
3.1 JDK代理
接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy
package com.javaxl.design.proxy.dynamic.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 目标接口
*/
interface ITeacherDao {
String teach();
ITeacherDao sleep(int minutes);
}
class TeacherDao implements ITeacherDao{
@Override
public String teach() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(new Date())+":老师传授知识";
}
@Override
public ITeacherDao sleep(int minutes) {
System.out.println("老师睡了" + minutes + "分钟");
return this;
}
}
//真实代理类的外衣
class TeacherDaoProxy{
private ITeacherDao target;
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
public Object xxx(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
String methodName = method.getName();
System.out.println("目标方法" + methodName + ":jdk代理开始...");
System.out.println("真实代理对象:"+proxy.getClass());
System.out.println("目标对象:"+target.getClass());
if("sleep".equals(methodName)){
// method.invoke(target, args);
// obj = proxy;
obj = method.invoke(target, args);
}else {
// proxy是真实代理类,method是目标方法,args是目标方法携带的参数
obj = method.invoke(target, args);
}
System.out.println("目标方法" + methodName + ":jdk代理结束...");
return obj;
}
});
}
}
public class Client {
public static void main(String[] args) {
TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
ITeacherDao ins = (ITeacherDao) proxy.xxx();
System.out.println("===========代理类实例被使用 begin=============");
System.out.println(ins);
System.out.println("===========代理类实例被使用 end=============");
System.out.println(ins.teach());
// System.out.println(proxy.execute());
System.out.println("===========代理类实例被使用 begin=============");
ins.sleep(10);
System.out.println("===========代理类实例被使用 end=============");
ins.sleep(20).sleep(60);
}
}
特点:不需要实现接口,但是目标对象要实现接口,否则不能用动态代理 ;代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象;代理类所在包:java.lang.reflect.Proxy
3.2 Cglib代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能使用JDK的动态代理,cglib是针对类来实现代理的,它的原理是对指定的目标类动态生成代理类的子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
注:在pom.xml文件中加入cglib的相关依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
package com.javaxl.design.proxy.dynamic.cglib; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.text.SimpleDateFormat; import java.util.Date; class TeacherDao { public String teach() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(new Date()) + ":老师传授知识"; } public TeacherDao sleep(int minutes) { System.out.println("老师睡了" + minutes + "分钟"); return this; } } //真实代理类的外衣 class TeacherDaoProxy implements MethodInterceptor { private Object target; public TeacherDaoProxy(Object target) { this.target = target; } //返回一个代理对象: 是 target 对象的代理对象 public Object getProxyInstance() { //1. 创建一个工具类 Enhancer enhancer = new Enhancer(); //2. 设置父类 enhancer.setSuperclass(target.getClass()); //3. 设置回调函数 enhancer.setCallback(this); //4. 创建子类对象,即代理对象 return enhancer.create(); } /** * @param proxyIns 由CGLib动态生成的代理类实例 * @param method 上文中实体类所调用的被代理的方法引用 * @param args 参数值列表 * @param methodProxy 生成的代理类对方法的代理引用 * @return * @throws Throwable */ @Override public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { String methodName = method.getName(); Object res; System.out.println("目标方法" + methodName + ":cglib代理开始..."); System.out.println("真实代理对象:" + proxyIns.getClass()); System.out.println("目标对象:" + target.getClass()); if ("sleep".equals(methodName)) { // method.invoke(target, args); // obj = proxy; res = method.invoke(target, args); // res = methodProxy.invokeSuper(proxyIns,args); res = proxyIns; } else { // proxy是真实代理类,method是目标方法,args是目标方法携带的参数 res = method.invoke(target, args); } System.out.println("目标方法" + methodName + ":cglib代理结束..."); return res; } } public class Client { public static void main(String[] args) { TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance(); proxy.sleep(111).sleep(222); } }
细节 目标对象与代理对象都不需要实现接口 Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展 Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用 Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
注意:①需要引入 cglib 的 jar 文件 ②在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException: ③目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
3.3 JDK代理与Cglib代理的比较
JDK代理中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现的接口中定义的方法进行代理 这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高
Cglib代理实现动态代理完全不受代理类必须要实现接口的限制,而且cglib底层采用ASM字节码生成,使用字节码技术生成代理类,比使用反射效率要高 唯一需要注意的是cglib不能声明为final的方法进行代理 原理是动态生成被代理类的子类
四、总结
应用 Spring框架的AOP就是Cglib代理的体现
个人对代理模式的一个简单了解就到这里.........
敖丙说过:你知道的越多,不知道的越多
作者:代码世界里的小李