AOP基本概念:
- Aspect(切面):通常是一个类,里面可以定义切入点和通知
- JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
- Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
- Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
-
AOP代理:
- AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
- Procy代理:将通知应用到目标对象之后,被动态创建的对象。
- Weaving织入:将切面代码插入到目标对象上,从而生成代理对象的过程
-
JDK动态代理:
- JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance(方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。
-
CGLIB代理:
- CGLIB(Code Generation Library)是一个高性能开源的代码
生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。
- CGLIB(Code Generation Library)是一个高性能开源的代码
-
AOP的用途:
- 权限的校验
- 日志的记录
- 事务的处理
- 性能的检测
-
AOP通知方法:
- 前置通知:在我们执行目标方法之前运行(@Before)
- 后置通知:在我们目标方法运行结束之后,不管有没有异常(@After)
- 返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)
- 异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
- 环绕通知:动态代理,需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)
-
切点表达式:
- 修饰关键字:public private protectd,一般这个写*,表示不限定关键词
- 返回值类型:int string Person void,一般省略不写(如果要写,修饰关键词不能*)
- 切入点属于哪个包,配置包名,必须写完整的包名,不支持子包
- 切入点属于哪个类,配置类名,如果*,表示不限定
- 切入点是哪个方法,配置方法名
- 切入点方法的参数,一般用两个点表示任意参数都可以
-
几个例子:
-
execution(public * *(…)) :
- pulbic开头的任意包的任意类的任意方法,任意返回值,任意参数
-
execution(* set*(…)):
- 任意修饰的任意包的任意类的以set开头的方法,任意返回值,任意的参数
-
execution(* com.xyz.service.AccountService.*(…)):
- 任意修饰的com.xyz.service包的AccountService类的任意方法,任意返回值,任意的参数
-
execution(* com.xyz.service..(…)):
- 任意修饰的com.xyz.service包的任意类的任意方法,任意返回值,任意的参数
-
execution(* com.xyz.service….(…)):
- 任意修饰的com.xyz.service包(包括所有的子包)的任意类的任意方法,任意返回值,任意的参数
- ProxyFactoryBean
- ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。
- 实现AOP
- 基于XML实现
-
添加依赖
-
创个接口(UserDao.java)
-
创建一个实现类,UserDaorel02.java实现上面的接口
-
配置文件application.xml
-
创建一个实现AOP的类MyAspect类
- 创建一个测试类,测试一下这个接口和AOP是否成功
结果测试成功。
- 基于注解实现
- 添加依赖
- 编写配置文件
<?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:c="http://www.springframework.org/schema/c" 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">
<!-- 设置包扫描 -->
<context:component-scan base-package="example04" />
<!-- 设置开启AOP注解 -->
<aop:aspectj-autoproxy />
</beans>
- 编写接口
public interface Service {
public void save();
public void delete();
public void update();
public int query();
public void batch();
}
- 编写实现类,实现接口
@Service
public class UserDaorel02 implements UserDao {
@Override
public void save() {
System.out.println("执行save");
}
@Override
public void delete() {
System.out.println("执行delete");
}
@Override
public void update() {
System.out.println("执行update");
}
@Override
public int query() {
System.out.println("执行query");
return 100;
}
@Override
public void batch() {
System.out.println("执行batch");
}
}
- 编写代理类
@Component
// 使用@Aspect表示这个类就是切面类
@Aspect
public class MyProxy {
/**
* 定义前置通知
*/
@Before("execution(* save*(..))")
public void saveMethodProxy() {
System.out.println("执行之前进行权限校验");
}
/**
* 定义后置通知
*/
@AfterReturning(value = "execution(* query*(..))")
public void afterMethodProxy() {
System.out.println("我是后置通知");
}
/**
* 定义环绕通知
*
* @throws Throwable
*/
@Around(value = "execution(* update*(..))")
public void aroundMethodProxy(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("我是环绕通知,在之前执行");
// 通过执行proceed方法才会执行我们目标类中的方法
joinPoint.proceed();
System.out.println("我是环绕通知,在之后执行");
}
/**
* 异常通知
*
* @throws Throwable
*/
@AfterThrowing(value = "execution(* delete*(..))")
public void exceptionMethodProxy(){
System.out.println("我是异常通知,在出现异常的时候执行");
}
/**
* 最终通知
*
* @throws Throwable
*/
@After(value = "execution(* batch*(..))")
public void afterMethodProxy2() {
System.out.println("我是最终通知,就算程序有异常,也会执行");
}
}
- 最后写个测试方法测试一下
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext2.xml")
public class TestUserDao {
@Autowired
private PeopleService peopleService;
@Test
public void testSaveMethod() {
this.peopleService.save();
this.peopleService.query();
this.peopleService.update();
this.peopleService.delete();
this.peopleService.batch();
}
}