本文主要讲解内容如下:

  1. Spring的核心之一 - AOP思想

(1) 代理模式- 动态代理

① JDK的动态代理 (Java官方)

② CGLIB 第三方代理

AOP概述

什么是AOP(面向切面编程)

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式运行时动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,进而提高开发效率。

传统Web开发模型: 纵向的编程

面向切面编程: 纵横配合的编程.

AOP的作用及优势

作用:在程序运行期间,不修改任何相关源码对已有方法进行增强。

优势:减少重复代码、提高开发效率、方便后期维护。

AOP的实现方式

使用动态代理模式来实现

可能通过上面的介绍,我们还是没有对AOP有一个清晰的认识。没关系,我们看看下面的具体应用。

案例中问题

通过下面的代码,我们能看出什么问题吗?

模拟事务管理器

public class TransactionMangerHandler {

    public void begin() {
System.out.println("开启事务");
} public void commit() {
System.out.println("提交事务");
} public void rollback() {
System.out.println("回滚事务");
} public void closeSession() {
System.out.println("关闭session");
}
}

Service层代码

public class UserServiceImpl implements UserService {
// 引入事务管理器
private TransactionManagerHandler txManager = new TransactionManagerHandler(); @Override
public void insert() {
try {
txManager.begin();
System.out.println("调用dao层insert方法");
txManager.commit();
} catch (Exception e) {
txManager.rollback();
} finally {
txManager.close();
}
} @Override
public void update() {
try {
txManager.begin();
System.out.println("调用dao层update方法");
txManager.commit();
} catch (Exception e) {
txManager.rollback();
} finally {
txManager.close();
}
}
}

存在的问题

上面代码的问题就是我们的事务控制的代码是重复性的。这还只是一个业务类,如果有多个业务类,每个业务类中都会有这些重复性的代码。是不是重复代码太多了?

通过传统方式来控制业务层的事务,那么会出现大量的冗余代码,例如开启事务、提交事务、事务回滚、关闭资源。这些冗余的代码,降低了开发效率,并且不利于代码后期的维护。我们可以借助AOP思想来集中管理冗余的代码。AOP可以理解为在业务方法的执行前后植入一些增强(事务开始、事务提交、事务回滚、资源关闭),到这里,AOP总的来说就是解决方法内部代码冗余的问题,并且在一定程度上使得方法内部的非业务逻辑代码和业务逻辑代码解耦,提高开发效率、质量的同时方便代码的后期维护!

解决上述问题的方案

  1. JDK的动态代理 - Java官方。

  2. CGLIB动态代理 -第三方组织开源。

  3. Spring的AOP技术(底层就是JDK动态代理和CGLIB代理技术)。

什么是动态代理技术?

Java中的动态代理,就是使用者使用的不是真实的对象,而是使用一个代理对象,而这个代理对象中包含的是真实的对象,代理对象的作用就是不改变原有对象的功能方法的基础之上封装新的功能。可以认为是真实对象的方法在代理对象的某一个制定好规则的方法中执行。如事务管理。

动态代理技术,可以使得业务逻辑代码和非业务逻辑代码相分离,提高了开发效率,控制事务的代码都已经在代理对象的方法中搭建好了,当需要调用真实对象的方法时,那么就会调用代理对象中已经搭建好框架的方法,只需将真实对象的方法植入到框架的指定位置,即可完成真实对象方法的访问。

JDK动态代理

JDK动态代理是Java官方的动态代理技术。

使用JDK官方的Proxy类创建代理对象:

  1. 需要通过Proxy类创建代理对象。

  2. 创建代理对象必须要有一个代理处理类(实现接口InvocationHandler的类)。

JDK动态代理API分析

1、java.lang.reflect.Proxy 类:

Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

主要方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder)

方法职责:为指定类加载器、一组接口及调用处理器生成动态代理类实例

参数:

  • loader: 类加载器
  • interfaces : 模拟的接口
  • hanlder: 代理执行处理器

返回:动态生成的代理对象

2、java.lang.reflect.InvocationHandler接口:

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

方法职责:负责集中处理动态代理类上的所有方法调用

参数:

  • proxy :生成的代理对象
  • method :当前调用的真实方法对象
  • args :当前调用方法的实参

返回: 真实方法的返回结果

3、JDK动态代理操作步骤:

① 实现InvocationHandler接口,创建增强代码处理器。

② 给Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。

③ 在处理器中实现增强操作。

案例代码

在我们的UserServiceImpl实现类中,使用JDK动态代理,进行方法的增强,增强事物的功能。

--UserService&UserServiceImpl

public interface UserService {

    void insert();

    void update();
}
public class UserServiceImpl implements UserService { @Override
public void insert() {
System.out.println("执行了service层的insert方法");
} @Override
public void update() {
System.out.println("执行了service层的update方法");
}
}

--JDK动态代理工具类

/**
* 获取代理对象的工具类
* 增强功能:事务管理
*/
@Component
public class DynamicProxyUtil { // 模拟的事务管理器
@Autowired
private TransactionMangerHandler tx; /**
* 根据类的字节码获取代理对象
* @param cls 被代理类的字节码
* @param <T> 被代理类的类型
* @return 代理对象
*/
public <T> T getProxyObject(Class<T> cls) {
/*
Proxy类是JDK动态代理类
静态方法newProxyInstance用于创建类的代理对象dd
*/
Object proxyObj = Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null; // 代理方法执行结果
try {
tx.begin(); // 开启事务
result = method.invoke(cls.newInstance(), args); // 执行被代理方法
tx.commit(); // 提交事务
} catch (Exception e) {
e.printStackTrace();
tx.rollback(); // 回滚事务
} finally{
tx.closeSession(); // 关闭资源
}
return result; // 返回被代理方法的执行结果
}
});
return (T) proxyObj; // 返回代理对象
}
}

--配置类

@Configuration
public class SpringConfig { @Bean(name = "tx")
public TransactionMangerHandler getTransactionMangerHandler() {
return new TransactionMangerHandler();
} @Bean(name = "dynamicProxyUtil")
public DynamicProxyUtil getDynamicProxyUtil() {
DynamicProxyUtil proxyUtil = new DynamicProxyUtil();
return proxyUtil;
}
}

--测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DynamicProxyTest { @Resource(name = "dynamicProxyUtil")
private DynamicProxyUtil proxyUtil; @Test
public void testInsert() {
// 通过ProxyUtil工具类获取某个类的代理对象
UserService userService = proxyUtil.getProxyObject(UserServiceImpl.class);
userService.insert();
} @Test
public void testUpdate() {
UserService userService = proxyUtil.getProxyObject(UserServiceImpl.class);
userService.update();
}

--测试结果

JDK动态代理的不足

  1. JDK动态代理的对象必须要实现接口,因为JDK动态代理是基于接口代理的。

  2. 需要为每个对象创建代理对象。

  3. 代理粒度大,动态代理的最小单位是类(所有类中的方法都会被处理),但是查询方法不需要事务,因此不需要被代理。

更详细内容参考https://www.cnblogs.com/satire/p/14620634.html

CGLIB动态代理(第三方代理)

CGLIB(Code Generation Library)是一个开源项目。

CGLIB和JDK动态代理一样都是动态代理,但是CGLIB可以代理没有实现接口的类。

CGLIB代理没有实现接口的类时,程序在JVM运行过程中动态的为这个类创建一个子类,并重写父类方法,在调用父类方法时,在方法执行之前、之后、异常、最终做增强。

Spring默认已经集成CGLIB代理,直接可以使用即可,不用拷贝任何jar包。

案例代码

在我们的UserServiceImpl的实现类中,使用CGLIB的动态代理,进行方法的增强,增强事物的功能。

--UserServiceImpl2

public class UserServiceImpl2 {

    public void insert() {
System.out.println("执行了service层的insert方法");
} public void update() {
System.out.println("执行了service层的update方法");
}
}

--CGLIB代理工具类

@Component("cglibProxyUtil")
public class CglibProxyUtil { @Autowired
@Qualifier("tx")
private TransactionMangerHandler tx; /**
* 返回一个代理对象,代理对象就做了方法的增强,(事物管理,日志控制,权限管理等等)
* @param cls 被代理类的字节码
* @return
*/
public <T> T getProxyObject(Class<T> cls) { Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(cls.getClassLoader());
// 设置被代理类的字节码
enhancer.setSuperclass(cls);
// 设置功能增强
/**
* 代理类的增强方法,正常增强的地方
* @param proxy 代理对象
* @param method 被代理对象的方法
* @param args 被代理对象方法的参数
* @return 被代理对象执行的结果
* @throws Throwable
*/
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
Object result = null;
try {
tx.begin();
result = method.invoke(cls.newInstance(), args);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally {
tx.closeSession();
}
return result;
}
});
// 创建代理对象
Object proxyObj = enhancer.create();
return (T) proxyObj;
}
}

--配置类

@Configuration
public class SpringConfig { @Bean(name = "tx")
public TransactionMangerHandler getTransactionMangerHandler() {
return new TransactionMangerHandler();
} @Bean(name = "cglibProxyUtil")
public CglibProxyUtil getDynamicProxyUtil() {
CglibProxyUtil proxyUtil = new CglibProxyUtil();
return proxyUtil;
}
}

--测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class CglibProxyTest { @Resource(name = "cglibProxyUtil")
private CglibProxyUtil proxyUtil; @Test
public void testInsert() {
// 通过ProxyUtil工具类获取某个类的代理对象
UserServiceImpl2 proxyObj = proxyUtil.getProxyObject(UserServiceImpl2.class);
proxyObj.insert();
} @Test
public void testUpdate() {
UserServiceImpl2 proxyObj = proxyUtil.getProxyObject(UserServiceImpl2.class);
proxyObj.update();
}
}

--测试结果

CGLIB代理的不足

  1. CGLIB可以标类的子类,并重写父类非final修饰符的方法。

  2. 要求类不能是final的要代理的方法要是非final、非static、非private的

  3. 动态代理的最小单位是类(所有类中的方法都会被处理)。

小结

解决代码重复的方案

在Spring中:

  • 若目标对象实现了若干接口,Spring就会使用JDK动态代理。

  • 若目标对象没有实现任何接口,Spring就使用CGLIB动态代理。

直接使用代理的缺陷

如果直接使用动态代理技术(JDK代理、CGLIB代理)解决代码重复问题,我们会发现,我们每一个类都要配置代理类,非常的麻烦。

动态代理模式的缺陷是什么

动态代理模式的缺陷是:

  1. 实现类必须要实现接口 -JDK动态代理

  2. 无法通过规则制定拦截无需功能增强的方法

如何解决这个问题?

使用Spring提供了AOP的实现。

【Spring Framework】Spring入门教程(五)AOP思想和动态代理的更多相关文章

  1. 转账示例(四):service层面实现(线程管理Connection,AOP思想,动态代理)(本例采用QueryRunner来执行sql语句,数据源为C3P0)

    用了AOP(面向切面编程),实现动态代理,service层面隐藏了开启事务.1.自行创建C3P0Uti,account数据库,导入Jar包 2.Dao层面 接口: package com.learni ...

  2. spring框架学习(四)AOP思想

    什么是AOP 为什么需要AOP 从Spring的角度看,AOP最大的用途就在于提供了事务管理的能力.事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦),所以,Spring在你访问 ...

  3. 无废话ExtJs 入门教程五[文本框:TextField]

    无废话ExtJs 入门教程五[文本框:TextField] extjs技术交流,欢迎加群(201926085) 继上一节内容,我们在表单里加了个两个文本框.如下所示代码区的第42行位置,items: ...

  4. Spring AOP中的动态代理

    0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  Spring AOP中的动态代理机制 2.1  ...

  5. 转:Spring AOP中的动态代理

    原文链接:Spring AOP中的动态代理 0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  S ...

  6. spring---aop(4)---Spring AOP的CGLIB动态代理

    写在前面 前面介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程. CGLib全称为Code Generation Library,是一个强 ...

  7. Spring Boot实践——Spring AOP实现之动态代理

    Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践——AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...

  8. PySide——Python图形化界面入门教程(五)

    PySide——Python图形化界面入门教程(五) ——QListWidget 翻译自:http://pythoncentral.io/pyside-pyqt-tutorial-the-qlistw ...

  9. Elasticsearch入门教程(五):Elasticsearch查询(一)

    原文:Elasticsearch入门教程(五):Elasticsearch查询(一) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:h ...

随机推荐

  1. 问题 D: 某种序列

    题目描述 数列A满足An = An-1 + An-2 + An-3, n >= 3  编写程序,给定A0, A1 和 A2, 计算A99 输入 输入包含多行数据  每行数据包含3个整数A0, A ...

  2. dotNET开发之MVC中Controller返回值类型ActionResult方法总结

    1.返回ViewResult视图结果,将视图呈现给网页 2. 返回PartialViewResult部分视图结果,主要用于返回部分视图内容 3. 返回ContentResult用户定义的内容类型 4. ...

  3. Spring Cloud Gateway实战之三:动态路由

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. [源码解析] PyTorch分布式(5) ------ DistributedDataParallel 总述&如何使用

    [源码解析] PyTorch 分布式(5) ------ DistributedDataParallel 总述&如何使用 目录 [源码解析] PyTorch 分布式(5) ------ Dis ...

  5. scrapy的安装,scrapy创建项目

    简要: scrapy的安装 # 1)pip install scrapy -i https://pypi.douban.com/simple(国内源) 一步到位 # 2) 报错1: building ...

  6. [Aizu1410]Draw in Straight Lines

    注意到当操作确定后,显然操作顺序总是涂黑色的1操作->涂白色的1操作->2操作 用$b/w_{r/c}(i,j)$表示$(i,j)$是否被黑色/白色 横着/竖着 涂过(1表示涂过,0表示没 ...

  7. 如何在Docker容器中使用Arthas

    Arthas(阿尔萨斯) 能为你做什么? Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱. 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar ...

  8. tabix 操作VCF文件

    tabix 可以对NGS分析中常见格式的文件建立索引,从而加快访问速度,不仅支持VCF文件,还支持BED, GFF,SAM等格式. 下载地址: 1 https://sourceforge.net/pr ...

  9. Vue.js知识点总结

    1. Vue.js简介 1.1 Vue.js简介 1.2 创建一个vue实例 2. Vue.js基础 2.1 模板语法 2.2 环境搭建 2.3 生命周期钩子

  10. rabbit mq的php使用 amqp 的支持

    rabbit mq的php使用 php想要操作rabbit 需要扩展amqp 1,先查看自己的php版本 phpinfo() 接下来下载dll文件 地址http://pecl.php.net/pack ...