代理模式的定义
代理模式即给一个对象提供一个代理的对象,代理对象其实是目标对象(被代理对象)的一种增强和扩展。相当于生活中明星的经纪人、中介。
代理模式的分类
静态代理
1、定义一个抽象公共对象
/**
* @author yms
* @description:定义一个抽象公共对象,即一个公共接口,让目标类和代理类去实现
* @date: 2021/1/8 18:38
*/
public interface IHaShiQiStore {
void sellHaShiQi(String type);
}
2、目标类(被代理类)
/**
* @author yms
* @description:目标类
* @date: 2021/1/8 18:42
*/
public class HaShiQiStore implements IHaShiQiStore{
@Override
public void sellHaShiQi(String type) {
System.out.println("该商店出售"+type);
}
}
3、代理类
/**
* @author yms
* @description:代理类
* @date: 2021/1/8 18:43
*/
public class StaticProxy implements IHaShiQiStore{
private IHaShiQiStore dogStore;
public StaticProxy (IHaShiQiStore store){
this.dogStore = store;
}
public void beforeSell(){
System.out.println("进行售前咨询");
}
public void afterSell(){
System.out.println("进行售后保养");
}
@Override
public void sellHaShiQi(String type) {
beforeSell();
dogStore.sellHaShiQi(type);
afterSell();
}
}
4、测试
/**
* @author yms
* @description:测试
* 静态代理的好处:编码相对简单
* 静态代理的弊端:扩展性、可维护性较差,
* 如果需要出售金毛,则需要重新创建一个新的公共接口、目标类及代理类,会造成很多重复代码
* @date: 2021/1/8 18:50
*/
public class StaticProxyTest {
public static void main(String[] args) {
//创建一个目标类对象
IHaShiQiStore target = new HaShiQiStore();
//创建一个代理类对象
IHaShiQiStore proxy = new StaticProxy(target);
//代理对象负责完成宠物狗的出售
proxy.sellHaShiQi("哈士奇");
}
}
动态代理
动态代理下,我们的代理类不需要和被代理类实现同一个接口,即在设计代理类的时候不需要去关心要代理谁,而是在运行的时候才会去指定需要代理的对象。Spring AOP就是通过动态代理去实现的。动态代理又分为两种:JDK动态代理和Cglib动态代理。
JDK动态代理
1、抽象公共对象
/**
* @author yms
* @description:需要动态代理的接口
* @date: 2021/1/9 10:48
*/
public interface IPetStore {
void sellPet(String type);
}
2、被代理类
/**
* @author yms
* @description:实际被代理的类(目标类)
* @date: 2021/1/9 10:50
*/
public class JdPetStore implements IPetStore{
@Override
public void sellPet(String type) {
System.out.println("京东宠物店,出售宠物--->"+type);
}
}
3、代理类(动态处理器)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author yms
* @description:代理类
* @date: 2021/1/9 10:51
* JdkProxy 实现了 InvocationHandler 接口,并能实现方法调用从代理类到被代理类的分派转发
* 其内部通常包含指向被代理类实例的引用,用于真正执行分派转发过来的方法调用.
* 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
*/
public class JdkProxy implements InvocationHandler {
//被代理的真实对象
private Object target;
//构造方法,给要代理的真实对象赋初始值
public JdkProxy (Object target){
this.target = target;
}
public void beforeSell(){
System.out.println("--------->这里负责售前,买之前做好心理准备");
}
public void afterSell(){
System.out.println("--------->这里负责售后,专治各种不服");
}
/**
* 所有动态代理类的方法调用,都会交给invoke()方法来处理
* @param proxy 代理对象
* @param method 将要执行的方法(被代理的方法)的信息(通过反射来调用这个方法)
* @param args 执行方法需要的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method信息---->"+method);
beforeSell();
Object result = method.invoke(target, args);//通过反射调用类里面的实际方法
afterSell();
return result;
}
}
4、测试
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @author yms
* @description:
* @date: 2021/1/9 11:02
*/
public class JdkProxyTest {
public static void main(String[] args) {
//被代理对象
IPetStore jd = new JdPetStore();
//当前被代理对象的类加载器
ClassLoader jdLoader = jd.getClass().getClassLoader();
//当前被代理对象所实现的所有接口
Class [] jdInterfaces = jd.getClass().getInterfaces();
InvocationHandler handler = new JdkProxy(jd);
//通过Proxy获取代理对象
IPetStore proxyJD = (IPetStore) Proxy.newProxyInstance(jdLoader,jdInterfaces,handler);
proxyJD.sellPet("哈士奇");
}
}
Cglib动态代理
1、抽象公共对象
/**
* @author yms
* @description:目标接口
* @date: 2021/1/9 23:08
*/
public interface IStar {
void singASong();
}
2、 被代理类
/**
* @author yms
* @description:被代理类
* @date: 2021/1/9 23:11
*/
public class StarImpl implements IStar{
@Override
public void singASong() {
System.out.println("郑源开演唱会");
}
}
3、代理类
/**
* @author yms
* @description:代理类
* @date: 2021/1/9 23:14
* cglib是一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法
* 简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。
* 但是如果被代理类被final修饰,那么它不可被继承,即不可被代理;
* 同样,如果被代理类中存在final修饰的方法,那么该方法也不可被代理
*
*/
public class CglibProxy implements MethodInterceptor {
public void beforeSing(){
System.out.println("主办方找到郑源的经纪人");
}
public void afterSing(){
System.out.println("经纪人处理演唱会后的相关事宜");
}
/**
* 代理方法
* @param target 被代理类
* @return 代理类
*/
public Object createProxy(Object target){
//创建一个动态代理类对象
Enhancer enhancer = new Enhancer();
//确定要代理的类,设置其父类
enhancer.setSuperclass(target.getClass());
//添加回调函数,即方法拦截处理器
enhancer.setCallback(this);
//返回创建的代理类
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
beforeSing();
Object obj = methodProxy.invokeSuper(o,objects);
afterSing();
return obj;
}
}
4、测试
/**
* @author yms
* @description:
* @date: 2021/1/9 23:38
* 1、静态代理需要为每个目标类(被代理类)创建一个代理类,代理类需要和被代理类实现同一个接口,
* 如果被代理类发生了改变,则代理类则需要同步进行改变,会造成很多重复代码,可维护性大大降低
* 2、JDK的动态代理具有局限性,使用动态代理的对象必须实现一个或多个接口
* 3、如果想代理没有实现接口的类,那么使用CGLIB动态代理即可
* 4、Spring AOP基于动态代理实现,很好的对OOP进行了延伸和扩展
*/
public class CgProxyText {
public static void main(String[] args) {
//创建代理类对象
CglibProxy proxy = new CglibProxy();
//创建被代理类对象(目标对象)
IStar star = new StarImpl();
//获取扩展后的被代理类对象
IStar strongStar = (IStar) proxy.createProxy(star);
//执行被代理类的方法
strongStar.singASong();
}
}