一、代理模式

代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

以简单模拟事务的执行过程说明各种代理区别

1.1 静态代理

由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

1
2
3
4
public interface PersonDao {
 
 void savePerson();
}
1
2
3
4
5
6
7
public class PersonDaoImpl implements PersonDao {
 
 @Override
 public void savePerson() {
  System.out.println("save person");
 }
}
1
2
3
4
5
6
7
8
9
10
public class Transaction {
  
 void beginTransaction(){
  System.out.println("begin Transaction");
 }
  
 void commit(){
  System.out.println("commit");
 }
}

接下来编写静态代理类---实现PersonDao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 静态代理类
 * @author qjc
 */
public class PersonDaoProxy implements PersonDao{
 
 PersonDao personDao;
 Transaction transaction;
  
 public PersonDaoProxy(PersonDao personDao, Transaction transaction) {
  this.personDao = personDao;
  this.transaction = transaction;
 }
 
 @Override
 public void savePerson() {
  this.transaction.beginTransaction();
  this.personDao.savePerson();
  this.transaction.commit();
 }
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * 测试静态代理
 * @author qjc
 */
public class TestPersonProxy {
  
 @Test
 public void testSave(){
  PersonDao personDao = new PersonDaoImpl();
  Transaction transaction = new Transaction();
  PersonDaoProxy proxy = new PersonDaoProxy(personDao, transaction);
   
  proxy.savePerson();
 }
}

总结:

1、静态代理模式并没有做到事务的重用

2、假设dao有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务

3、如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应改变

1.2 JDK动态代理

动态代理类:在程序运行时,运用反射机制动态创建而成。

JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类

使用上个例子的PersonDao接口、PersonDaoImpl类及Transaction类

编写拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
/**
 * 拦截器
 *   1、目标类导入进来
 *   2、事物导入进来
 *   3、invoke完成:开启事务、调用目标对象的方法、事务提交
 *
 * @author qjc
 */
public class Interceptor implements InvocationHandler {
 
 private Object target; // 目标类
 private Transaction transaction;
 
 public Interceptor(Object target, Transaction transaction) {
  this.target = target;
  this.transaction = transaction;
 }
 
 /**
  * @param proxy 目标对象的代理类实例
  * @param method 对应于在代理实例上调用接口方法的Method实例
  * @param args 传入到代理实例上方法参数值的对象数组
  * @return 方法的返回值,没有返回值是null
  * @throws Throwable
  */
 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  String methodName = method.getName();
  if ("savePerson".equals(methodName)
    || "deletePerson".equals(methodName)
    || "updatePerson".equals(methodName)) {
 
   this.transaction.beginTransaction(); // 开启事务
   method.invoke(target); // 调用目标方法
   this.transaction.commit(); // 提交事务
 
  } else {
   method.invoke(target);
  }
  return null;
 }
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 测试jdk动态代理
 * @author qjc
 */
public class TestJDKProxy {
  
 @Test
 public void testSave(){
  /**
   * 1、创建一个目标对象
   * 2、创建一个事务
   * 3、创建一个拦截器
   * 4、动态产生一个代理对象
   */
  Object target = new PersonDaoImpl();
  Transaction transaction = new Transaction();
  Interceptor interceptor = new Interceptor(target, transaction);
  /**
   * 参数一:设置代码使用的类加载器,一般采用跟目标类相同的类加载器
   * 参数二:设置代理类实现的接口,跟目标类使用相同的接口
   * 参数三:设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
   */
  PersonDao personDao = (PersonDao) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    interceptor);
  personDao.savePerson();
 }
}

总结

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

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

3、利用JDKProxy方式必须有接口的存在。

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

缺点:

1、在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务

2、拦截器中的invoke方法的if判断语句在真实的开发环境下是不靠谱的,因为一旦方法很多if语句需要写很多。

1.3 CGLIB动态代理

使用上个例子的PersonDaoImpl类和Transaction类(不用接口)

编写拦截器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
/**
 * CGLIB代理 拦截器
 * @author qjc
 */
public class Interceptor implements MethodInterceptor {
 
 private Object target; // 代理的目标类
 private Transaction transaction;
 
 public Interceptor(Object target, Transaction transaction) {
  this.target = target;
  this.transaction = transaction;
 }
 
 /**
  * 创建目标对象的代理对象
  *
  * @return
  */
 public Object createProxy() {
  // 代码增强
  Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象
  enhancer.setCallback(this); // 参数为拦截器
  enhancer.setSuperclass(target.getClass());// 设置父类
  return enhancer.create(); // 创建代理对象
 }
 
 /**
  * @param obj 目标对象代理类的实例
  * @param method 代理实例上 调用父类方法的Method实例
  * @param args 传入到代理实例上方法参数值的对象数组
  * @param methodProxy 使用它调用父类的方法
  * @return
  * @throws Throwable
  */
 public Object intercept(Object obj, Method method, Object[] args,
   MethodProxy methodProxy) throws Throwable {
  this.transaction.beginTransaction();
  method.invoke(target);
  this.transaction.commit();
  return null;
 }
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * 测试cglib动态代理
 * 通过cglib产生的代理对象,代理类是目标类的子类
 * @author qjc
 */
public class TestCglibProxy {
  
 @Test
 public void testSave(){
  
  Object target = new PersonDaoImpl();
  Transaction transaction = new Transaction();
  Interceptor interceptor = new Interceptor(target, transaction);
   
  PersonDaoImpl personDaoImpl = (PersonDaoImpl) interceptor.createProxy();
  personDaoImpl.savePerson();
 }
}

总结:

1、CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。

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

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

4、用CGLib生成的代理类重写了父类的各个方法。

5、拦截器中的intercept方法内容正好就是代理类中的方法体 CGLIB和JDK动态代理区别:

JDK:

目标类和代理类实现了共同的接口

拦截器必须实现InvocationHandler接口,而这个接口中invoke方法体的内容就是代理对象方法体的内容

CGLIB:

目标类 是代理类的父类

拦截器必须实现MethodInterceptor接口,而接口中的intercept方法就是代理类的方法体,使用字节码增强机制创建代理对象的.

二、面向切面编程

OOP(面向对象编程):封装、继承、多态、抽象

封装,对代码进行基本的管理、模块化的管理。每个类可能都有自己的职能,出了问题就是论事找人就行了。从修改角度讲,直接修改代码可能有风险,这不是个长远之计,最自然的是从类型封装变化。但是新的类型和旧的体系之间怎么去融合,所以说需要在类与类之间建立一种血缘关系。那么这就是继承的需求,通过继承就可以发现这些类之间是有关联的,它们之间是有父子关系的。然后在继承基础之上多态起决定性的特征。所以说一般认为面向对象最核心的特征,其实是多态。前面几个都是在做铺垫的。多态才是它最核心的特征。子类中通过重写方法,代表了扩展这个层面的东西,而它能融入老的体系中能够正常工作,这是重用这个层面的东西,新的方法、旧的体系、扩展和重用。

AOP(面向切面编程):

面向切面编程,是一种通过预编译方式运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.

OOP与AOP区别:

  OOP:针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清楚的逻辑单元划分。

  AOP:针对业务处理过程中的横切逻辑 进行提取,它所面对的是处理过程中的某个步骤或者阶段,以获得逻辑过程中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。AOP做到了代码块的重用。

spring AOP代理机制:

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

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

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

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

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

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

使用第一个例子的 PersonDao接口、PersonDaoImpl类和Transaction类

编写spring配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="personDao" class="cn.qjc.aop.xml.PersonDaoImpl"></bean>
 <bean id="transaction" class="cn.qjc.aop.xml.Transaction"></bean>
  
 <aop:config>
  <!-- 切入点表达式 确定目标类 -->
  <aop:pointcut expression="execution(* cn.qjc.aop.xml.PersonDaoImpl.*(..))" id="perform"/>
  
  <!-- ref指向对象就是切面 -->
  <aop:aspect ref="transaction">
   <aop:before method="beginTransaction" pointcut-ref="perform"/>
   <aop:after-returning method="commit" pointcut-ref="perform"/>
  </aop:aspect>
 </aop:config>
  
</beans>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 测试spring动态代理
 * @author qjc
 */
public class TransactionTest {
 
 @Test
 public void testSave(){
  ApplicationContext context = new ClassPathXmlApplicationContext("cn/qjc/aop/xml/applicationContext.xml");
  PersonDao personDao = (PersonDao) context.getBean("personDao");
  personDao.savePerson();
 }
}

spring AOP原理

1、当spring容器启动的时候,加载两个bean,对像个bean进行实例化
2、当spring容器对配置文件解析到<aop:config>的时候,把切入点表达式解析出来,按照切入点表达式匹配spring容器内容的bean
3、如果匹配成功,则为该bean创建代理对象
4、当客户端利用context.getBean获取一个对象时,如果该对象有代理对象,则返回代理对象,如果没有代理对象,则返回对象本身

spring基础概念AOP与动态代理理解的更多相关文章

  1. 【spring 4】AOP:动态代理

    一.动态代理简介 动态代理与普通代理相比较,最大的好处是接口中声明的所有方法都被转移到一个集中的方法中处理(invoke),这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那 ...

  2. Spring核心框架 - AOP之动态代理机制

    动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码.动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类. ...

  3. 【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】

    一.cglib动态代理 1.简介 (1)CGlib是一个强大的,高性能,高质量的Code生成类库.它可以在运行期扩展Java类与实现Java接口. (2) 用CGlib生成代理类是目标类的子类. (3 ...

  4. 浅谈Spring的AOP实现-动态代理

    说起Spring的AOP(Aspect-Oriented Programming)面向切面编程大家都很熟悉(Spring不是这次博文的重点),但是我先提出几个问题,看看同学们是否了解,如果了解的话可以 ...

  5. Spring基础系列-AOP源码分析

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...

  6. AOP与动态代理有什么联系

    曾遇到“AOP与动态代理有什么联系”的问题,现把个人观点整理如下: 我觉得,动态代理是AOP的主要实现手段之一,AOP是动态代理的一种应用深化 AOP是一种思想,或者是方法论,类似OOP,是OOP的有 ...

  7. Spring基础系列--AOP织入逻辑跟踪

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9619910.html 其实在之前的源码解读里面,关于织入的部分并没有说清楚,那些前置.后 ...

  8. 【spring基础】AOP概念与动态代理详解

    一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

  9. Java基础---Java---基础加强---类加载器、委托机制、AOP、 动态代理技术、让动态生成的类成为目标类的代理、实现Spring可配置的AOP框架

    类加载器 Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader 类加载器也是Jav ...

随机推荐

  1. listen 58

    Different Brain Regions Handle Different Music Types (Vivaldi) versus (the Beatles) . Both great. Bu ...

  2. unity3d mvvm c#

    using UnityEngine; using System.Collections; public interface IState { void BeforEnter(); void Befor ...

  3. 1091 Acute Stroke (30)(30 分)

    One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the re ...

  4. 监测GPU使用情况命令

    每2秒监测一次:watch -n 2 nvidia-smi

  5. AtCoder Grand Contest 009 D:Uninity

    题目传送门:https://agc009.contest.atcoder.jp/tasks/agc009_d 题目翻译 定义只有一个点的树权值为\(0\),若干棵(可以是\(0\)棵)权值为\(k\) ...

  6. DSP编程

    File:isctype.c Line 68 DSP/BIOS程序启动顺序 CCS V5 使用教程一: 安装激活与创建工程 CCS V5 使用教程二:创建工程和配置软件仿真 CCS V5 使用教程三: ...

  7. 六 Vue学习 首页 (下)

    一:Store介绍: state: 相当于数据 action: action去commit mutations mutation: 只有mutation 才能改变state 例: const stor ...

  8. TCP 协议的消息

    TCP: SYN ACK FIN RST PSH URG 详解 TCP的三次握手是怎么进行的了:发送端发送一个SYN=1,ACK=0标志的数据包给接收端,请求进行连接,这是第一次握手:接收端收到请求并 ...

  9. CentOS6下安装Java jdk1.7.0_10和 maven

    安装步骤如下: 1. 下载JDK7.0_10 (jdk-7u10-linux-i586.tar.gz) 地址: 2. 卸载系统自带的开源JDK 查看是否安装JDK rpm -qa | grep jav ...

  10. iis部署错误:HTTP 错误 500.21 - Internal Server Error

    将网站发布到IIS,访问发生如下错误: HTTP 错误 500.21 - Internal Server Error处理程序“PageHandlerFactory-Integr”在其模块列表中有一个错 ...