文章目录
1、AOP的概念
1、什么是AOP
- 面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- 通俗理解:不通过修改源代码的方式,就可以在主干功能中添加新的功能。
- 使用登录的例子来说明AOP操作
2、AOP底层原理
AOP底层使用动态代理
-
第一种有接口情况,使用JDK动态代理创建接口实现类代理对象,增强类的方法(下面会介绍这种方式)
-
第二种没有接口情况,使用CGLIB动态代理创建子类的代理对象,增强类里面的方法
2、AOP底层原理(JDK动态代理代码,多理解理解)
1、使用JDK动态代理,需要使用proxy类里面的方法创建代理对象
(1)需要调用newProxyInstance方法
方法里面有三个参数:
- classLoder:类加载器
- 增强的方法所在的类所实现的接口的集合,支持多个接口共同传入
- 实现接口InvocationHandler的类,这个参数传入的就是代理对象,在这个类中写入增强的方法
2、编写JDK动态代理代码
- 创建接口UserDao,并创建类UserDaoIml实现UserDao接口
public interface UserDao {
public int add(int a,int b);
}
public class UserDaoIml implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add方法执行了");
return a+b;
}
}
- 创建UserDaoProxy类,实现接口InvocationHandler,重写invoke方法
class UserDaoProxy implements InvocationHandler{
//把创建的是谁的代理对象,把谁传递过来
//有参构造传递
private Object obj;
public UserDaoProxy(Object obj){
this.obj=obj;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行"+method.getName()+":传递的参数..."+ Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj,args);
//方法之后
System.out.println("方法之后执行..."+obj);
return res;
}
}
- 创建JDKProxy,实现方法的增强、添加
public class JDKProxy {
public static void main(String[] args) {
//用于存储接口的数组,用于newProxyInstance函数的第二个参数
Class[] interfaces = {UserDao.class};
//将被增强的方法所在类作为参数传入InvocationHandler接口实现类
UserDaoIml userDaoIml = new UserDaoIml();
UserDao userDao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDaoIml));
System.out.println(userDao.add(1,2));
}
}
- 测试结果
方法之前执行add:传递的参数...[1, 2]
add方法执行了
方法之后执行...spring.UserDaoIml@179d3b25
执行结果为:3
3、AOP操作术语
- 连接点(JoinPoint):类里面的那些方法可以被增强,也就是Spring里面允许你使用通知的地方,这个就有很多了,基本上所有的方法前后都可以使用通知。
- 切入点(Pointcut):类里面实际被增强的方法
- 通知(Advice)(注:增强方法的逻辑部分,在被用于增强的方法上书写注释,下面会介绍部分注释的写法)
通知的类型:
- 前置通知:在需要增强的方法前执行——@Before
- 后置通知:不管什么情况都会执行,即使有异常也会执行——@After
- 环绕通知:在需要增强的方法前后都要执行——@Around
- 异常通知:在需要增强的方法出现异常时执行——@AfterThrowing
- 返回通知:在需要增强的方法后执行——@AfterReturning
- 切面(Aspect):把通知应用到切入点的过程,这是一个动作
4、AspectJ
1、AspectJ介绍
-
Spring框架一般都是基于AspectJ实现的AOP操作
-
AspectJ不是Spring组成部分,这是一个独立的AOP框架,一般把AspectJ和Spring框架一起使用来实现AOP操作
-
基于AspectJ的实现AOP操作主要有两种方式:
- 基于xml配置文件
- 基于注解方式实现(在项目中一般使用该方式)
2、切入点表达式
@Before(value = "切入点表达式放入的位置")
(1)切点表达式的作用:指明对某个类里面的某个方法进行增强
(2)语法结构:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
- 举例1:
execution(*spring.Userdao.add(..))
含义:对spring.Userdao接口中的add方法进行增强 - 举例2:
execution(*spring.Userdao.*(..))
含义:对Spring.Userdao中的所有方法进行增强 - 举例3:
execution(public double spring.Userdao.*(..))
含义:对Spring.Userdao中返回类型为double类型的方法进行增强 - 举例4:
execution (* *.add(int,..)) || execution(* *.sub(int,..))
含义:对任意类中第一个参数为int类型的add方法或sub方法进行增强
5、利用AspectJ基于注解方式实现AOP操作(代码演示)
1、代码演示
-
在项目工程里面引入AOP相关的依赖
AspectJ:aspectjweaver下载地址:
https://www.eclipse.org/downloads/download.php?file=/tools/aspectj/aspectj-1.9.6.jar
aopalliance.jar下载地址:
https://sourceforge.net/projects/aopalliance/files/aopalliance/1.0/aopalliance.zip/download
cglib-2.2.0.jar下载地址:
http://www.xwood.net/site_domain/_root/5870/5930/5932/9330/9352/t_c41554.html -
创建一个User类,在User类中创建方法
@Component (value = "user")
public class User {
public void add(){
System.out.println("add......");
}
}
- 创建Userproxy增强类,在增强类中创建方法,添加了注解@Aspect
环绕通知:
- 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
- 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
- 环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
@Component
@Aspect
public class Userproxy {
@Before(value = "execution(* Aop.User.add(..))")
public void before(){
System.out.println("@Before 前置通知");
}
@After(value = "execution(* Aop.User.add(..))")
public void after(){
System.out.println("@After 后置通知");
}
@AfterThrowing(value = "execution(* Aop.User.add(..))")
public void afterThrowing(){
System.out.println("@AfterThrowing 异常通知");
}
@Around(value = "execution(* Aop.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("@Around 环绕通知前");
proceedingJoinPoint.proceed();
System.out.println("@Around 环绕通知后");
}
@AfterReturning(value = "execution(* Aop.User.add(..))")
public void afterreturning(){
System.out.println("@AfterReturning 返回后通知");
}
}
- 在Spring配置文件中进行通知(代理)的配置
<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">
<!--开启注解扫描,需要引入名称空间-->
<context:component-scan base-package="Aop"></context:component-scan>
<!--开启AspectJ生成代理对象,需要引入名称空间-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- 创建测试类,进行测试
public class Test_AOP {
@Test
public void testAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user",User.class);
user.add();
}
}
- 测试结果
@Around 环绕通知前
@Before 前置通知
add......
@AfterReturning 返回后通知
@After 后置通知
@Around 环绕通知后
2、基于AspectJ的进阶操作
- 可以利用@Pointcut注解对相同的切入点表达式进行抽取
@Pointcut(value = "execution(* Aop.User.add(..))")
public void pointdemo(){
}
@Before(value = "pointdemo()")
public void before(){
System.out.println("before.....");
}
- 如果有多个增强类对同一个方法进行增强,可以设置增强类的优先级
注:在增强类上面添加注解@Order(数字类型值),数字类型值越小它的优先级越高
@Component
@Aspect
@Order(1)
public class personProxy {
@Before(value = "execution(* Aop.User.add(..))")
public void before(){
System.out.println("person Before..........");
}
}
@Component
@Aspect
@Order(2)
public class UserProxy {
@Before(value = "execution(* Aop.User.add(..))")
public void before(){
System.out.println("User Before..........");
}
}
6、完全使用注解实现AOP操作
- 创建一个配置类
@Configuration:表明这是一个配置类
@ComponentScan(basePackages = {“Aop”}):表示扫描包的路径为:Aop
@EnableAspectJAutoProxy(proxyTargetClass = true):表示开启AOP自动代理
@Configuration
@ComponentScan(basePackages = {"Aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class configAOP {
}
- 创建测试类,进行测试(测试类和测试结果与上面的代码演示相同)
@Test
public void testAop1(){
ApplicationContext context = new AnnotationConfigApplicationContext(configAOP.class);
User user = context.getBean("user", User.class);
user.add();
}
❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤
若对Spring基础知识感兴趣的可以关注一下博主,我会持续更新Spring基础知识(一边学习一边记录),一起进步,有错误的地方也可以在评论区指出来喔,谢谢大家啦!!!