170829-关于AOP面向切面编程
1.AOP概念:Aspect Oriented Programming 面向切面编程
2.作用:本质上来说是一种简化代码的方式
继承机制
封装方法
动态代理 ……
3.情景举例
①数学计算器接口[MathCalculator]
int add(int i,int j);
int sub(int i,int j);
int mul(int i, int j);
int div(int i,int j);
②提供简单实现[EasyImpl]
③在简单实现的基础上让每一个计算方法都能够打印日志[LoginImpl]
④缺陷
[1]手动添加日志繁琐,重复
[2]统一修改不便
[3]对目标方法本来要实现的核心功能有干扰,使程序代码很臃肿,不易于开发维护
⑤使用动态代理实现
[1]创建一个类,让这个类能够提供一个目标对象的代理对象
[2]在代理对象中打印日志
4.AOP术语![参见图例和doc文档] AOP概述
●AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论, 是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
●参见图例和doc文档解释AOP的各个术语!
●Spring的AOP既可以使用xml配置的方式实现,也可以使用注解的方式来实现!
5.在Spring中使用AOP实现日志功能
①Spring中可以使用注解或XML文件配置的方式实现AOP。
②导入jar包
com.springsource.net.sf.cglib -2.2.0.jar
com.springsource.org.aopalliance-1.0.0 .jar
com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar
commons-logging-1.1.3. jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE. jar
③开启基于注解的AOP功能
< aop:aspectj-autoproxy />
④声明一个切面类,并把这个切面类加入到IOC容器中
@Aspect//表示这是一个切面类
@Component//加入IOC容器
public class LogAspect {}
⑤在切面类中声明通知方法
[1]前置通知:@Before
[2]返回通知:@AfterReturning
[3]异常通知:@AfterThrowing
[4]后置通知:@After
[5]环绕通知:@Around :环绕通知是前面四个通知的集合体!
@Aspect//表示这是一个切面类
@Component//将本类对象加入到IOC容器中!
public class LogAspect {
@Before(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))")
public void showBeginLog(){
System.out.println("AOP日志开始"); } @After(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") public void showReturnLog(){ System.out.println("AOP方法返回"); } @AfterThrowing(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") public void showExceptionLog(){ System.out.println("AOP方法异常"); } @AfterReturning(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") public void showAfterLog(){ System.out.println("AOP方法结束"); } }
⑥被代理的对象也需要加入IOC容器 @Component//加入IOC容器 public class MathCalculatorImpl { public int add(int i,int j){ int result = i+j; return result; } public int sub(int i,int j){ int result = i-j; return result; } public int multi(int i,int j){ int result = i*j; return result; } public int divide(int i,int j){ int result = i/j; return result; } } 6.切入点表达式: 1.上述案例通过junit测试,会发现,我们调用目标类的四个方法只有add方法被加入了4个通知,如果想所有的方法都加上这些通知,可以 在切入点表达式处,将execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 换成: execution(public int com.neuedu.aop.target.MathCalculatorImpl.*(int, int))这样只要是有两个参数,且 参数类型为int的方法在执行的时候都会执行其相应的通知方法! 2.①切入点表达式的语法格式[参见第5章AOP细节] execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表])) 参见第5章AOP细节:演示验证 1.任意参数,任意类型 2.任意返回值 3.用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可! 需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值!
最详细的切入点表达式: execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 最模糊的切入点表达式: execution (* *.*(..)) 7.统一声明切入点表达式 @Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))") public void myPointCut(){} 8.通知方法的细节 ①在通知中获取目标方法的方法名和参数列表 [1]在通知方法中声明一个JoinPoint类型的形参 [2]调用JoinPoint对象的getSignature()方法获取目标方法的签名 [3]调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表 ②在返回通知中获取方法的返回值 [1]在@AfterReturning注解中添加returning属性 @AfterReturning (value="myPointCut()", returning= "result") [2]在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致 showReturnLog(JoinPoint joinPoint, Object result) ③在异常通知中获取异常对象 [1]在@ AfterThrowing注解中添加throwing属性 @AfterThrowing (value="myPointCut()",throwing= "throwable" ) [2]在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致 showExceptinLog(JoinPoint joinPoint, Throwable throwable) 9.根据接口类型获取target对象时,实际上真正放在IOC容器中的对象是代理对象,而并不是目标对象本身!
10.环绕通知:@Around 1.环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数 @Around(value="pointCut()") public void around(ProceedingJoinPoint joinPoint){ } 2.环绕通知会将其他4个通知能干的,自己都给干了! 注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理! @Around(value="pointCut()") public Object around(ProceedingJoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); String methodName = signature.getName(); List<Object> list = Arrays.asList(args); Object result = null; try { //目标方法之前要执行的操作 System.out.println("[环绕日志]"+methodName+"开始了,参数为:"+list); //调用目标方法 result = joinPoint.proceed(args); //目标方法正常执行之后的操作 System.out.println("[环绕日志]"+methodName+"返回了,返回值为:"+result); } catch (Throwable e) { //目标方法抛出异常信息之后的操作 System.out.println("[环绕日志]"+methodName+"出异常了,异常对象为:"+e); throw new RuntimeException(e.getMessage()); }finally{ //方法最终结束时执行的操作! System.out.println("[环绕日志]"+methodName+"结束了!"); } return result; } 11.切面的优先级 对于同一个代理对象,可j以同时有多个切面共同对它进行代理。 可以在切面类上通过@Order (value=50)注解来进行设置,值越小优先级越高! @Aspect @Component @Order(value=40) public class TxAspect { @Around(value="execution(public * com.neuedu.aop.target.MathCalculatorImpl.*(..))") public Object around(ProceedingJoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); String methodName = signature.getName(); List<Object> list = Arrays.asList(args); Object result = null; try { //目标方法之前要执行的操作 System.out.println("[事务日志]"+methodName+"开始了,参数为:"+list); //调用目标方法 result = joinPoint.proceed(args); //目标方法正常执行之后的操作 System.out.println("[事务日志]"+methodName+"返回了,返回值为:"+result); } catch (Throwable e) { //目标方法抛出异常信息之后的操作 System.out.println("[事务日志]"+methodName+"出异常了,异常对象为:"+e); throw new RuntimeException(e.getMessage()); }finally{ //方法最终结束时执行的操作! System.out.println("[事务日志]"+methodName+"结束了!"); } return result; } } 12.注意:上面的AOP都是通过注解实现的,AOP实际上也可以通过xml配置的方式实现!
<!-- 1.将需要加载到IOC容器中的bean配置好 --> <bean id="logAspect" class="com.neuedu.aop.proxy.LogAspect"></bean> <bean id="txAspect" class="com.neuedu.aop.target.TxAspect"></bean> <bean id="calculator" class="com.neuedu.aop.target.MathCalculatorImpl"></bean> <!-- 2.配置AOP,需要导入AOP名称空间 --> <aop:config> <!-- 声明切入点表达式 --> <aop:pointcut expression="execution(* com.neuedu.aop.target.MathCalculatorImpl.*(..))" id="myPointCut"/> <!-- 配置日志切面类,引用前面的类 ,通过order属性控制优先级--> <aop:aspect ref="logAspect" order="25"> <!-- 通过method属性指定切面类的切面方法,通过pointcut-ref指定切入点表达式 --> <aop:before method="showBeginLog" pointcut-ref="myPointCut"/> <aop:after method="showAfterLog" pointcut-ref="myPointCut"/> <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/> <aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/> <aop:around method="around" pointcut-ref="myPointCut"/> </aop:aspect> <!-- 配置事务切面类,引用前面的类 --> <aop:aspect ref="txAspect" order="20"> <aop:around method="around" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config>
需要知道的是:事务的管理是和AOP是有很大关系的,即声明式事务的底层是用事务实现的! 13.批处理(batch)------------>好比快递员【不能一件一件的送快递】 - 批处理指的是一次操作中执行多条SQL语句 - 批处理相比于一次一次执行效率会提高很多
- 批处理主要是分两步: 1.将要执行的SQL语句保存 2.执行SQL语句 - Statement和PreparedStatement都支持批处理操作,这里我们只需要掌握PreparedStatement的批处理方式: - 方法: void addBatch() - 将要执行的SQL先保存起来,先不执行 - 这个方法在设置完所有的占位符之后调用 int[] executeBatch() - 这个方法用来执行SQL语句,这个方法会将批处理中所有SQL语句执行 - mysql默认批处理是关闭的,所以我们还需要去打开mysql的批处理: rewriteBatchedStatements=true 我们需要将以上的参数添加到mysql的url地址中 - 注意:低版本的mysql-jdbc驱动也不支持批处理,一般都是在修改的时候使用批处理,查询的时候不使用! 案例演示: 1.创建一张新的数据表 CREATE TABLE t_emp( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(50) ) 2.反复打开数据库客户端,插入语句【相当于每次获取一个connection连接,执行executeUpdate语句】 INSERT INTO t_emp(NAME) VALUES('张三'); SELECT * FROM t_emp; 3.引出批处理--->执行效率高,资源利用率好!
@Test//测试批处理 public void testBatch(){ //向t_emp表中插入10000条数据 //准备两个变量 Connection connection = null; PreparedStatement ps = null; try { //获取数据库连接 connection=JDBCUtil.getConnection(); //准备SQL模板 String sql = "INSERT INTO t_emp(NAME) VALUES(?)"; //获取PrepareStatement ps = connection.prepareStatement(sql); //创建一个for循环,来设置占位符 for(int i = 0; i < 10000 ;i++){ //填充占位符 ps.setString(1,"emp"+i); //添加到批处理方法中,调用无参的,有参的是Statement来调用的! ps.addBatch(); } //获取一个时间戳 long start = System.currentTimeMillis(); //执行批处理 ps.executeBatch(); //获取一个时间戳 long end = System.currentTimeMillis(); System.out.println("共花费了:"+(end-start)); } catch (SQLException e) { e.printStackTrace(); } }
5) 事务(Transaction) 演示银行转账的功能: 1.创建一张表示账号的表 CREATE TABLE t_account( id INT PRIMARY KEY AUTO_INCREMENT, a_name VARCHAR(50), balance DECIMAL(11,2) ) 2.向表中插入几个用户 INSERT INTO t_account(id,a_name,balance) VALUES(NULL,'sunwukong',1000); INSERT INTO t_account(id,a_name,balance) VALUES(NULL,'zhubajie',1000); INSERT INTO t_account(id,a_name,balance) VALUES(NULL,'shaheshang',1000); SELECT * FROM t_account; 3.#sunwukong向shaheshang转账100元 #从sunwukong的账号减去100元 UPDATE t_account SET balance = balance - 100 WHERE a_name='sunwukong';
#给shaheshang的账号加上100元 UPDATE t_account SET balance = balance +100 WHERE a_name = 'shaheshang'; 重新设置为1000元: UPDATE t_account SET balance =1000; 4.从java代码中演示上面的案例: 1.创建Dao类 public class AcountDao { public void update(String name,double money){ //准备两个变量 Connection conn = null; PreparedStatement ps = null; //准备SQL模板 String sql = "UPDATE t_account SET balance = balance + ? WHERE a_name = ?"; try { conn = JDBCUtil.getConnection(); //获取PreparedStatement ps = conn.prepareStatement(sql); //填充占位符 ps.setDouble(1, money); ps.setString(2, name); //执行SQL语句 ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JDBCUtil.close(conn, ps, null); } } } 2.测试该DAO public class TestTransaction { private AcountDao accountDao = new AcountDao(); @Test public void test() { //从sunwukong账户向shaheshang账户转账100元! //1.从sunwukong账户扣除100元 accountDao.update("sunwukong", -100); //2.向shaheshang账户添加100元 accountDao.update("shaheshang", 100); } } //显然上面是可以正常执行的! 但是如果上面的程序在suwukong减去100元之后,shaheshang加钱之前,出现了异常,如下所示: //从sunwukong账户向shaheshang账户转账100元! //1.从sunwukong账户扣除100元 accountDao.update("sunwukong", -100); int i =10/0;//添加一个异常 //2.向shaheshang账户添加100元 accountDao.update("shaheshang", 100); - 在开发中我们的一个业务往往需要同时操作多个表,这些操作往往是不可分割,业务中的对数据库的多次操作, 要么同时成功,要么全都失败。
- 事务的特性(ACID): 原子性(atomicity) 一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。 一致性(consistency) 事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation) 一个事务的执行不能被其他事务干扰。 即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 持久性(durability) 持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。 接下来的其他操作或故障不应该对其有任何影响。 - 操作事务的基本步骤: 1.开启事务 - 开启事务以后,我们只后的所有操作将都会在同一个事务当中 2.操作数据库 - 开启事务以后再去操作数据库,所有操作将不会直接提交到数据库中 3.提交事务 - 将修改应用到数据库 4.回滚事务 - 数据库操作过程中出现异常了,回滚事务,回滚事务以后,数据库变成开启事务之前的状态 - mysql中的事务控制 #开启事务 START TRANSACTION #回滚事务 ROLLBACK #提交事务 COMMIT - JDBC中的事务主要通过Connection对象来控制的 1.开启事务 void setAutoCommit(boolean autoCommit) throws SQLException; - 设置事务是否自动提交,默认是自动提交 - 设置事务手动提交 conn.setAutoCommit(false); 2.提交事务 void commit() throws SQLException; - 提交事务 conn.commit() 3.回滚事务 void rollback() throws SQLException; - 回滚事务 conn.rollback() - 事务控制的格式: //创建一个Connection Connection conn = null; try{ //获取Connection conn = JDBCUtils.getConnection(); //开启事务 conn.setAutoCommit(false); //对数据库进行操作 //操作成功,提交事务 conn.commit(); }catch(Exception e){ e.printStackTrace(); //回滚事务 try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } }finally{ JDBCUtils.close(conn, null, null); } - 注意:我们在同一个事务中使用的数据库连接(Connection)必须是同一个,否则事务还是不作用! 所以此时原来的AcountDAO中的update方法要改为如下所示: public class AcountDao { public void update(Connection conn,String name,double money){ //准备两个变量 PreparedStatement ps = null; //准备SQL模板 String sql = "UPDATE t_account SET balance = balance + ? WHERE a_name = ?"; try { //获取PreparedStatement ps = conn.prepareStatement(sql); //填充占位符 ps.setDouble(1, money); ps.setString(2, name); //执行SQL语句 ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ //此时也不能在这里关闭数据库连接了,而是在外边统一关闭 JDBCUtil.close(null, ps, null); } } }
170829-关于AOP面向切面编程的更多相关文章
- AOP 面向切面编程, Attribute在项目中的应用
一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...
- AOP面向切面编程的四种实现
一.AOP(面向切面编程)的四种实现分别为最原始的经典AOP.代理工厂bean(ProxyFacteryBean)和默认自动代理DefaultAdvisorAutoProxyCreator以及Bea ...
- Javascript aop(面向切面编程)之around(环绕)
Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...
- Method Swizzling和AOP(面向切面编程)实践
Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...
- [转] AOP面向切面编程
AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...
- C# AOP 面向切面编程之 调用拦截
有时候我们需要在代码中对方法调用进行拦截,并修改参数和返回值,这种操作叫做AOP(面向切面编程) 不过需要注意的是,AOP的效率很慢,在需要高效率场合慎用. 以下是C#的AOP方法: 首先建立一个控制 ...
- 【原创】Android AOP面向切面编程AspectJ
一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...
- 论AOP面向切面编程思想
原创: eleven 原文:https://mp.weixin.qq.com/s/8klfhCkagOxlF1R0qfZsgg [前言] AOP(Aspect-Oriented Programming ...
- 学习笔记: AOP面向切面编程和C#多种实现
AOP:面向切面编程 编程思想 OOP:一切皆对象,对象交互组成功能,功能叠加组成模块,模块叠加组成系统 类--砖头 系统--房子 类--细胞 系统--人 ...
随机推荐
- @Scheduled(cron = "0/5 * * * * *")将时间改为配置
有两种方法:第一种当然你可以把Scheduled写到xml文件中进行配置. 第二种在你的类前面添加 此处讲解第二种写法 第二种在你的类前面添加@PropertySource("classpa ...
- [Web 前端] 025 js 的对象、数组和数学对象
1. Javascript 对象 1.1 创建对象 1.1.1 使用原始的方式创建内置对象 var myObject = new Object(); myObject.name = "lij ...
- java锁的使用
1 synchronize和ReentrantLock synchronize锁是jvm内置的锁,它锁的是synchronize所在的类的对象,要同步那么就只能有一个对象. ReentrantLock ...
- [转帖]解决K8S 安装只有 一直提示:kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1 的方法
Centos7 终端报Message from syslogd :kernel:unregister_netdevice https://www.jianshu.com/p/96d7e2cd9e99 ...
- pg_receivewal实践
测试从pg_receivewal的日志中恢复从库为主库: 主从配置async模式,配置pg_receivewal接收日志pg_receivewal -D /dbaas/pg/data/pg_recei ...
- 用了 10 多年的 Tomcat 居然有bug !
Java技术栈 www.javastack.cn 优秀的Java技术公众号 为了解决分布式链路追踪的问题,我们引入了实现OpenTracing的Jaeger来实现.然后我们为SpringBoot框架写 ...
- Python 入门之Python基础知识
Python 入门之Python基础知识 1.变量 (1)变量就是把程序运行的中间结果临时存在内存中,以便后续代码使用 (2)变量的作用: 昵称,就是代指内存中某个地址中的内容 a = 123 变量名 ...
- HNUSTOJ-1253 Babelfish(字典树)
1253: Problem C: Babelfish 时间限制: 1 Sec 内存限制: 128 MB提交: 14 解决: 3[提交][状态][讨论版] 题目描述 Problem C: Babel ...
- JVM — 性能调优
概念: 一:堆(Heap)和非堆(Non-heap)内存 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.” ...
- Jpa/Hibernate ManyToOne 关联非主键列 延迟加载失效
@ManyToOne配置延迟加载,如果是关联主键列, @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "billid", ...