代理模式就是给对象提供一个代理,并由代理对象来控制原对象的引用,使得客户不能与真正的目标对象通信,代理对象是目标对象的代表, 其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉
静态代理
抽象角色:一般使用接口和抽象类来实现
public interface Rent {
//抽象对象
public void rent();
}
真实角色:被代理的角色
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房屋出租");
}
}
代理角色:代理真实角色
public class Proxy implements Rent{
private Host host;
public Proxy(Host host){
this.host=host;
}
@Override
public void rent() {
host.rent();
}
}
客户:使用代理角色进行操作
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮房东
Proxy proxy = new Proxy(host);
//客户找中介
proxy.rent();
}
}
优点:使真实角色更加存粹,公共业务由代理类来完成,满足开闭原则
缺点:多了代理类,工作量变大,对于框架场景不太适合
动态代理
基于接口的JDK代理
创建一个对象生成器,实现了InvocationHandler接口重写了invoke方法,被代理的目标对象要实现接口,通过接口来获取对象信息
public class ProxyInvocationHandler implements InvocationHandler {
private Host host;
public ProxyInvocationHandler(Host host){
this.host = host;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o = method.invoke(host, args);
return o;
}
//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
host.getClass().getInterfaces(),this);
}
}
基于cglib代理
对于没有接口的类,想要实现动态代理,就需要cglib了,CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建 子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
Cglib 子类代理实现方法:
1.需要引入 cglib 的 jar 文件,但是 Spring 的核心包中已经包括了 Cglib 功能,所 以直接引入 spring-core-xxx.jar 即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为 final,否则报错
4.目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额 外的业务方法.
public class CarFactoryImpl {
public void sell() {
System.out.println("汽车厂卖汽车");
}
}
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/*
* 拦截所有目标类方法的调用
* 参数:
* obj 目标实例对象
* method 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//代理类调用父类的方法
System.out.println("开始事务");
Object obj1 = proxy.invokeSuper(obj, args);
System.out.println("关闭事务");
return obj1;
}
}
public class Test {
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
CarFactoryImpl carFactory = (CarFactoryImpl) proxy.getProxy(CarFactoryImpl.class);
carFactory.sell();
}
}