spring提供了aop的功能,我们只要知道怎么用。
代理对象不用我们去生成了。spring帮我们生成。但是,我们得去配。
增强需要自己编写。比如,要写日志。那么写日志的代码需要自己来写。
写增强,再配置。配完了,就会帮我们去生成代理对象,帮我们做aop相关的功能。
aop有两种。一是,spring自己提供的实现。但是,spring自己的实现比较麻烦。
另一家公司,AspectJ,也提供了aop的实现。spring发现,AspectJ的aop实现比较简单。spring就把它拿过来了,作为自己的实现的方式。开发中,一般都会用AspectJ的方式。
一、aop相关术语
1. Joinpoint(连接点)
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。一个类中的所有方法都可以称为是连接点。这些方法都是可以被增强的。spring只能对方法增强。
public class BookDaoImpl {
public void save() {
System.out.println("保存图书...");
}
public void update() {
System.out.println("修改图书...");
}
}
这里的两个方法都称为连接点。
2. Pointcut(切入点)
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
上面的代码。如果,我们只想对save()方法做增强,那么,save()方法就是切入点。当程序执行到切入点的时候,增强就来了。
3. Advice(通知/增强)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
4. Introduction(引介)
引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
5. Target(目标对象) -- 代理的目标对象
就是我们上面代码中的BookDaoImpl类的对象。也可以叫做被增强的对象。
6. Weaving(织入) -- 是指把增强应用到目标对象来创建新的代理对象的过程
7. Proxy(代理) -- 一个类被AOP织入增强后,就产生一个结果代理类
8. Aspect(切面) -- 是切入点和通知的结合,以后咱们自己来编写和配置的
二、AspectJ的XML方式完成AOP的开发
步骤一:创建JavaWEB项目,引入具体的开发的jar包
先引入Spring框架开发的基本开发包
再引入Spring框架的AOP的开发包
spring的传统AOP的开发的包
- spring-aop-4.2.4.RELEASE.jar
- com.springsource.org.aopalliance-1.0.0.jar aop联盟,定义了很多规范
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar --AspectJ的包
- spring-aspects-4.2.4.RELEASE.jar --spring整合AspectJ的包
步骤二:创建Spring的配置文件,引入具体的AOP的schema约束
<?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">
</beans>
步骤三:创建包结构,编写具体的接口和实现类
CustomerDao -- 接口
CustomerDaoImpl -- 实现类
步骤四:将目标类配置到Spring中
<bean id="customerDao" class="com.ken.demo3.CustomerDaoImpl"/>
步骤五:定义切面类
/**
* 切面类:切入点+通知
*
*/
public class MyAspectXml {
/**
* 通知(具体的增强)
*/
public void log() {
System.out.println("记录日志...");
}
}
步骤六:在配置文件中定义切面类
<bean id="myAspectXml" class="com.ken.demo3.MyAspectXml"/>
步骤七:在配置文件中完成aop的配置
<!-- 配置aop -->
<aop:config>
<!-- 配置切面类:切入点+通知 -->
<aop:aspect ref="myAspectXml">
<!-- 配置前置通知,save方法执行之前,增强的方法会执行 -->
<!-- 切面类里的哪个方法。myAspectXml切面的log方法(有可能有多个切面,也可能有多个方法) -->
<!-- 切入点表达式:execution(public void com.ken.demo3.CustomerDaoImpl.save()) -->
<aop:before method="log" pointcut="execution(public void com.ken.demo3.CustomerDaoImpl.save())" />
</aop:aspect>
</aop:config>
步骤八:完成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo3 {
@Resource(name = "customerDao")
private CustomerDao customerDao;
@Test
public void run1() {
customerDao.save();
customerDao.update();
}
}
三、切入点的表达式
<?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="customerDao" class="com.ken.demo3.CustomerDaoImpl" />
<!-- 切面类 -->
<bean id="myAspectXml" class="com.ken.demo3.MyAspectXml" />
<!-- 配置aop -->
<aop:config>
<aop:aspect ref="myAspectXml">
<!-- 切入点的表达式
1.execution() 固定的,不能不写
2.public 可以省略不写
3.void,返回值可以出现* 表示任何类型,返回值类型不能不写
4.包名可以使用*代替。一个*只表示一层包。简写:*..*,表示任意层包。不能不写。
5.*DaoImpl:类名,表示以DaoImpl结尾
6.方法:save*(),一般以save开头
7.方法的参数:..表示任意参数
-->
<!-- <aop:before method="log" pointcut="execution(public void com.ken.demo3.CustomerDaoImpl.save())" /> -->
<!-- public 可以省略不写 -->
<!-- <aop:before method="log" pointcut="execution(void com.ken.demo3.CustomerDaoImpl.save())" /> -->
<!-- void,返回值可以出现* 表示任何类型,返回值类型不能不写 -->
<!-- <aop:before method="log" pointcut="execution(* com.ken.demo3.CustomerDaoImpl.save())" /> -->
<!-- 包名可以使用*代替。一个*只表示一层包。简写:*..*,表示任意层包。不能不写。 -->
<!-- <aop:before method="log" pointcut="execution(* *..*.CustomerDaoImpl.save())" /> -->
<!-- *DaoImpl:类名,表示以DaoImpl结尾 -->
<!-- <aop:before method="log" pointcut="execution(* *..*.*DaoImpl.save())" /> -->
<!-- 方法:save*(),一般以save开头 -->
<!-- <aop:before method="log" pointcut="execution(* *..*.*DaoImpl.save*())" /> -->
<!-- 方法的参数:..表示任意参数 -->
<aop:before method="log" pointcut="execution(* *..*.*DaoImpl.save*(..))" />
</aop:aspect>
</aop:config>
</beans>
四、AOP的通知类型
1. 前置通知在目标类的方法执行之前执行。
配置文件信息:
<aop:config>
<aop:aspect ref="myAspectXml">
<aop:before method="log" pointcut="execution(* *..*.*DaoImpl.save*(..))" />
</aop:aspect>
</aop:config>
应用:可以对方法的参数来做校验
2. 最终通知
在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
不管程序执行成功还是失败,方法执行之后,都会通知。
在配置文件中编写具体的配置:
<aop:config>
<aop:aspect ref="myAspectXml">
<aop:after method="after" pointcut="execution(* *..*.*DaoImpl.save*(..))" />
</aop:aspect>
</aop:config>
应用:例如像释放资源
3. 后置通知
方法正常执行后的通知。 只有方法执行成功之后,才会通知。
在配置文件中编写具体的配置:
<aop:config>
<aop:aspect ref="myAspectXml">
<aop:after-returning method="afterReturning" pointcut="execution(* *..*.*DaoImpl.save*(..))" />
</aop:aspect>
</aop:config>
应用:可以修改方法的返回值
4. 异常抛出通知
在抛出异常后通知
在配置文件中编写具体的配置:
<aop:config>
<aop:aspect ref="myAspectXml">
<aop:after-throwing method="afterThrowing" pointcut="execution(* *..*.*DaoImpl.save*(..))" />
</aop:aspect>
</aop:config>
应用:包装异常的信息
5. 环绕通知
方法的执行前后执行。配置事务很好。
在配置文件中编写具体的配置:
<aop:config>
<aop:aspect ref="myAspectXml">
<aop:around method="around" pointcut="execution(* *..*.*DaoImpl.save*(..))" />
</aop:aspect>
</aop:config>
要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。
五、Spring框架的AOP--注解方式
步骤一:创建JavaWEB项目,引入具体的开发的jar包
先引入Spring框架开发的基本开发包
再引入Spring框架的AOP的开发包
spring的传统AOP的开发的包
spring-aop-4.2.4.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
aspectJ的开发包
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar
步骤二:创建Spring的配置文件,引入具体的AOP的schema约束
<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">
</beans>
步骤三:创建包结构,编写具体的接口和实现类
CustomerDao -- 接口
CustomerDaoImpl -- 实现类
步骤四:将目标类配置到Spring中
<bean id="customerDao" class="com.ken.demo1.CustomerDaoImpl"/>
步骤五:定义切面类
添加切面和通知的注解
@Aspect -- 定义切面类的注解
通知类型(注解的参数是切入点的表达式)
@Before -- 前置通知
@AfterReturing -- 后置通知
@Around -- 环绕通知
@After -- 最终通知
@AfterThrowing -- 异常抛出通知
具体的代码如下
@Aspect
public class MyAspectAnno {
// @Before(value = "execution(public void com.ken.demo1.CustomerDaoImpl.save())")
@Before(value = "MyAspectAnno.fn()")
public void log() {
System.out.println("记录日志...");
}
// @After(value = "execution(public void com.ken.demo1.CustomerDaoImpl.save())")
@After(value = "MyAspectAnno.fn()")
public void after() {
System.out.println("最终通知...");
}
// @Around(value = "execution(public void com.ken.demo1.CustomerDaoImpl.save())")
@Around(value = "MyAspectAnno.fn()")
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("环绕通知1...");
try {
joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("环绕通知2...");
}
//配置通用的切入点。使用@Pointcut定义通用的切入点
@Pointcut(value = "execution(public * *..*.*DaoImpl.save*(..))")
public void fn() {
}
}
步骤六:在配置文件中定义切面类
<bean id="myAspectAnno" class="com.ken.demo1.MyAspectAnno" />
步骤七:在配置文件中开启自动代理
<aop:aspectj-autoproxy/>
完成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1 {
@Resource(name = "customerDao")
private CustomerDao customerDao;
@Test
public void run1() {
customerDao.save();
customerDao.update();
}
}