spring笔记--使用springAPI以及自定义类 实现AOP的一个例子
Spring的另一个重要思想是AOP,面向切面的编程,它提供了一种机制,可以在执行业务前后执行另外的代码,Servlet中的Filter就是一种AOP思想的体现,下面通过一个例子来感受一下.
假设我们现在需要在针对数据库进行CRUD操作时添加一组日志,即在执行CRUD方法前后分别加上一句话,实现简单的面向切面编程的功能.我用到的是spring4,在配置文件上较之之前的版本可能有些不同.
使用springAPI来实现AOP,除了spring必不可少的核心jar包,还需要两个jar包需要导入:
- aspectjweaver.jar 下载链接: http://download.csdn.net/detail/luojiming1990/5432831
- aopalliance.jar 下载链接:http://download.csdn.net/detail/zhaoshe/3153090
并且配置文件的头文件也需要略作修改,需要加入aop的命名空间(namespace),详见下面实例中的beans.xml.
UserService类(省略数据库操作代码,只做简单的打印来模拟):
public class UserService { public void add(){
System.out.println("添加用户");
}
public void delete(){
System.out.println("删除用户");
}
public void update(){
System.out.println("修改用户");
}
public void search(){
System.out.println("查询用户");
}
}
Log类(在执行增删改查方法前执行):
public class Log implements MethodBeforeAdvice{ /***
* method:被调用的方法对象
* arg1:被调用的方法的参数
* target:被调用方法的目标对象
*/
@Override
public void before(Method method, Object[] arg1, Object target)
throws Throwable {
System.out.println(target.getClass().getName()+"中的"+method.getName()+"方法被执行");
} }
AfterLog类(在执行增删改查方法后执行):
public class AfterLog implements AfterReturningAdvice{
/**
* returnValue:返回值类型
* method:被调用的方法对象
* arg1:被调用的方法的参数
* target:被调用方法的目标对象
*/
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"中的"+method.getName()+"方法被执行成功,返回值是"+returnValue); }
spring配置文件(beans.xml):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.wang.service.UserService"></bean>
<bean id="log" class="com.wang.log.Log"></bean>
<bean id="afterlog" class="com.wang.log.AfterLog"></bean>
<aop:config>
<!--"*"为通配符表示所有方法,第一个* 表示任意返回值 第二个*表示所有方法
".."表示任意个数的参数 -->
<aop:pointcut expression="execution(* com.wang.service.UserService.*())" id="pointcut"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试代码testDemo:
@Test
public void test1(){
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
UserService userService=(UserService)context.getBean("userService");
userService.add();
userService.search();
}
运行测试可以看到控制台的打印结果:
com.wang.service.UserService中的add方法被执行
添加用户
com.wang.service.UserService中的add方法被执行成功,返回值是nullcom.wang.service.UserService中的search方法被执行
查询用户
com.wang.service.UserService中的search方法被执行成功,返回值是null
在配置文件中,我们看到了一些这样的代码:
<aop:config>
<!--"*"为通配符表示所有方法
".."表示任意个数的参数 -->
<aop:pointcut expression="execution(* com.wang.service.UserService.*())" id="pointcut"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
我们来介绍几个AOP的相关概念,这个就比较好理解了:
- 切面(Aspect):在本例中.add(),delete(),等方法中都有一些代码,在真实的程序中这里不会只是简单的打印语句而是一些有意义的代码,这些代码可以看做是AOP的切面.
- 通知(Advisor):本例中的两个日志类,这里可以成为拦截器,都是实现了某个*Advisor接口,这两个类就是指AOP中的通知,一旦Spring符合了条件,就会发出通知,与生活中我们所说的通知不同的是,Spring中的通知是带有执行代码的,能实现某种功能.
- 切入点(pointcut):在配置拦截器(<aop-config>)的时候,xml中配置了UserService中所有的方法都是用拦截器,这个配置是通过spring中的一个已经写好的类完成的,这个类能配置对哪些方法使用拦截器,从那个地方"切入"进去.配置是可以使用通配符.
简而言之:"切入点"负责往"什么地方"插入代码,"通知"负责插入"什么代码".
SpringAOP将公共的业务(如日志,安全)和领域业务结合,当执行领域业务时候把公共业务加进来,实现公共业务的重复利用,使得领域业务功能更加纯粹,程序员可以专注于领域业务.
当然除了使用springAPI,我们也可以通过自定义的类,即不需要实现任何借口或继承任何类,的方式来实现上述功能:
只需要一个Log类:
public class Log { public void before(){
System.out.println("执行方法前");
} public void after(){
System.out.println("执行方法后");
}
}
修改配置文件beans.xml中的<aop-config>为:
<aop:config>
<aop:aspect ref="log">
<aop:pointcut expression="execution(* com.wang.service.UserService.*(..))" id="pointcut"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
执行上面的测试代码,打印出的结果是:
执行方法前
添加用户
执行方法后
执行方法前
查询用户
执行方法后
其实这种配置方式还可以用注解来完成,下面介绍一下使用注解的方式,顺带讲一个环绕方法,它和before和after一样,不过是在某一个方法前后都会执行的代码:
Log类:
@Aspect
public class Log {
@Before("execution(* com.wang.service.*.*(..))")
public void before(){
System.out.println("执行方法前");
}
@After("execution(* com.wang.service.*.*(..))")
public void after(){
System.out.println("执行方法后");
} @Around("execution(* com.wang.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前");
System.out.println("签名:"+pjp.getSignature());
//执行目标方法
Object proceed = pjp.proceed();
System.out.println("环绕后");
return proceed;
}
在beans.xml中,只需要将<aop-config>修改为一行代码:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.wang.service.UserService"></bean>
<bean id="log" class="com.wang.log.Log"></bean>
<aop:aspectj-autoproxy/>
</beans>
打印结果如下,读者自行理解,这里对于around不再解释:
环绕前
签名:void com.wang.service.UserService.add()
执行方法前
添加用户
环绕后
执行方法后
如果希望在before和after方法中得到当前执行方法的方法名或者参数的话,可以在bofore或者after方法中加一个参数,就是上面around方法中的参数,
获得方法名:String methodName=pjp.getSignature().getName();
获得方法参数列表:List<Object> list=Arrays.toList(pjp.getArgs());
Spring支持5种类型的通知注解:
@Before:前置通知,在方法执行前执行
@After:后置通知,在方法执行后执行(此注解标注的方法无法得到方法的返回值)
@AfterRunning:返回通知,在方法返回结果之后执行(此注解标注的方法,可以获取到方法的返回值)
@AfterThrowing:异常通知,在方法抛出异常后执行
@Around:环绕通知,问绕着方法执行(环绕通知必须要有ProceedingJoinPoint类型的参数,且必须要有返回值,见上面).
spring笔记--使用springAPI以及自定义类 实现AOP的一个例子的更多相关文章
- (一)使用springAPI以及自定义类 实现AOP-aop编程
Spring的另一个重要思想是AOP,面向切面的编程,它提供了一种机制,可以在执行业务前后执行另外的代码,Servlet中的Filter就是一种AOP思想的体现,下面通过一个例子来感受一下. 假设我们 ...
- C++引入自定义类(同一项目下 和 不同项目下的 区别)
今天遇到的问题,使用其他项目中的类时,引入头文件无效,然后发现如下: 项目B: 自定义类B BPro.h #include<iostream> using namespace std; c ...
- Objective-c官方文档 怎么自定义类
通过类别来给已经存在的类添加方法来实现自定义类 如果你需要添加一个方法给一个已经存在的类,也许能增加新的功能使你更容易来在我们的应用里处理一些事情.最简单的方法是用类别. 这个语法有点想类的接口描述但 ...
- python面向对象反射-框架原理-动态导入-元类-自定义类-单例模式-项目的生命周期-05
反射 reflect 反射(reflect)其实是反省,自省的意思 反省:指的是一个对象应该具备可以检测.修改.增加自身属性的能力 反射:通过字符串获取对象或者类的属性,进行操作 设计框架时需要通过反 ...
- Spring自定义类扫描器 ClassPathScanningCandidateComponentProvider
项目中有个需求 读取xml文件,然后 对xml文件进行解析,比如如果是 Gender=0/1的话,分别代表男女. 所以需要在构造函数之后,初始化bean之前进行过滤解析 xml文件: <inte ...
- spring boot自定义类配置绑定在配置文件中自动提示
在spring boot的日常使用中,我们可能需要使用配置绑定的方式动态配置自定义类的成员变量. 这个时候,我们在配置文件中配置spring默认已有的配置时,只需要输入部分关键字即可自动提示,如下图: ...
- Java基础知识强化之IO流笔记55:IO流练习之 自定义类模拟LineNumberReader的获取行号功能案例
1. 自定义类模拟LineNumberReader的获取行号功能案例 2. 代码实现: (1)MyBufferedReader.java: package cn.itcast_08; import j ...
- SpringBoot在自定义类中调用service层等Spring其他层
解决方案: 1.上代码 @Component public class ServerHandler extends IoHandlerAdapter { @Autowired protected He ...
- Java学习笔记day06_自定义类_ArrayList
1.自定义类class 使用类的形式, 对现实中的事物进行描述. 类是引用数据类型. 事物: 方法,变量. 方法:事物具备的功能. 变量:事物的属性. 格式: public class 类名{ //属 ...
随机推荐
- 解决:View调用invalidate()后不刷新onDraw()
近来学android图片处理,按照例子来,自定义一个View,之后在Activity里面手动调用该View的invalidate()后,一直无法刷新onDraw() 上网搜了一下,有两种解决办法: 一 ...
- Devexpress treeList
1.数据绑定最基本的两个属性:KeyFieldName和ParentFieldName. SELECT OfficeID,OfficeName,ParentOfficeID FROM tOffice ...
- 使div下的图片自适应div的大小
div img{ max-width:100%; height:auto; } 这里div 要给固定的宽度 开始这里还想了半天 用网上的方法也不行 问老大 又一句话就给我解决了...老大真男神啊!!! ...
- 一鼓作气 博客--第五篇 note5
一.迭代器 二.装饰器 三.生成器 1.生成列表的方式有几种 2.把列表每个数都加1 2.1 data =[1,2,3] for i in map(lambda x:x+1,data):print(i ...
- windows下C++高精度计时
写代码时,经常会计算某一段代码的运行时间,以下提供一个微秒级别的类供参考 class CTimeCost { public: CTimeCost(const string &str) : m_ ...
- CSS篇之动画(2)
animation-name(自定义动画) name为动画名称.不要用中文,尽量用与动画相关的名称.元素所应用的动画名称,必须与规则@keyframes配合使用,因为动画名称由@keyframes定义 ...
- Linux堆溢出漏洞利用之unlink
Linux堆溢出漏洞利用之unlink 作者:走位@阿里聚安全 0 前言 之前我们深入了解了glibc malloc的运行机制(文章链接请看文末▼),下面就让我们开始真正的堆溢出漏洞利用学习吧.说实话 ...
- Java NIO2:缓冲区
什么是缓冲区 一个缓冲区对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索.缓冲区像前篇文章讨论的那样被写满和释放,对于每个非布尔原始数据类型都有一个缓 ...
- ANDROID下面的游戏更新目录
更新模块的整体方案终于搞定了,包括launcher的自更新,以及framework,app等代码的更新,均测试通过. 很激动地拿到ANDROID上去测试,一下就傻眼了. 在创建upd目录的时候,就遇上 ...
- awk 留底
序 因为经常做awk编码,而且跨过一段时间就容易忘记,故在此做个留底.便于翻阅.——后期会在这个页面不断补充! 常用常量 属性 描述 NR 已读入的总记录数 ARGIND 当前被 ...