Spring AOP的几个概念
1.切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
2.连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
3.通知(Advice):通知就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
4.切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
5.目标对象(Target):就是被AOP动态代理的目标对象;
AOP的通知类型
◆ 前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行
应用:数据校验
◆ 后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知
应用:现场清理
◆ 返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行
应用:返回值相关数据处理
◆ 抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
应用:对原始方法中出现的异常信息进行处理
◆ 环绕通知:在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行
应用:十分强大,可以做任何事情
注意:当同一个切入点配置了多个通知时,通知会存在运行的先后顺序,该顺序以通知配置的顺序为准
AOP配置之纯注解方式
简易配置类
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.project")
@Component
@Aspect//切面
public class Aops {
@Pointcut("execution(* *..*(..))")//切入点
public void pt(){};
@Before("pt()")//通知
public void strong(){
System.out.println("增强方法");
}
}
接口和实现类
//接口
public interface StudentService {
void find();
}
//--------------------------------------------------------------
//实现类
@Component
public class StudentServiceImpl implements StudentService {
public void find() {
System.out.println("找到了该学生");
}
}
测试类
public class StudentServiceTest {
@Test
public void testFind() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Aops.class);
StudentService bean = (StudentService) ctx.getBean(StudentService.class);
bean.find();
}
}
打印结果:
增强方法
找到了该学生
注解开发AOP注意事项
- 切入点最终体现为一个方法,无参无返回值,无实际方法体内容,但不能是抽象方法
- 引用切入点时必须使用方法调用名称,方法后面的()不能省略
- 切面类中定义的切入点只能在当前类中使用,如果想引用其他类中定义的切入点使用“类名.方法名()”引用
- 可以在通知类型注解后添加参数,实现XML配置中的属性,例如after-returning后的returning属性
AOP配置之配置文件方式
spring配置文件
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.project"/>
<aop:aspectj-autoproxy/>
<!--资源bean-->
<bean class="com.project.service.impl.StudentServiceImpl" id="service"/>
<bean id="aopadvice" class="com.project.aop.Aops"/>
<!--aop配置项-->
<aop:config>
<!--expression:指定范围-->
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<!--绑定通知类-->
<aop:aspect ref="aopadvice">
<!--增强方法-->
<aop:after method="strong" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
接口和实现类
//接口
public interface StudentService {
void find();
}
//--------------------------------------------------------------
//实现类
@Component
public class StudentServiceImpl implements StudentService {
public void find() {
System.out.println("找到了该学生");
}
}
通知类
public class Aops {
public void strong(){
System.out.println("增强方法");
}
}
测试类
public class StudentServiceTest {
@Test
public void testFind() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService bean = (StudentService) ctx.getBean("service");
bean.find();
}
}
打印结果:
增强方法
找到了该学生
AOP底层原理
SpringAOP底层使用了动态代理,spring框架会生成并管理代理类,
spring框架对于这种编程思想的实现基于两种动态代理模式,分别是 JDK动态代理 及 CGLIB的动态代理,这两种动态代理的区别是 JDK动态代理需要目标对象实现接口,而 CGLIB的动态代理则不需要。
接口及其实现类
//接口
public interface PersonService {
void show();
}
//实现类
public class PersonServiceImpl implements PersonService {
public void show() {
System.out.println("毛坯房");
}
}
jdk动态代理
package com.project.proxy;
import com.project.service.PersonService;
import com.project.service.impl.PersonServiceImpl;
import java.lang.reflect.Proxy;
public class ProxyPersonService {
public static void main(String[] args) {
/**
* personService:目标对象
* 使用jdk动态代理生成代理类
*/
final PersonService personService = new PersonServiceImpl();
PersonService proxyInstance = (PersonService) Proxy.newProxyInstance(
//目标对象的类加载器
PersonServiceImpl.class.getClassLoader(),
//目标对象实现的接口
PersonServiceImpl.class.getInterfaces(),
//第三个参数InvocationHandler接口实现类,用来封装额外功能,这里用的是lambda表达式
(proxy, method, param) -> {
Object invoke = method.invoke(personService, param);
System.out.println("精装修");
return invoke;
}
);
proxyInstance.show();
}
}
打印结果:
毛坯房
精装修
cglib
package com.project.Cglib;
import com.project.service.PersonService;
import com.project.service.impl.PersonServiceImpl;
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
public class CglibProxyTest {
@Test
public void TestCglibProxy() {
/**
* 使用cglib代理生成代理类
*/
//创建Enhancer对象
Enhancer enhancer = new Enhancer();
//设置enhancer的父类为指定类型,这里就是我们需要代理的类
enhancer.setSuperclass(PersonServiceImpl.class);
//回调方法过滤
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
//执行原来的方法
Object invokeSuper = methodProxy.invokeSuper(o, objects);
//增强
System.out.println("刮大白");
return invokeSuper;
});
PersonService personService = (PersonService) enhancer.create();
personService.show();
//cglib动态代理会代理被代理类的所有方法
}
}
打印结果:
毛坯房
刮大白
事务管理
声明式事务
名称:@Transactional
- 类型:方法注解,类注解,接口注解
- 位置:方法定义上方,类定义上方,接口定义上方
- 作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性
- 范例
@Transactional(
readOnly = false, //是否只读事物
timeout = -1, //超时时间,-1为不超时
isolation = Isolation.DEFAULT, //隔离级别,默认是跟随数据库
rollbackFor = {ArithmeticException.class, IOException.class}, //抛出指定异常,回滚事物
noRollbackFor = {}, //抛出指定异常不会滚事物
propagation = Propagation.REQUIRES_NEW //事物传播行为
)
需要在spring开启事务注解驱动,并指定对应的事务管理器
*<tx:annotation-driven transaction-manager=“txManager”/>