1.什么是动态代理?

简单的来说,就是本来让我自己做的事,请给别人来做,这个请的人就是代理对象

那么动态代理就是在程序运行过程中产生这个代理对象,而程序运行中产生的对象就是用反射的来生成一个代理。

举一个例子:

有一个user对象,这个对象有四个方法分别是增,删,改,查。但是外界是不能直接调用这几个方法。你最多能执行查的操作,增、删、改的操作是不能执行的,你必须要加一个权限操作,应该看看你是否有权限执行这个操作。同理,谁操作了这个东西,你需要给我留下记录,免得我不知道是谁做的。所以,我应该在每一个方法的前面加权限校验,在每一个方法的后面加日志记录。

该怎么做呢?

有人说,很简单,直接在user对象的实现类里面去改,在增、删、改查前面加上权限校验,在后面加上日志记录。你能随便改别人的代码吗?你一改,所以用过user对象的地方都要改,这不乱套了吗?

有人说,可以再重新创建一个user对象,在新对象中加上权限校验和日志记录。确实是这样。但是如果我还有一个学生类,还有一个老师类...等等,你每一个都新创建一个对象的话,太麻烦了,而且没有必要,因为对我来说,我只关心对象的增、删、改、查操作,对于权限校验和日志记录我并不关心,这个时候,我们可以找中介来做权限校验和日志记录的事情,这个中介就是动态代理对象!

在java.lang.reflect包下提供一个Proxy类和一个InvocationHandle接口,通过这个类和接口可以实现生成动态代理对象。JDK提供的代理只能针对接口,而cglib则更加强大。

Proxy类的方法创建动态代理对象

Public static Object newProxyInstance(ClassLoader loader, Class<?> interfaces, InvocationHandle h);

最终会调用InvocationHandle的方法invoke

Object invoke(Object proxy, Method method, Object[] args);

2.动态代理的实现

下面用代码实现一下:

创建一个接口,其中有四个方法

/**
* Created by YuKai Fan on 2018/9/25.
*/
public interface UserDao {
public abstract void add();
public abstract void delete();
public abstract void update();
public abstract void find();
}

在创建该接口的实现类

/**
* Created by YuKai Fan on 2018/9/25.
*/
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加");
} @Override
public void delete() {
System.out.println("删除");
} @Override
public void update() {
System.out.println("修改");
} @Override
public void find() {
System.out.println("查找");
}
}

在创建一个类实现InvocationHandle接口

/**
* 该类实现了InvocationHandle接口
* Created by YuKai Fan on 2018/9/25.
*/
public class MyInvocationHandle implements InvocationHandler {
private Object target; //目标对象 public MyInvocationHandle(Object target) {
this.target = target;
} //重写invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("权限校验");
Object invoke = method.invoke(target, args);
System.out.println("日志记录");
return invoke;//返回的就是代理对象
}
}

创建测试类

/**
* Created by YuKai Fan on 2018/9/25.
*/
public class Test {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
userDao.add();
userDao.delete();
userDao.update();
userDao.find();
System.out.println("----------");
//我们要创建一个动态代理对象,在Proxy类中有一个方法可以创建动态代理对象
//public static Object newProxyInstance(ClassLoader loader, Class<?> interfaces, InvocationHandle h);
//将userDao对象作为一个代理对象
MyInvocationHandle myInvocationHandle = new MyInvocationHandle(userDao);
UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), myInvocationHandle);
proxy.find();
proxy.add();
proxy.update();
proxy.delete();
}
}

输出结果为

添加
删除
修改
查找
----------
权限校验
查找
日志记录
权限校验
添加
日志记录
权限校验
修改
日志记录
权限校验
删除
日志记录

以上为JDK动态代理,只能针对接口做代理。我们有更强大的代理cglib。

调用Proxy类中的newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)方法可以创建一个动态代理对象,但是这个方法需要3个参数,前两个参数是固定的,但第三个参数比较麻烦,需要我们创建一个类MyInvocationHandler来实现InvocationHandler接口,这个类里面要重写invoke()方法。

JDK动态代理和cglib动态代理有什么区别? JDK动态代理智能对实现了接口的类生成代理对象; cglib可以对任意类生成代理对象,它的原理是对目标对象进行继承代理,如果目标对象被final修饰,那么该类无法被cglib代理。

3.动态代理的应用

Spring框架的一大特点就是AOP,SpringAOP的本质就是动态代理,而且Spring使用的是JDK与CGlib的混合代理。如果被代理的对象实现了接口,就优先使用jdk代理,如果没有实现接口,就是用cglib代理。

AOP(Aspect-OrientedProgramming,面向切面编程),AOP包括切面(Aspect),通知(Advice),连接点(joinpoint),实现方式就是通过目标对象的代理在连接点前后加入通知,完成统一的切面操作。

实现AOP的技术,主要分为两大类:

一是动态代理,利用截取消息的方式。对消息进行装饰,以取代原有对象的执行。

二是静态织入,引入特定的语法创建“方面”,从而是的编译器可以在编译期间织入有关“方面”的代码。

Spring提供了两种方式来生成代理对象:JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdviseSupport对象的配置来决定。

默认的策略是如果目标类是接口,则使用JDK动态代理技术,如果目标对象没有实现接口,则默认会采用CGLIB代理。

如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true" />)

4.总结

JDK动态代理

1.因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。

2.生成的代理类的所有方法都拦截了目标类的所有方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。

3.利用JDKProxy方法必须有接口的存在

4.invoke方法中的三个参数可以访问目标类的被调用方法的API,别调用方法的参数,被调用方法的返回类型。

cglib动态代理

1.CGlib是一个强大的,高性能,高质量的code生成类库。他可以来运行气扩展Java类与实现Java接口

2.用CGlib生成代理类是目标类的子类

3.用CGlib生成代理类不需要接口

4.用CGlib生成的代理类重写了父类的各个方法

5.拦截器中的intercept方法内容正好就是代理类中的方法体

Spring的两种代理方式

1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

优点:因为有接口,所以使系统更加松耦合

缺点:为每个目标类创建接口

2.若目标对象没有实现任何接口,spring使用cglib库生成目标对象的子类。

优点:因为代理类与目标类是继承关系,所以不需要有接口的存在

缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。

Java基础——动态代理的更多相关文章

  1. java基础--动态代理实现与原理详细分析

    关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 一.代理模式                     ...

  2. java 基础 --- 动态代理和静态代理

    问题  : 代理的应用场景是什么 动态代理的底层原理是什么,为什么只能继承接口 概述 代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法.这种模式在AOP (切面编程)中非 ...

  3. java --- 设计模式 --- 动态代理

    Java设计模式——动态代理 java提供了动态代理的对象,本文主要探究它的实现, 动态代理是AOP(面向切面编程, Aspect Oriented Programming)的基础实现方式, 动态代理 ...

  4. Java:动态代理小记

    Java:动态代理小记 对 Java 中的 动态代理,做一个微不足道的小小小小记 概述 动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理.比如说加日志,加事务等.可以给这个类创建一个代理 ...

  5. java的动态代理机制详解

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

  6. java中动态代理实现机制

    前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...

  7. Java特性-动态代理

    代理在开发中无处不在: 我们完成一个接口开发A,接口下有很多个实现类,这些类有些共同要处理的部分,比如每一个类都定义了接口A中的方法getXX(String name).我现在想把每次调用某个实现类的 ...

  8. java的动态代理机制

    前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...

  9. java中动态代理

    一.在java中怎样实现动态代理 1.我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象 接口: package org.dynamicproxy.test; public ...

随机推荐

  1. 关于unique去重

    嗯.... unique这个东西也是一个冷门知识..... 但是在有时候它还是比较好用的东西... 下面就在详细代码中看unique是如何实际应用的....它主要是用于数组去重 #include< ...

  2. spark_运行spark-shell报错_javax.jdo.JDOFatalDataStoreException: Unable to open a test connection to the given database.

    error: # ./spark-shell Caused by: javax.jdo.JDOFatalDataStoreException: Unable to open a test connec ...

  3. JAVA四大特征:封装,继承,多态,抽象

    1,封装 将对象的属性和方法组合成一个独立的整体,隐藏实现的细节,并提供对外访问的接口. 封装的好处: (1):隐藏实现细节.好比你买了台电视机,你只需要怎么使用,并不用了解其实现原理. (2):安全 ...

  4. python import error:no module named yaml

    解决办法: python2 : sudo apt-get install python-yaml python3 : sudo apt-get install python3-yaml

  5. D. Vitya and Strange Lesson

    http://codeforces.com/contest/842/problem/D 1.整体的数组是不用变的,比如数组a[]经过一次询问x后,然后再询问y,相当于询问x ^ y ^ a[i]后的m ...

  6. discuz迁移到虚拟空间后无法上传图片的问题

    discuz X3迁移到虚拟空间后无法上传图片,提示"附件无法保存": 解决方法: 1.看看虚拟空间的容量是不是满了. 2.登录管理员后台,工具->更新缓存.

  7. 判断两个IP地址是不是属于同一子网的方法

    一个IP地址有三种写法: 第一种,单个IP,如192.168.55.28 第二种,IP/子网掩码,如192.168.55.28/255.255.255.0 第三种,IP/子网掩码长度,如192.168 ...

  8. 4.0.3的mongodb 安装和java使用

    一 整合 由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面. 附上自己的github项目地址 https://github.com/247292980/spring- ...

  9. ORACLE比较两个数据库的表结构

    create table ESPACE_TABLE( TABLE_NAME VARCHAR2(100) not null) create table ESPACE_COLUMN( TABLE_NAME ...

  10. Customers Who Never Order

    Suppose that a website contains two tables, the Customers table and the Orders table. Write a SQL qu ...