1.什么是代理
代理模式是常用的Java设计模式,他们的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的来说就是,我们在访问实际对象时,是通过代理对象来访问的。
简单来说,代理可以在不修改原目标类代码的前提下,增加或修改原目标类。同时避免了用户直接访问核心方法。
Java中的代理分为两大类:静态代理(基于jdk实现的静态代理)和动态代理(基于jdk实现的动态代理、基于CGLB实现的动态代理)。
2.静态代理
静态代理是指在程序运行前就已经存在代理类的字节码文件,代理类和目标类的关系在运行前就已确定,且代理类通常是手工编写的。
其模式为由核心类(目标类)生成核心对象(目标对象),由代理类生成代理对象,在由代理对象代理核心对象,而核心类和代理类都会实现同一个接口。
接口起到了一个沟通(通知)的作用,即通知代理类所要代理的核心是什么。
静态代理的案例:
1、接口类(BuyShoes)
接口核心方法clothes
package 代理;
public interface BuyClothes {
void clothes(String size);
}
2、核心类(ShoesFactory)
核心类实现核心方法
package 代理;
public class ClothesFactory implements BuyClothes{
public void clothes(String size){
System.out.println("为您定制了一款大小为"+size+"的衣服");
}
}
3、代理类(Proxy)
创建目标类ClothesFactory的对象,实现接口的核心方法,在方法中调用核心类对象的核心方法。然后添加服务方法。
package 代理;
public class Proxy implements BuyClothes{
private ClothesFactory clothesFactory = new ClothesFactory();
@Override
public void clothes(String size) {
PreService();
clothesFactory.clothes(size);
AfterService();
}
public void PreService(){
System.out.println("为您进行了市场调研");
}
public void AfterService(){
System.out.println("为您提供一系列售后服务");
}
}
4、测试类
package 代理;
public class Test {
public static void main(String[] args) {
//静态代理
Proxy proxy = new Proxy();
proxy.clothes("2xl");
}
}
结果:
静态代理的缺点:
-
静态代理类和被代理类实现相同的接口,导致接口的数量增加,系统更加臃肿。
-
每一个代理类只能代理一个接口,如果要代理多个接口就需要创建多个代理类。
-
代理类需要手动编写,代码量较大,且容易出错。
3.动态代理
为了解决静态代理的问题,我们使用动态代理。动态代理可以通过Java的反射机制在运行时动态地生成代理类,无需手动编写代理类。动态代理可以代理多个接口,且可以实现通用的代理逻辑,避免了代码重复,同时也避免了静态代理中需要手动编写代理类的缺点。
Java提供了java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现动态代理。
动态代理的案例
1、接口类(BuyClothes)(BuyShoes)
接口类定义核心方法
public interface BuyClothes {
void clothes(String size);
}
public interface BuyShoes {
void shoes(String size);
}
2、核心类(ClothesFactory)(ShoesFactory)
核心类实现接口类的核心方法
public class ClothesFactory implements BuyClothes{
public void clothes(String size){
System.out.println("为您定制了一款大小为"+size+"的衣服");
}
}
public class ShoesFactory implements BuyShoes{
@Override
public void shoes(String size) {
System.out.println("为您定制了一款大小为"+size+"的鞋子");
}
}
3、动态代理类(DyProxy)
定义一个实现了InvocationHandler
接口的代理类,这个类中需要实现invoke
方法,在该方法中实现对目标对象方法的调用和增强逻辑的织入。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DyProxy implements InvocationHandler {
//让用户告诉我们,要代理谁
private Object o;
public DyProxy(Object o){
this.o = o;
}
//第二步:获取目标类的接口,要知道自己代理的核心方法是什么
public Object getProxyInterface(){
return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(),this);
}
//invoke方法是利用反射获取到要代理的核心方法
//Object:jdk创建的代理类,无需赋值
//Method:目标类当中的方法,jdk提供,无需赋值
//Object[]:目标类当中的方法的参数,jdk提供,无需赋值
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
PreService();
method.invoke(o,args);
AfterService();
return null;
}
public void PreService(){
System.out.println("为您进行了市场调研");
}
public void AfterService(){
System.out.println("为您提供一系列售后服务");
}
}
4、Test类
public class Test {
public static void main(String[] args) {
//动态代理
ClothesFactory clothesFactory = new ClothesFactory();
DyProxy dyProxy1 = new DyProxy(clothesFactor);
//已经知道了目标类当中的核心方法是什么
BuyClothes clothes = (BuyClothes) dyProxy1.getProxyInterface();
clothes.clothes("XXL");
ShoesFactory shoesFactory = new ShoesFactory();
DyProxy dyProxy2 = new DyProxy(shoesFactory);
//已经知道了目标类当中的核心方法是什么
BuyShoes shoes = (BuyShoes) dyProxy2.getProxyInterface();
shoes.shoes("40");
}
}
结果:
相比静态代理,动态代理有以下优点:
- 可以动态代理多个接口:动态代理使用的是接口代理,而不是类代理,因此可以代理多个接口。这对于需要实现多个接口的类来说非常方便。
- 避免了静态代理中需要手动编写代理类的缺点:动态代理是在运行时生成代理类的技术,可以避免静态代理中需要手动编写代理类的缺点。
- 代码更简洁:由于动态代理可以实现通用的代理逻辑,因此代码更加简洁。
- 可以支持 AOP 编程:动态代理可以实现 AOP 编程,即在不修改原有代码的情况下,增加日志、事务等功能。
总之,动态代理相比静态代理更加灵活、方便、简洁,可以避免代码重复,实现通用的代理逻辑,支持 AOP 编程。因此,在实际开发中,我们应该尽量使用动态代理,避免使用静态代理。