AOP思想:横向重复、纵向抽取
2.底层实现:
Spring的AOP底层使用两种代理机制
(1) JDK的动态代理:针对实现了接口的类产生代理
(2)Cglib的动态代理:针对没有实现接口的类产生代理、应用的是底层的字节码增强的技术生成当前类的子类对象
3.相关术语
(1)Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring中这些点指的是方法,因为Spring只支持方法类型的连接点(可以被切入的点)
(2)Pointcut(切入点):所谓切入点是指对哪些Jonpoint进行拦截的定义(已经被切入的点)
(3)Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所做的事情就是通知。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知(切面要完成的功能)
(4)Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或者Field。
(5)Aspect(切面):是切入点和通知(引介)的结合
(6)Target(目标对象):代理的目标对象
(7)Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
(8)Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入,而Aspect采用编译时期织入和类装载期织入
4.AOP实例
(1)创建Web项目(项目结构)
(2)导入jar
(3)加入日志配置(log4j.properties)
# Global logging configuration
log4j.rootLogger=info, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
(4)先创建接口->创建实现类
package com.linixn.spring.service;
public interface UserService{
//增加
public void save();
//删除
public void delete();
//修改
public void update();
//查询
public void select();
}
package com.linixn.spring.service;
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("增加");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("更新");
}
@Override
public void select() {
System.out.println("查询");
}
}
(5)编写通知
前置通知:在目标方法之前调用
后置通知(出现异常就不调用):在目标方法之后调用
后置通知(无论是否出现异常都调用):在目标方法之后调用
环绕通知:在目标方法之前、后调用
异常通知:出现异常则调用
package com.linixn.spring.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class TransactionAdive {
// 前置通知:在目标方法之前调用
// 后置通知(出现异常就不调用):在目标方法之后调用
// 后置通知(无论是否出现异常都调用):在目标方法之后调用
// 环绕通知:在目标方法之前、后调用
// 异常通知:出现异常则调用
public void before() {
System.out.println("前置通知被执行");
}
public void afterReturning() {
System.out.println("后置置通知被执行(出现异常不调用)");
}
public void after() {
System.out.println("后置通知(无论是否出现异常都调用)");
}
public void afterException() {
System.out.println("异常通知:出现异常则调用");
}
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕通知之前");
Object proceed= point.proceed();
System.out.println("环绕通知之后");
return proceed;
}
}
(6)配置织入,将通知织入目标对象
aop:pointcut
符号 | 含义 |
---|---|
. | 匹配除换行外的所有单个字符 |
* | 匹配前面的字符0次或者多次 |
+ | 匹配前面的字符1次或者多次 |
? | 匹配前面的字符0次或者1次 |
^ | 匹配字符开始 |
$ | 匹配字符结束 |
x|y | 匹配x或y |
[xyz] | xyz其中一个 |
{n} | 匹配n次 |
{n,} | 匹配至少n次 |
{n,m} | 匹配n次到m次 |
\ | 转义字符 |
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:centext="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!-- 目标对象 -->
<bean name="userService" class="com.linixn.spring.service.UserServiceImpl"></bean>
<!-- 通知对象 -->
<bean name="transactionAdvice" class="com.linixn.spring.advice.TransactionAdive"></bean>
<!-- 将通知对象织入目标对象 -->
<aop:config>
<!-- 选择切入点 -->
<!-- 方法加返回值 -->
<aop:pointcut expression="execution(public void com.linixn.spring.service.UserService.update() )" id="pointcut"/>
<aop:aspect ref="transactionAdvice">
<!-- 前置切入 -->
<aop:before method="before" pointcut-ref="pointcut"/>
<!-- 无论是否出现异常都会调用 -->
<aop:after method="after" pointcut-ref="pointcut"/>
<!-- 出现异常不调用 -->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterException" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
(7)测试
(1)无异常测试
package com.linixn.spring.service.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.linixn.spring.service.UserService;
//创建容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserTest {
@Resource(name="userService")
private UserService userSer;
@Test
public void UserTest1() {
userSer.update();
}
}
(2)出现异常测试
修改updata()方法加入 int a=1/0;
测试结果