一 概述
1 介绍
AOP(Aspect Oriented Programming)面向切面编程技术,将一个个的横切关注点放到模块中,称之为切面。每一个的切面都能影响业务的某一种功能,切面的目的是功能增强,日志切面就是一个横切关注点,应用中方法需做日志记录的只需要插入日志的切面即可。(动态代理就可以实现 AOP)
AOP 可将与业务无关,但为业务模块所共同调用的逻辑或责任(如事务处理、日志管理、权限控制等)封装起来,减少系统冗余代码,降低模块间耦合度,增强可拓展性和可维护性。将业务方法中与业务无关的操作抽离到不同的对象中,最后使用动态代理的方式组合起来,动态地为类增加功能。
2 名词解释
Joinpoint:连接点,指需要被增强的方法。
Pointcut:切入点,哪些包中的哪些类中的哪些方法,可认为是连接点的集合。
Advice:增强,当拦截到 Joinpoint 之后,在方法执行的什么时机(when)做什么样(what)的增强。
根据时机分为:前置增强、后置增强、异常增强、最终增强、环绕增强。
Aspect:切面,Pointcut + Advice,去哪些地方 + 在什么时候 + 做什么增强。
Target:被代理的目标对象。
Weaving:织入,把 Advice 加到 Target 上之后,创建出 Proxy 对象的过程。
Proxy:一个类被 AOP 织入增强后,产生的代理类。
where:去哪里做增强。
3 AspectJ
AOP 思想的实现有两种方式 Spring AOP 和 AspectJ。与原始 Spring AOP 编程相比 AspectJ(比如切入点表达式)开发更为简洁。AspectJ 是一个面向切面的框架,它使用 Java 对 AOP 进行了实现。
4 AspectJ 切入点语法
// execution(修饰符.返回类型.全限定名.方法名.方法参数.异常类型)
// 其中修饰符可以省略,返回类型可用*号代替,全限定名中的包结构每个部分都可用*号代替但只表示一个单词
// 对于实现类可用*ServiceImpl代替多个ServiceImpl实现类,方法也可设为*,参数可用..代替
// execution()为切入点表达式 ()内为切入点 此方法可确定到指定类的指定方法进行增强
// * :匹配任何部分,只能表示一个单词
// .. :可用于全限定名中(表示当前包及子包)和方法参数中(表示子 0 到 N 个参数)
// 例子 第一个*号后面有空格
execution(* cn.tj.ssm.service.impl.*ServiceImpl.*(..))
二 AOP配置(XML)
1 添加依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
2 事务(增强)类
public class TransactionManager {
/*开启事务*/
public void begin(){
System.out.println("事务开启了.....");
}
/*提交事务*/
public void commit(){
System.out.println("事务提交了........");
}
/*回滚事务*/
public void rollback(){
System.out.println("事务回滚了.........");
}
}
3 目标对象 接口实现类 – EmployeeServiceImpl
// 接口
public interface EmployeeService {
/*员工保存*/
public int save(String username,String password);
}
public class EmployeeServiceImpl implements EmployeeService {
// save 此方法为连接点
@Override
public int save(String username, String password) {
System.out.println("员工姓名:"+username+"密码:"+password);
return 0;
}
}
4 配置文件 – spring-core.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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="employeeService" class="cn.tj.service.impl.EmployeeServiceImpl"></bean>
<!--增强类对象-->
<bean id="transactionManager" class="cn.tj.tx.TransactionManager"></bean>
<!--声明(开启)aop 将增强对象的方法放入目标对象中 引入约束后可使用-->
<aop:config >
<!--定义切面 通过ref关联增强类对象-->
<aop:aspect ref="transactionManager">
<!--切入点-->
<aop:pointcut id="pc"
expression="execution(* cn.tj.service.impl.*ServiceImpl.*(..))"/>
<!--前置增强(before) 针对pc切入点做前置增强 begin是增强类中的方法-->
<aop:before method="begin" pointcut-ref="pc"></aop:before>
<!--后置增强-->
<aop:after-returning method="commit" pointcut-ref="pc"></aop:after-returning>
<!--异常增强-->
<aop:after-throwing method="rollback" pointcut-ref="pc"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>
5 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {
// 注入对象直接调用即可
@Autowired
private EmployeeService employeeService;
@Test
public void emp_save() {
employeeService.save("叶凡","admin");
}
}
6 切换动态代理 – 修改配置文件
<!--Spring AOP在目标对象实现接口时,默认使用 JDK 动态代理。若想使用 CGLIB 动态代理,需配置如下-->
<aop:config proxy-target-class="true">
<!--内部与jdk相同-->
</aop:config>
三 AOP配置(注解)
1 目标对象 接口实现类 – EmployeeServiceImpl
// 接口
public interface EmployeeService {
/*员工保存*/
public int save(String username,String password);
}
public class EmployeeServiceImpl implements EmployeeService {
// save 此方法为连接点
@Override
public int save(String username, String password) {
System.out.println("员工姓名:"+username+"密码:"+password);
return 0;
}
}
2 事务(增强)类
// 交给Spring容器处理 并设置为切面对象
@Component
@Aspect
public class TransactionManager {
/*切入点表达式方法*/
@Pointcut("execution(* cn.tj.service.impl.*ServiceImpl.*(..))")
public void pc(){}
/*开启事务 添加对应注解 关联切入点 或用@Before(Pointcut="切入点表达式")*/
@Before("pc()")
public void begin(){
System.out.println("事务开启了.....");
}
/*提交事务*/
@AfterReturning( "pc()")
public void commit(){
System.out.println("事务提交了........");
}
/*回滚事务*/
@AfterThrowing("pc()")
public void rollback(){
System.out.println("事务回滚了.........");
}
}
3 配置文件 – spring-core.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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="employeeService" class="cn.tj.service.impl.EmployeeServiceImpl"></bean>
<!--增强类对象-->
<bean id="transactionManager" class="cn.tj.tx.TransactionManager"></bean>
<!--开启aop注解扫描 为配置AspectJ切面的bean创建代理,并将切面织入到这些 bean 中-->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
</beans>
4 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {
// 注入对象直接调用即可
@Autowired
private EmployeeService employeeService;
@Test
public void emp_save() {
employeeService.save("叶凡","admin");
}
}
5 切换动态代理 – 修改配置文件
<aop:aspectj-autoproxy proxy-target-class="true"/>