NO.11 Spring AOP
一:概念理解
Spring AOP(Aspect Oriented Program)面向切面,集成了aspectj这个工具, 措施是采用横向抽取的机制取代了传统向继承体系的重复代码使用,比如说想要开发一些其他的功能,如性能监控、事务的管理、日志或权限的管理等,都可以用AOP。上节所述的代理机制也是AOP的一个思想,是采用手动的方式/最基本的方式使用AOP的思想。
二:使用例子
把业务bean放到service层去处理,现在想要通过AOP对service进行编程,编程之后,希望能够代理业务bean进行日志打印等操作。
1)性能监控
2)事务管理 操作数据库,采用mvc分层,数据库操作在dao层,实际的业务处理在service层,应该在service层处理事务机制,那么,在service层如何让处理事务机制呢?在spring中就是采用aop的思想。
3)安全检查 也就是所谓的权限。
可将这些代码单独的抽离出去,不跟实际业务发生在一起,就是让代码与实际业务隔离,降低程序的耦合性。
4)缓存优化
5)记录日志
三: AOP的术语
(1)连接点:(JoinPoint):指的是那些被拦截到的点(在Spring AOP中也就是方法)。假如说有一条业务线正在执行,通过控制层到达服务层再到Dao层,到服务层时会调用服务层里的一个方法,在执行service方法时被aop拦截代理,代理之后,由AOP代理执行,这个方法就称为一个连接点,也就是aop与这条业务线有了一个连接。AOP在代理方法时通过这个业务的执行,spring aop最低拦截/执行级别就是方法,不能再深入到某一行的代码,最小单元就是方法。方法执行时,可以进行代理,假如说是存钱操作,代理之后,由AOP代理执行,在连接点处打印日志。假如又有第二个业务支付,同样有一个连接点。只要AOP拦截了service中的某个方法,这个方法就称为连接点。
(2)切面:如果service中有很多连接点的话,由这些连接点组成的面叫做切面。
(3)切入点:指哪些被拦截到的点的定义,匹配连接点的断言。 AOP会处理几个功能,如记录日志、权限管理等,如果没有权限执行存钱操作,那么需要进行拦截,这样的由多个功能作用于一个方法,每个功能叫做一个切入点。
(4)通知: 拦截到连接点后,所要做的事情就是通知 (1、前置通知 2、后置通知 3、异常通知 4、环绕通知<前置通知和后置通知结合在一起>)。 用AOP做方法的一个代理,当已经控制或代理方法之后,这个方法要执行,如果AOP是打印日志的,一次日志的打印,就是一次通知。或者做权限的控制,有权限检查了一次使之通过,没有权限不通过,又进行了一次通知。对连接点所做的作用/告知,就是所谓的通知。
(5)目标对象:被一个或者多个切面所通知的对象,也被称作被通知对象。
(6)代理对象:AOP框架创建的对象,它和目标对象遵循同样的接口,使用它的方式和使用目标对象的方式是一样的,但是它是目标对象的增强版,“通知”中的代码执行将会被代理对象的方法调用触发。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
四:在Spring中进行AOP的开发
Spring 本身没有AOP的组件,需要将 aspectJ安装到Spring 平台上。
基于XML的配置Spring AOP开发
Spring AOP相关Jar包:
spring-aop-4.3.3.RELEASE.jar
aspectjweaver-1.8.5.jar
aspectjrt-1.8.5.jar
pom文件中需要添加
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.5</version>
</dependency>
想要在配置文件中进行AOP的开发,首先定义一个spring的配置文件applicationContext-aop-xml.xml,同样需要引入aop的命名空间。
需要怎样的操作去替换上节写的手工代理类?
两种配置方法:
1>采用xml配置开发spring组件
2>采用注解的方式
Spring AOP开发步骤:
(1)将业务bean初始化到ioc容器中
将bean——StuCardServiceImpl配置到xml当中去
(2)配置通知类
(3)AOP配置
1>配置切点
属性expression:表达式 类似模糊查询
可通过表达式代替方法中的参数(..)表示方法中的任意参数。
2>配置aspect 通过属性ref指定通知类
前置通知,通知方法method,当运行pointcut-ref时触发。
<?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:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="stuCardService" class="com.xt.spring.aop.xml.StuCardServiceImpl"></bean>
<!-- 配置通知类 -->
<bean id="loggerAspect" class="com.xt.spring.aop.xml.LoggerAspect"></bean>
<aop:config>
<!-- 拦截类里的一个方法 -->
<aop:pointcut expression="execution(void com.xt.spring.aop.xml.StuCardServiceImpl.deposit(..))" id="stuCardDeposit"/>
<aop:aspect ref="loggerAspect">
<aop:before method="beforeMethodAdvice" pointcut-ref="stuCardDeposit"/>
</aop:aspect>
</aop:config>
</beans>
如果想要拦截所有方法,可将execution(void com.xt.spring.aop.xml.StuCardServiceImpl.deposit(..))
中的deposit改为“*”。
StuService类
package com.xt.spring.aop.xml;
public interface StuCardService {
public void deposit(String cardNo,String i);
public void payMoney(String cardNo,String money);
}
StuService实现类
package com.xt.spring.aop.xml;
public class StuCardServiceImpl implements StuCardService{
public void deposit(String cardNo, String money) {
System.out.println("向学生卡" + cardNo + "存钱:" + money);
}
public void payMoney(String cardNo, String money) {
System.out.println("使用学生卡" + cardNo + "支付:" + money);
}
}
通知类
package com.xt.spring.aop.xml;
import org.aspectj.lang.JoinPoint;
public class LoggerAspect {
//自定义前置通知
public void beforeMethodAdvice(JoinPoint jp){
System.out.println("方法:"+ jp.getSignature().getName() + "被执行,前置通知被触发.......");
}
public void afterMethodAdvice(JoinPoint jp){
System.out.println("方法:"+ jp.getSignature().getName() + "被执行,后置通知被触发.......");
}
}
测试类
package com.xt.spring.aop.xml;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AOPTest {
private ApplicationContext ioc;
@Before
public void iocInit(){
ioc = new ClassPathXmlApplicationContext("applicationContext-aop-xml.xml");
}
@Test
public void test(){
StuCardService scs = ioc.getBean("stuCardService",StuCardService.class);
scs.deposit("4512324", "500");
scs.payMoney("4512324", "500");
}
}
通过配置产生关系,降低耦合度。