动态代理
代理
代理概念
-
在开发中存在a类需要调用c类的方法,完成某一个功能,但是c禁止a调用。这时,可以在a和c之间创建一个b类代理,a类访问b类,b类访问c类。例如:登录的时候需要进行短信验证,这个时候代理就是中国移动的子公司来完成短信的发送功能
-
代理模式就是为其他对象提供一种代理来控制这个对象的访问,在某些情况下一个对象不适合或不能直接引用另一个对象,而代理对象可以在客户类和目标对象直接起到中介的作用
代理作用:
-
功能增强
其中目标对象实现真正的功能,但是代理对象可以对目标对象的功能做进一步的扩充。
-
控制权限
限制对目标对象的直接访问
代理分类:
-
静态代理
-
动态代理
静态代理
概念
代理类是自己手动创建的,所需要代理的目标类是确定的,容易实现,理解简单。
实现逻辑:
示例
现在厂家需要卖优盘(不允许少量购买),此时顾客要想购买,不能直接和厂家联系,那么就需要一个商家去代理厂家进行销售。
定义一个卖优盘接口(规范行为)
public interface USBSell {
//卖usb
double sell(int amount);
}
厂家类(实现这个行为)
public class USBFactory implements USBSell {
@Override
//厂家卖优盘,不提供单买,顾客不能直接到厂家买
//根据数量调整价格(单价)
public double sell(int amount) {
double price=80;
if(amount<1000){
price=80;
}else if(amount<2000){
price=75;
}else{
price=70;
}
return price;
}
}
商家代理厂家(同样实现卖优盘这个行为)
public class TaoBao implements USBSell {
//假设商家进货量
private int amount=3000;
//商家代理厂家进行销售优盘
private USBFactory factory=new USBFactory();
//卖优盘,由厂家提供,代理厂家
//商家进行功能增强,提高售价
@Override
public double sell(int amount) {
//厂家单价
double price=factory.sell(amount);
//商家进行功能增强
price=price+16;
return price;
}
public double getPrice(){
return sell(amount);
}
}
顾客购买优盘
public class User {
public static void main(String[] args) {
TaoBao taoBao = new TaoBao();
//用户购买
System.out.println(taoBao.getPrice());
}
结果
86.0
静态代理的缺点
-
当目标类增多了,代理类也需要增加(例如:上例中创建了一个工厂类,那么该类只能代表一个工厂,当建立了其它品牌的工厂后,还需要为该工厂创建代理类)
-
当接口的方法增加或修改的时候,很多类都需要修改。因为,目标类和代理类都实现了相同的接口
动态代理(jdk)
概念
动态代理是利用的反射机制动态地生成代理的对象,我们不需要知道谁代理谁。
优点
代理类的那部分代码被固定下来了,不会因为业务的增加而逐渐庞大。 可以实现AOP编程
AOP
面向切面编程-- 可以通过预编译方式和运行其动态代理实现在不修改源代码的情况下给程序动态统一添加某种特定功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
动态代理所涉及的类
proxy类
它的作用就是用来动态创建一个代理对象,它提供了许多的方法,但是我们主要使用 newProxyInstance 。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
InvocationHandler接口
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
Method 类
我们在反射中会用到这个类
比如:
上面的淘宝类,我们利用反射来执行sell方法
try {
Class<?> taoBao = TaoBao.class;
//可以通过反射来得到所有的方法一级方法的各种信息
Method[] methods = taoBao.getDeclaredMethods();
for(Method m:methods){
System.out.println(m.getName());
for(Type t:m.getGenericParameterTypes()){
System.out.println(t);
}
}
Method method = taoBao.getDeclaredMethod("sell", int.class);
System.out.println(method.invoke(taoBao.newInstance(),3000));
} catch (Exception e) {
e.printStackTrace();
}
结果
sell
int
getPrice
86.0
注:menthod的invoke和InvocationHandler中的invoke的方法是完全不同的。
前者:是当前method对象所得到的对应方法去执行这个得到方法
后者:是在代理对象调用目标方法时会调用所关联的InvocationHandler中的invoke方法
动态代理示例
method.invoke()是用来执行目标方法的
使用Proxy类的静态方法,来创建代理对象,并把返回值转换为接口类型
帮助文档
快捷键目录标题文本样式列表链接代码片表格注脚注释自定义列表LaTeX 数学公式插入甘特图插入UML图插入Mermaid流程图插入Flowchart流程图插入类图
目录复制
使用过程:
-
Inv ocationHandler接口:表示代理要干什么(定义目标类要完成的功能)
-
创建目标类实现接口
-
创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
1.invoke方法:重写invoke方法,把原来静态代理中代理类要完成的功能写在方法内
2.method.invoke()是用来执行目标方法的 -
使用Proxy类的静态方法,来创建代理对象,并把返回值转换为接口类型
定义卖优盘行为的接口
public interface USBSell2 {
//卖usb
double sell(int amount);
}
厂家
public class USBFactory2 implements USBSell2 {
@Override
//厂家卖优盘,不提供单买,顾客不能直接到厂家买
//根据数量调整价格
public double sell(int amount) {
double price=80;
if(amount<1000){
price=80;
}else if(amount<2000){
price=75;
}else{
price=70;
}
return price;
}
}
定义代理中用到的InvocationHandler实现类
//实现InvocationHandler 接口,实现增强功能
class myInvoketion implements InvocationHandler{
private Object target=null;//传入的目标对象
//这里需要传入要代理的对象,传入谁,就位谁动态的创建一个代理对象
public myInvoketion( Object o){
target=o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//可以对参数进行处理
args[0]=(int)args[0]-1100;
//执行目标方法,返回对应的值
Object result = method.invoke(target, args);
//对实际目标方法的结果进行处理
if (result!=null){
Double price=(Double) result;
price=price+16;
result=price;
}
//返回结果,这就是静态代理中得到的结果
return result;
}
}
测试
public static void main(String[] args) {
//目标对象
USBFactory2 usbFactory2=new USBFactory2();
//代理对象
/*
三个参数:类加载器,类的接口数组,InvocationHandler实现类
*/
USBSell2 proxy=(USBSell2) Proxy.newProxyInstance(usbFactory2.getClass().getClassLoader(),
usbFactory2.getClass().getInterfaces(),
new myInvoketion(usbFactory2));
System.out.println(proxy.sell(3002));
}
结果
91.0
缺点
没有接口是无法使用
cglib代理
jdk的动态代理必须有接口,目标类一定要实现接口,没有接口的时候使用cglib动态代理 (引用第三方jar包)
概念
使用目标对象的子类的方式实现的代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展,能够在运行时动态生成字节码,可以解决目标对象没有实现接口的问题。