代理是基本的设计模式之一,是为了提供额外或不同的操作而插入的用以代替实际的"对象"的对象。代理对象通常继承自实际对象或将实际对象作为自己的成员变量,因此能够在提供额外操作的同时与"实际对象"通信并调用其原有的功能。
代理模式定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
根据创建代理类的不同可以分为静态代理和动态代理。
静态代理
程序员创建或特定代码生成工具自动生成源代码,在对其编译。在程序运行前,代理类.class字节码文件就已经存在。
interface IStar {
void dance();
String sing(String song,String song_text);
}
@Data
@AllArgsConstructor
public class BigStar implements IStar {
private String starName;
@Override
public void dance() {
System.out.println(starName+"正在跳舞~");
}
@Override
public String sing(String song,String song_text) {
System.out.println(this.starName+"正在唱歌:《"+song+"》");
return song_text;
}
}
public class ProxyUtil implements IStar{
private IStar star;
public ProxyUtil(IStar star){
this.star = star;
}
@Override
public void dance() {
System.out.println("代理安排跳舞产地");
this.star.dance();
}
@Override
public String sing(String song, String song_text) {
System.out.println("代理安排唱歌产地");
String text = this.star.sing(song,song_text);
System.out.println("歌词:"+text);
return "谢谢";
}
}
public class Test {
public static void main(String[] args) {
//创建一个阿yueyue的代理
ProxyUtil 阿yueyue的代理 = new ProxyUtil(new BigStar("阿yueyue"));
阿yueyue的代理.dance();
阿yueyue的代理.sing("沈园外","歌词1111");
//创建一个虞书欣的代理
ProxyUtil 虞书欣的代理 = new ProxyUtil(new BigStar("虞书欣"));
虞书欣的代理.dance();
虞书欣的代理.sing("如果爱忘了","歌词2222");
}
}
上例是一个静态代理的实现,一个委托类对应一个代理类,代理类在编译期间就已经确定。如果没有使用接口,代理类可以通过继承委托类实现静态代理。
动态代理
代理类在程序运行时利用反射机制动态创建而成,主要分为jdk动态代理和cglib动态代理。
jdk实现动态代理需要实现类通过接口定义业务方法,对于没有接口的实现类,可以使用cglib代理。
cglib采用了非常底层的字节码技术,原理是通过字节码技术为一个类创建一个子类(继承的方式),
并在子类中使用方法拦截技术拦截所以父类方法的调用,顺势织入横切逻辑,两种代理都是实现Spring Aop的基础。
jdk动态代理
JDK 动态代理类的字节码在程序运行时由 Java 反射机制动态生成,无需手工编写它的源代码。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为 Java 反射机制可以生成任意类型的动态代理类。
java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。
一、定义业务接口以及实现
public interface IUserService {
void login(String userName,String userPwd);
void register(String userName);
void download(String bookName);
}
public class UserService implements IUserService {
@Override
public void login(String userName,String userPwd) {
System.out.println("正在执行登录操作~");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("登录完成,账号:"+userName+",密码:"+userPwd);
}
@Override
public void register(String userName) {
System.out.println("正在执行注册操作~");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("用户名"+userName+"注册完成");
}
@Override
public void download(String bookName) {
System.out.println("正在执行下载操作~");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("《"+bookName+"》" +"下载完成。");
}
}
二、自定义创建代理的类,在该类里调用Proxy的静态方法创建一个代理类
public class DefineProxy {
private Object target;
public DefineProxy(Object target){
this.target = target;
}
public Object createProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始计算方法 "+method.getName()+" 的执行时间~");
long start = System.currentTimeMillis();
Object invoke = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("执行时间为:"+(end-start)+"毫秒。");
return invoke;
}
});
}
}
① DefineProxy构造方法传入的target对象是即将被代理的类的对象,最终还是需要使用这个对象来执行业务操作。
② Proxy的静态方法newProxyInstance参数
//public static Object newProxyInstance(ClassLoader loader,
// Class<?>[] interfaces,
// InvocationHandler h)
- loader:类加载器,使用本类的类加载器即可,或者target对象的类加载器也行。
- interfaces:接口数组,表示被代理类实现的接口,因可能会实现多个接口,所以这里是数组的形式。
- InvocationHandler h:核心,在这里创建一个内部实现类实现InvocationHandler接口,重写invoke方法
invoke里的参数proxy是代理类的实例,method是代理类的实例执行的方法,args则是执行的方法里面的参数,
我们可以在这里对执行核心业务逻辑前后增加代码。method.invoke(target, args)这里是用了反射的原理让target对象去执行method方法。
三、通过代理调用方法
public class Test {
public static void main(String[] args) {
// IUserService proxy = (IUserService)ProxyFactory.jdk_getProxyInstance(new UserService());
IUserService proxy = (IUserService)new DefineProxy(new UserService()).createProxy();
proxy.login("anbanyu","czw520kdd");
proxy.register("anbanyu");
proxy.download("小而美:持续盈利的经营法则");
}
}
cglib 动态代理
JDK 中提供的生成动态代理类的机制有个鲜明的特点是:某个类必须有实现的接口,如果某个类没有实现接口,那么这个类就不能通过 JDK 产生动态代理了!不过幸好我们有 CGLib。CGLIB(Code Generation Library)是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。此外,因为 CGLIB 采用整型变量建立了方法索引,这比使用 JDK 动态代理更快(使用 Java 反射技术创建代理类的实例)。
CGLib 创建某个类 A 的动态代理类的模式是:
- 查找 A 上的所有非 final 的 public 类型的方法定义;
- 将这些方法的定义转换成字节码;
- 将组成的字节码转换成相应的代理的 class 对象;
- 实现 MethodInterceptor 接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
一、首先定义一个委托类,注意就是一个普通的类
public class Dog {
public void eat(){
System.out.println("狗吃屎");
}
}
二、实现MethodInterceptor方法
public class CGLibProxy implements MethodInterceptor {
private Object target;
public CGLibProxy(Object target){
this.target = target;
}
public Object createProxy(){
//cglib中的增强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
//设置要创建动态代理的类
enhancer.setSuperclass(target.getClass());
//设置回调,这里相当于是对于代理类上所有方法的调用,都会调用callback,而callback则需要实现intercept()方法进行拦截。
enhancer.setCallback(this);
//创建代理类
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib proxy start...");
methodProxy.invokeSuper(o,args);
System.out.println("cglib proxy end...");
return null;
}
}
三、测试代理类
public class Test {
public static void main(String[] args) {
// Dog dog = (Dog)ProxyFactory.cglib_getProxyInstance(new Dog());
Dog dog = (Dog)new CGLibProxy(new Dog()).createProxy();
dog.eat();
}
}
- 注意由于 CGLib 动态代理采用的是继承委托类的方式,因此不能代理 final 修饰的类。如果将上例中的类Dog添加 final 修饰符,再次运行则会看到如下错误信息:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class chapter14.Train at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:565) at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:329)
两种动态代理区别总结
Java 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。而 CGLIB 动态代理是利用 ASM 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。
使用场景
实现 AOP 功能
Spring 的 AOP 功能就是利用动态代理的原理实现的。其会根据被代理对象是否实现了接口选择不同的生成代理对象的方式,如果被代理对象实现了需要被代理的接口,则使用 JDK 的动态代理,否则便使用 CGLIB 代理。
- 如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP,对应的包装类为 JdkDynamicAopProxy。
- 如果目标对象实现了接口,可以强制使用 CGLIB 实现 AOP
- 如果目标对象没有实现了接口,必须采用 CGLIB 库,spring 会自动在 JDK 动态代理和 CGLIB 之间转换
Spring Aop 和 cglib的关系
- 最底层是字节码。
- ASM是操作字节码的工具。
- cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)。
- Spring aop基于cglib进行封装,实现cglib方式的动态代理。
cglib依赖包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>