代理模式:
对被代理的对象提供一种代理以控制对于对象的访问,代理对象可以控制对于对象的访问,在不改变目标对象的基础上添加额外的功能。
场景:(1)远程访问的对象 (2)创建开销很大的对象 (3)需要安全控制的对象(4)AOP(面向切面编程)(5)事务(6)权限管理
特点:
(1)具有两种主体:代理对象、被代理对象
(2)对于被代理对象,事情是必须要去做的,但是自己不想去做,或者没有条件去做,所有需要由代理对象去完成。
(3)代理对象能够获取被代理对象的资料,代码层面(能够获得被代理对象的引用)
生活中的案例:
(1)中介:通常买二手车的时候,会去网上找车源,对车进行质量检测,以及各种过户手续的办理,自己可能不想做这些事情,所以可以找第三方的中介公司来完成这些事情,我只负责把我想要的车辆的信息(价位、车辆新旧程度、品牌)反馈给中介,中介把所有的办好就只管我来签字验收就可以了。
(2)黄牛:当春运火车票比较紧张的时候,我抢不到票,且自己也不想去抢,则可以通过黄牛去买,我只管把我的车票信息给他(初始地、目的地、出发时间、车次),由此不用抢票,也可以买到相应的车票,抢票的过程由黄牛去做。
(3)媒人:平时自己没有时间去交女朋友,则需要媒人去介绍,我们只管把我们想要的女朋友的类型给媒人,媒人则帮我们去寻找合适的对象。
代理分为两种
(1)静态代理:在编译前,已经把代码创建好,程序运行时,class文件已经存在
(2)动态代理:程序运行时,运用反射机制动态生成代理类
静态代理实现:
接口
public interface IUser {
void add();
void delete();
}
具体实现类
public class UserImps implements IUser {
public void add() {
System.out.println("add");
}
public void delete() {
System.out.println("delete");
}
}
代理类
public class UserProxy implements IUser {
private IUser iUser;
public UserProxy(IUser iUser){
this.iUser = iUser;
}
public void add() {
System.out.println("before");
iUser.add();
System.out.println("after");
}
public void delete() {
System.out.println("before");
iUser.delete();
System.out.println("after");
}
}
由代码可以看出:代理类和实现类持有相同的接口,并且代理类持有具体实现类的引用,调用代理类相当于调用实现类,由此可以在代理类执行相应的增强代码。
缺点:
(1)不够灵活,每个代理类只为一个接口服务。会造成大量的代码重复
(2)静态代理类只为特定的接口服务,如果有多个接口,则要生成多个代理对象,不利于系统维护。
动态代理(关心过程):
动态代理的字节码类在程序运行时运用反射机制动态生成,不用手工写代码
动态代理类的实现
代理类
public class CarAgency implements InvocationHandler{
//被代理对象的引用
private Object target;
//获取被代理对象的信息,为被代理对象生成代理对象
public Object getInstanche(Object target){
this.target = target;
Class clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("办理车辆质量检查报告");
method.invoke(target,args);
System.out.println("办理车辆二手车过户");
return null;
}
}
接口类:
public interface CustomerService {
void bugCar();
}
接口实现类
public class CustomerServiceImpl implements CustomerService {
@Override
public void bugCar() {
System.out.println("决定就买这辆车了");
}
}
通过以上的步骤就可以实现一个动态代理,但是动态代理的原理是怎样的呢
原理:
(1)代理类拿到被代理类的引用,获取相应的接口信息
运行时生成class的条件:(1)提供一组interface (2)相应的类加载器 (3)生成proxy不处理实际工作,需要传入一个handler,由handler负责实际的工作
动态代理处于reflect包下,
(1)InvocationHandler:
InvocationHandler 接口为方法调用接口,它声明了负责调用任意一个方法的invoke()方法。(InvocationHandler 是代理实例的调用处理程序实现的接口。invoke()在代理实例上处理方法调用并返回结果
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
proxy:动态代理类实例
method:要调用的方法
args:要调用的参数
(2)Proxy(提供创建动态代理类 和 实例的静态方法)
getProxyClass() 提供动态代理类
public static Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) throws IllegalArgumentException
loader:动态代理的类加载器
interfaces:动态代理要实现的所有接口
<strong>newProxyInstance()(负责创建动态代理类的实例)
</strong>
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException
InvocationHandler h:InvocationHandler接口的子类的实例
interfaces:动态代理要实现的所有接口
loader:动态代理的类加载器 接口:
public interface DynamicHelloWorld { String hello(); }
实现类:
public class DynamicHelloWorldImps implements DynamicHelloWorld {
public String hello() {
System.out.println("hello world");
return "hello world";
}
}
动态代理类(实现invocationhandler)
public class DynamicProxy implements InvocationHandler {
//真实对象的引用
private Object target;
public DynamicProxy(Object target) {
//持有对真实对象的引用
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before dynamicProxy");
//反射机制调用目标类的方法
method.invoke(target, args);
System.out.println("after dynamicProxy");
return null;
}
}
测试类
public static void main(String[] args) {
DynamicHelloWorld dynamicHelloWorld = new DynamicHelloWorldImps();
InvocationHandler ih = new DynamicProxy(dynamicHelloWorld);
//根据proxy和业务处理逻辑 生成一个代理对象
DynamicHelloWorld dhw = (DynamicHelloWorld) Proxy.newProxyInstance(DynamicHelloWorld.class.getClassLoader(),new Class<?>[]{DynamicHelloWorld.class},ih);
dhw.hello();
}
CGlib动态代理:
CGlib是针对类实现的,采用继承的方式为目标类生成子类,覆盖父类的方法,对方法进行增强,不能对final的类进行代理。
CGlib通过字节码的技术为一个类创建子类,在子类中采用方法拦截的形式拦截所有父类方法的调用,顺势插入横切逻辑。
具体实现类
public class userImpl {
public void say(){
System.out.println("这是一个测试");
}
}
动态代理类:
public class userCglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
//传入目标类
enhancer.setSuperclass(target.getClass());
//设置回调
enhancer.setCallback(this);
//返回代理对象
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object obj = methodProxy.invokeSuper(o,objects);
System.out.println("after");
return obj;
}
}
测试类:
public class test {
public static void main(String[] args) {
userCglibProxy proxy = new userCglibProxy();
userImpl obj = (userImpl)proxy.getInstance(new userImpl());
obj.say();
}
}
JDK动态代理和CGlib动态代理是实现Spring AOP的基础。Spring对于两者进行结合使用,实现Spring AOP。