目录
- 1 基础知识:代理
- 2 AOP简介
- 3 Spring使用AOP
- 3.1 使用spring API接口实现
- 3.1.1 配置xml
- 3.1.2 定义服务接口
- 3.1.3 服务接口实现类
- 3.1.4 创建切面编程的类(比如添加日志)
- 3.1.4.1 在方法执行前添加日志
- 3.1.4.2 在方法执行后添加日志
- 3.1.5 xml配置aop把二者连接起来
- 3.1.6 测试方法
- 3.2 自定义类实现AOP
- 3.2.1 切面定义
- 3.2.2 xml配置
- 3.3 注解实现
- 3.3.1 pom导入依赖aspectj
- 3.3.2 注解切面类
- 3.3.3 aop自动代理配置
- 3.4 Advice方法种类
- 3.5 execution表达式
- 4 总结
1 基础知识:代理
2 AOP简介
Aspect Oriented Programming(面向切面编程),通过预编译方式和运行期动态代理实现程序功能统一维护的一种技术。
理解:程序是纵向开发,依次从后端向前端开发编写,假设由x轴表示,那么AOP就是在y轴上进行开发,针对某一个x轴环节,进行处理,比如需要在x轴某个环节新增一个日志功能,就是横向y轴开发,不改变x轴原有功能。
3 Spring使用AOP
需要导入依赖:
<!--aspectjweaver(AOP使用)-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
<scope>runtime</scope>
</dependency>
相关概念:
- 横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关,但是是关注的部分。如日志,安全,缓存,事务等等。。。
- 切面(Aspect):横切关注点(被模块化)的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是一个方法。
- 目标(Target):被通知(被代理)对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):是Advice执行地点的定义。
- 连接点(JoinPoint):与切入点匹配的执行点。
3.1 使用spring API接口实现
3.1.1 配置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"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--自动扫描包-->
<context:component-scan base-package="xyz.cqulwj"/>
</beans>
3.1.2 定义服务接口
public interface UserService {
void add();
void delete();
void update();
void select();
}
3.1.3 服务接口实现类
@Component
public class UserServiceImpl implements UserService{
static Logger logger = Logger.getLogger(UserServiceImpl.class);
@Override
public void add() {
logger.debug("添加元素");
}
@Override
public void delete() {
logger.debug("删除元素");
}
@Override
public void update() {
logger.debug("更新元素");
}
@Override
public void select() {
logger.debug("查询元素");
}
}
3.1.4 创建切面编程的类(比如添加日志)
3.1.4.1 在方法执行前添加日志
@Component
public class LogBefore implements MethodBeforeAdvice {
static Logger logger = Logger.getLogger(LogBefore.class);
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
logger.debug(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
3.1.4.2 在方法执行后添加日志
@Component
public class LogAfter implements AfterReturningAdvice {
static Logger logger = Logger.getLogger(LogAfter.class);
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
logger.debug("执行了"+method.getName()+"方法");
}
}
3.1.5 xml配置aop把二者连接起来
<!--aop配置-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* xyz.cqulwj.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="logAfter" pointcut-ref="pointcut"/>
</aop:config>
把切面编程的代码运用到原实现中。
3.1.6 测试方法
@Test
public void serviceTest() {
//获得上下文
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//获得对象
UserService userService=(UserService) context.getBean("userServiceImpl");
//显示输出
userService.add();
userService.delete();
userService.update();
userService.select();
}
可以看到在每一个接口方法执行前后,都有日志写入。
3.2 自定义类实现AOP
3.2.1 切面定义
@Component
public class DiyLog {
static Logger logger = Logger.getLogger(DiyLog.class);
public void before(){
logger.debug("方法执行前");
}
public void after(){
logger.debug("方法执行后");
}
}
注意要加上@Component
注册成bean;
3.2.2 xml配置
<!--aop配置-->
<aop:config>
<aop:aspect ref="diyLog">
<aop:pointcut id="point" expression="execution(* xyz.cqulwj.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
aop:aspect
把DiyLog
作为切面(要插入的类),aop:pointcut
定义了切入点,需要给哪个类的哪些方法切入,aop:before
和aop:after
定义了要在切入点执行切面的哪些方法,也叫作通知。
3.3 注解实现
3.3.1 pom导入依赖aspectj
<!--AspectJ支持-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
3.3.2 注解切面类
@Component
@Aspect
public class DiyLog {
static Logger logger = Logger.getLogger(DiyLog.class);
@Before("execution(* xyz.cqulwj.service.UserServiceImpl.*(..))")
public void before(){
logger.debug("方法执行前");
}
@After("execution(* xyz.cqulwj.service.UserServiceImpl.*(..))")
public void after(){
logger.debug("方法执行后");
}
}
3.3.3 aop自动代理配置
<!--aop自动代理-->
<aop:aspectj-autoproxy/>
3.4 Advice方法种类
-
@Before
:方法执行前执行该通知; -
@AfterThrowing
:抛出异常后执行; -
@AfterReturning
:方法返回值执行后; -
@After
:方法执行之后(包括@AfterThrowing
后和@AfterReturning
)执行;
比较特殊就是@Around
,先进行以上对于原方法的拼接操作,然后作为ProceedingJoinPoint
类型的对象传递给@Around
函数体,可以使用jp.proceed()
来执行拼接后的方法,另外,在@Around
中还会拼接@Before
和@After
的代码。
[xyz.cqulwj.div.DiyLog]--->@Before方法执行前
[xyz.cqulwj.div.DiyLog]--->@Around环绕前
[xyz.cqulwj.div.DiyLog]--->@Around方法执行
[xyz.cqulwj.div.DiyLog]--->@Before方法执行前
[xyz.cqulwj.service.UserServiceImpl]-执行目标对象函数
[xyz.cqulwj.div.DiyLog]--->@AfterReturning返回值执行后
[xyz.cqulwj.div.DiyLog]--->@After方法执行后
[xyz.cqulwj.div.DiyLog]--->@Around环绕后
[xyz.cqulwj.div.DiyLog]--->@After方法执行后
3.5 execution表达式
4 总结
用动态代理的思想去理解AOP会更好理解,定义一个切面类,里面有一些切面方法,这些方法需要在目标对象的一些方法的生命周期(开始前、返回值后、异常后、结束后等)执行。