什么是代理模式?
所谓的代理者是指一个类别可以作为其它东西的接口。为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
它的组成有:
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
代理模式有什么作用?
1.功能增强:在原有的功能上增加额外的功能
2.控制访问:代理类不让直接访问目标类.例如商家不让用户访问厂家.
如何实现代理模式?
1.静态代理:代理类需要我们自己手动实现,要手写一个JAVA类来作为代理类,同时要代理的目标类是特定的.
2.动态代理:在程序执行过程中通过反射机制,创建代理对象并动态的指定要代理的目标类.
如果要代理的目标类很多,使用动态代理,当修改接口中的方法时,不会影响代理类.
静态代理实现
1.创建一个接口定义要实现的功能
2.定义一个代理类实现功能接口
3.定义一个目标类也实现功能接口
4.在目标类中进行功能实现,将目标类注入到代理类中,代理类调用目标类中的方法,然后进行功能增强.
5.定义一个客户类来调用代理类中的方法
代理类中要做的的事情:调用目标类方法和功能增强.
缺点:
目标类增加后,代理类也会增加.
接口类修改后影响的类比较多.
动态代理
特点:不用创建代理类,可以给不同的目标随时创建代理
实现方式:
1.JDK动态代理实现:使用JDK反射包中的类和接口实现动态代理.反射包java.long.reflect中的三个类:invocationHandler,method,Porxy
2.CGLIB动态代理:CGLIB是一个第三方的工具库,它的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改.(因为cglib是通过继承重写方法实现动态代理,所以目标类和目标类中的方法不能是final修饰的.)使用cglib实现动态代理对目标类的要求就是能继承就行,它在很多框架中使用.
要了解动态代理我们首先要了解反射和Method类
一般我们执行某个方法时,是通过new一个对象,然后通过对象名.方法名的方式来执行.
其实也可以通过反射来执行某个对象的方法.
A类中有个方法f(String name);
第一步:创建对象 A a = new A();
第二步:获取A类中方法f(String name)的对应的Method类对象
Method method = a.class.getMethod(“f1”,String.class);//这里传入的是方法名和需要传入的参数类型的class.
第三步:通过Method中的invoke()方法可以执行f1()方法.
参数:Object:要执行的方法所属于的对象
Object… args:执行时候的参数.
Object ret = method.invoke(a,“李明”);//表示执行a对象的f1()方法,参数是一个名字.
JDK实现动态代理
反射包java.lang.reflact里面有三个类InvocationHandler,Method,Porxy.
1.InvocationHandler接口(调用处理器):这个接口中就一个没有实现的方法invoke();
invoke():表示代理对象要执行的功能代码即你的代理类要完成的功能代码要写在这个方法中.主要是调用目标方法和功能增强.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
这个方法有三个参数:
- Object proxy:jdk的代理对象,无需我们自己操作,JDK自动生成.
- Method method:目标类中的方法,jdk提供,也是JDK来提供的.
- Object[] args:目标类方法中的参数,JDK赋值.
2.Method类:表示方法,确切的说就是目标类中的方法.
作用:通过Method可以执行某个目标类的方法,Method.invoke();
method.invoke(目标对象,方法参数);
说明:method.invoke()就是来执行目标方法的,等同于静态代理中的调用目标类中方法.
3.Proxy类:核心的对象,创建代理对象.使用proxy中的方法来代替new的过程.
方法:静态方法 newProxyInstance()
作用:创建代理对象,相当于我们静态代理中用new的方式创建代理对象的过程.
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
三个参数:
- ClassLoader loader:类加载器,负责向内存中加载对象(这里是目标对象的类加载器).使用反射获取对象的ClassLoader,例如a.getClass().getClassLoader();
- Class<?>[] interfaces:接口,目标对象所实现的接口,也是用反射获取
- InvocationHandler h:我们自己写的,代理类要完成的功能.
如何实现JDK动态代理
1.创建接口,定义目标类要实现的功能
//租房子的接口
public interface RentService {
public float Rent(String name,int count);
}
2.创建目标类实现接口
//租房子接口的实现类
public class Landlord implements RentService {
@Override
public float Rent(String name, int count) {
System.out.println(name+"租了"+count+"个月房子!");
return 1000.0f*count;
}
}
3.创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能(掉用目标方法,增强功能)
@SuppressWarnings("all")
/*
InvocationHandler接口的实现类
*/
public class MyPorxy implements InvocationHandler {
//目标对象
private Object target = null;
public MyPorxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
res = method.invoke(target, args);
if (res != null){
Float price = (float)res;
System.out.println("收取中介费半个月房租"+500+"元!");
price += 500;
res = price;
}
return res;
}
}
4.使用Porxy的静态方法,创建代理对象,并把返回值转换为接口类型.
public class test {
public static void main(String[] args) {
//创建目标对象
RentService rentService = new Landlord();
//创建InvocationHandler对象
InvocationHandler handler = new MyPorxy(rentService);
//创建代理对象
RentService porxy = (RentService) Proxy.newProxyInstance(rentService.getClass().getClassLoader(), rentService.getClass().getInterfaces(), handler);
float price = porxy.Rent("黎明", 3);
System.out.println(price);
}
}