Spring入门详细教程(三)
前言
本篇紧接着spring入门详细教程(二),建议阅读本篇前,先阅读第一篇和第二篇。链接如下:
Spring入门详细教程(一) https://www.cnblogs.com/jichi/p/10165538.html
Spring入门详细教程(二) https://www.cnblogs.com/jichi/p/10176601.html
本篇主要讲解spring的aop相关。
一、aop的概念
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
AOP主要实现功能日志记录,性能统计,安全控制,事务处理,异常处理等等。将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
AOP主要思想总结为横向重复,纵向抽取。
二、spring实现aop的原理及底层实现
spring实现aop的底层使用了两种代理机制。一种是jdk的动态代理,一种是cglib的动态代理。下面来分析一下两种代理模式。
1、jdk的动态代理
被代理对象必须要实现接口才能产生代理对象,如果被代理对象不能实现接口,则这种方式的动态代理技术无效。
接下来做一个底层代码的编写来进行理解。
(1)首先jdk的动态代理要求被代理对象必须实现接口。我们准备一个接口以及一个接口的实现类。
public interface UserDao {public void saveUser();
}
public class UserDaoImpl implements UserDao {public void saveUser(){System.out.println("保存用户");}
}
(2)建立一个UserDao的动态代理类,实现接口InvocationHandler。
public class UserProxy implements InvocationHandler{private UserDao userDao ;public UserProxy(UserDao userDao) {this.userDao = userDao;}public UserDao createProxy(){UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(), this);return userDaoProxy;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("动态代理");return method.invoke(userDao, args);}}
(3)进行单元测试,发现第一个方法执行的时候没有被动态代理,第二个执行的时候进行了动态代理。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJunit {@Testpublic void test3(){UserDaoImpl userDaoImpl = new UserDaoImpl();userDaoImpl.saveUser();UserProxy userProxy = new UserProxy(userDaoImpl);UserDao createProxy = userProxy.createProxy();createProxy.saveUser();}
}
2、cglib动态代理
针对一些不能实现接口的代理对象产生代理,可以对没有被final修饰的任何对象进行继承代理,其底层应用的是字节码增强的技术,生成代理对象的子类对象。如果被final修饰,类不可继承,便不可使用cglib动态代理。
(1)创建一个cglib动态代理对象实现接口。
public class CglibProxy implements MethodInterceptor{private UserDaoImpl userDaoImpl;public CglibProxy(UserDaoImpl userDaoImpl) {this.userDaoImpl = userDaoImpl;}public UserDaoImpl createProxy(){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserDaoImpl.class);enhancer.setCallback(this);UserDaoImpl udi = (UserDaoImpl) enhancer.create();return udi;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object obj = methodProxy.invokeSuper(proxy, args);System.out.println("动态代理");return obj;}}
(2)进行单元测试。
public void test4(){UserDaoImpl userDaoImpl = new UserDaoImpl();userDaoImpl.saveUser();CglibProxy cglib = new CglibProxy(userDaoImpl);UserDaoImpl userDaoImpl2 = cglib.createProxy();userDaoImpl2.saveUser();}
可以发现第一个saveUser没有执行动态代理,第二个执行了动态代理。
结论:两种代理技术针对不同情况,互相弥补,从而使任何对象都可以实现动态代理。spring在进行aop的时候,默认使用jdk的动态代理技术,当发现jdk的动态代理技术不好使的情况下,使用cglib动态代理技术,保证被代理对象能够被正常代理。如需使用cglib动态代理可以再spring的配置文件中进行配置。
<aop:config proxy-target-class="true">
三、aop开发中的相关概念
1、Joinpoint(连接点):目标对象中,所有可以增强的方法。
2、Pointcut(切入点):目标对象中,已经增强的方法。
3、Advice(通知):对于目标对象来说,需要给目标对象增强的方法。
4、Target(目标对象):被代理对象。
5、Weaving(织入):将通知应用到切入点的过程。
6、Proxy(代理):将通知织入到目标对象后,形成的增强后的对象。
7、Aspect(切面):切入点和通知的结合。
四、spring中aop的实现方式
分两种方式介绍,一种是xml配置方式,一种是注解方式。
1、xml配置方式
(1)实现spring的aop需要导入aop包,aspect包,aopalliance包,weaver包。在spring教程一中可以找到获取这些包的方法。
(2)编写需要增加的方法类。
public class UserDaoImpl{public void saveUser(){System.out.println("保存用户");}public void deleteUser(){System.out.println("删除用户");}
}
(3)编写通知,也就是说想要增加的代码方法。
public class UserAdvice{public void before(){System.out.println("前置通知");}public void afterReturning(){System.out.println("后置通知(不发生异常的情况下调用)");}public Object around(ProceedingJoinPoint pjp) throws Throwable{System.out.println("执行前");Object proceed = pjp.proceed();System.out.println("执行后");return proceed;}public void afterThrowException(){System.out.println("发生异常调用");}public void after(){System.out.println("后置通知,发生异常也会调用");}
}
(4)在spring的配置文件中进行配置
<bean name = "userDaoImpl" class="com.jichi.aop.UserDaoImpl"></bean><bean name="userAdvice" class="com.jichi.aop.UserAdvice"></bean><aop:config><aop:pointcut expression="execution(* com.jichi.aop..UserDaoImpl.*(..))" id="pc"/><aop:aspect ref="userAdvice"><aop:before method="before" pointcut-ref="pc"/><aop:after-returning method="afterReturning" pointcut-ref="pc"/><aop:around method="around" pointcut-ref="pc"/><aop:after-throwing method="afterThrowException" pointcut-ref="pc"/><aop:after method="after" pointcut-ref="pc"/></aop:aspect></aop:config>
(5)进行单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAop {@Resourceprivate UserDaoImpl userDaoImpl;@Testpublic void test1(){userDaoImpl.saveUser();}
}
结果如下:织入成功。
2、注解配置方式
(1)同第一种方式需要导入包
(2)编写需要增加的方法类。
public class UserDaoImpl{public void saveUser(){System.out.println("保存用户");}public void deleteUser(){System.out.println("删除用户"); } }
(3)编写通知,也就是说想要增加的代码方法。
public class UserAdvice{public void before(){System.out.println("前置通知");}public void afterReturning(){System.out.println("后置通知(不发生异常的情况下调用)"); } public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("执行前"); Object proceed = pjp.proceed(); System.out.println("执行后"); return proceed; } public void afterThrowException(){ System.out.println("发生异常调用"); } public void after(){ System.out.println("后置通知,发生异常也会调用"); } }
(4)在spring配置文件中进行配置,并开启注解aop
<bean name = "userDaoImpl" class="com.jichi.aop.UserDaoImpl"></bean><bean name="userAdvice" class="com.jichi.aop.UserAdvice"></bean><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(5)在通知类上打上aspect的注解。在方法上打上相应注解
@Aspect
public class UserAdvice{@Before("execution(* com.jichi.aop..UserDaoImpl.*(..))")public void before(){System.out.println("前置通知");}@AfterReturning("execution(* com.jichi.aop..UserDaoImpl.*(..))")public void afterReturning(){System.out.println("后置通知(不发生异常的情况下调用)");}@Around("execution(* com.jichi.aop..UserDaoImpl.*(..))")public Object around(ProceedingJoinPoint pjp) throws Throwable{System.out.println("执行前");Object proceed = pjp.proceed();System.out.println("执行后");return proceed;}@AfterThrowing("execution(* com.jichi.aop..UserDaoImpl.*(..))")public void afterThrowException(){System.out.println("发生异常调用");}@After("execution(* com.jichi.aop..UserDaoImpl.*(..))")public void after(){System.out.println("后置通知,发生异常也会调用");}
}
优化方式:每个方法都配置方法抽取,显得比较臃肿,可以进行提取,方法如下
@Aspect
public class UserAdvice{@Pointcut("execution(* com.jichi.aop..UserDaoImpl.*(..))")public void adc(){}@Before("UserAdvice.adc()")public void before(){System.out.println("前置通知");}@AfterReturning("UserAdvice.adc()")public void afterReturning(){System.out.println("后置通知(不发生异常的情况下调用)");}@Around("UserAdvice.adc()")public Object around(ProceedingJoinPoint pjp) throws Throwable{System.out.println("执行前");Object proceed = pjp.proceed();System.out.println("执行后");return proceed;}@AfterThrowing("UserAdvice.adc()")public void afterThrowException(){System.out.println("发生异常调用");}@After("UserAdvice.adc()")public void after(){System.out.println("后置通知,发生异常也会调用");}
}
Spring入门详细教程(三)相关推荐
- spring入门详细教程(五)
前言 本篇紧接着spring入门详细教程(三),建议阅读本篇前,先阅读第一篇,第二篇以及第三篇.链接如下: Spring入门详细教程(一) https://www.cnblogs.com/jichi/ ...
- Spring入门详细教程(四)
前言 本篇紧接着spring入门详细教程(三),建议阅读本篇前,先阅读第一篇,第二篇以及第三篇.链接如下: Spring入门详细教程(一) https://www.cnblogs.com/jichi/ ...
- Spring入门详细教程(二)
前言 本篇紧接着spring入门详细教程(一),建议阅读本篇前,先阅读第一篇.链接如下: Spring入门详细教程(一) https://www.cnblogs.com/jichi/p/1016553 ...
- Spring入门详细教程(一)
一.spring概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的 ...
- ThinkJS框架入门详细教程(二)新手入门项目
一.准备工作 参考前一篇:ThinkJS框架入门详细教程(一)开发环境 安装thinkJS命令 npm install -g think-cli 监测是否安装成功 thinkjs -v 二.创建项目 ...
- NMAP入门详细教程
NAMP入门详细教程 一.功能: 网络扫描和嗅探. 二.原理: 使用TCP/IP协议栈指纹准确地判断目标主机的相关信息. 三.作用: 识别活跃主机 识别开放端口以及相关的服务 识别主机的系统指纹 路由 ...
- Pandas入门详细教程
作者:luanhz 来源:小数志 导读 本文主要是对pandas进行入门详细介绍,通过本文你将系统性了解pandas为何会有数据分析界"瑞士军刀"的盛誉. 行文二级目录 01 关于 ...
- python数据科学系列:pandas入门详细教程
导读 前2篇分别系统性介绍了numpy和matplotlib的入门基本知识,今天本文自然是要对pandas进行入门详细介绍,通过本文你将系统性了解pandas为何会有数据分析界"瑞士军刀&q ...
- Spring认证中国教育管理中心-Spring Data Neo4j教程三
原标题:Spring认证中国教育管理中心-Spring Data Neo4j教程三(Spring中国教育管理中心) 6.2.处理和提供唯一 ID 6.2.1.使用内部 Neo4j id 为您的域类提供 ...
最新文章
- 【深度学习】图像输入网络必要的处理流程
- java.util.concurrent介绍
- 【渗透测试】一次授权的渗透测试——建议收藏
- 对话诺奖得主:科学离不开国际合作
- 用Python标准库turtle画一只老虎,祝您新年虎虎生威,大吉大利
- 7年,OpenStack从入门到放弃
- 明星开餐厅十店九亏?明星靠“卖面子”能撑多久?
- Mac 上 QuickTime Player 播放器以 1.1、1.2 倍速等更精确速度快进/快退播放的方法
- oracle中字符串长度计算,根据 oracle 标准计算超长字符串的长度
- 美化版缤纷彩色文字广告代码文字+网站添加AD教程
- 鼠标移动文字上显示图片
- 传感器技术—新型光电传感器(学习笔记十 补充)
- mysqldb 安装包 linux,Linux下Python MySQLdb模块安装过程及错误解决
- ElasticSearch 索引创建
- 新电脑自带的office密钥在哪里?
- 后缀自动机+DP BZOJ 3238 差异
- 8除以2表示什么意思_8除以2等于4表示什么
- 如何消除USB共享网络导致的Windows系统中自动增加的网络设备序列号?
- 解决Vue路由重复跳转报错
- 【OpenPose-Windows】OpenPose1.4.0+VS2017+CUDA9.2+cuDNN9.2+Windows配置教程
热门文章
- ApplicationStartedEvent 与 ContextStartedEvent 有区别吗?
- 技术前沿:Redis推出性能碾压ES和Mongo的大杀器
- 13KB的代码能做什么?有些人可是弄出了一个完整的游戏!
- 皮一皮:这车是要开上天啊...
- 皮一皮:一个戒指吃出了电视剧的感觉...
- 皮一皮:颜值的重要。。。
- 每日一皮:你不知道你的骑手为了给你送餐要经历什么...
- GitHub官方开源新命令行工具
- php 的ob start,php ob_start()函数详解
- python 操作fit文件图像