• Spring中实现AOP的功能


    Java知识点总结:想看的可以从这里进入

    13.4、AOP的实现

    AOP它是一种思想,并非是Spring 特有,Spring只是支持了AOP的框架之一,它是方法级别的AOP框架,主要是以某个类的某个方法为连接点,实现动态代理

    在Spring中有4种方式实现AOP,在使用时基本是以 @AspectJ 注解 为主,XML配置为辅实现AOP。

    1. 通过ProxyFactory和对应接口
    2. XML配置AOP
    3. @AspectJ注解实现AOP
    4. 使用 Aspectj 注入切面
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aopartifactId>
        <version>6.0.3version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aopartifactId>
        <version>5.3.21version>
    dependency>
    
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aspectsartifactId>
        <version>6.0.3version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aspectsartifactId>
        <version>5.3.21version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    13.4.1、通过XML实现
    • 创建dao,模拟从数据库中获取数据

      @Bean("userList")
      public List<User> getUserList(){
          List<User> userList = new ArrayList<>();
          userList.add(new User(1,"用户1","123456",false));
          userList.add(new User(2,"用户2","234567",false));
          userList.add(new User(3,"用户3","345678",false));
          userList.add(new User(4,"用户4","456789",false));
          userList.add(new User(5,"用户5","567890",false));
          return userList;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      @Repository
      public class ProxyTestUserDao {
          @Resource
          private List<User> userList;
      
          //改
          public void update(User user) {
              boolean exists = true;
              for (int i=0; i<userList.size(); i++){
                  if(user.getUserId().equals(userList.get(i).getUserId())){
                      exists = false;
                      userList.set(i,user);
                      break;
                  }
              }
              if(exists){
                  System.out.println("没有此用户");
              }
          }
          //删
          public void delete(int id) {
              boolean exists = false;
              for (int i=0; i<userList.size(); i++){
                  if(id == userList.get(i).getUserId()){
                      exists = true;
                      userList.remove(i);
                      break;
                  }
              }
              if(!exists){
                  System.out.println("没有此用户");
              }
          }
          //查
          public User select(int id) {
              for (User user : userList) {
                  if (id == user.getUserId()) {
                      return user;
                  }
              }
              return null;
          }
          public List<User> selectAll() {
              return userList;
          }
          //增加
          public void insert(User user) {
              int id = userList.get(userList.size()-1).getUserId();
              id += 1;
              user.setUserId(id);
              userList.add(user);
              System.out.println("增加成功");
          }
      
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
    • 创建service,用其作为连接点

      @Service
      public class ProxyTestUserServiceImpl implements ProxyTestUserService {
          @Autowired
          private ProxyTestUserDao proxyTestUserDao;
      
          @Override
          public void update(User user) {
              proxyTestUserDao.update(user);
          }
          @Override
          public void delete(int id) {
              proxyTestUserDao.delete(id);
          }
          @Override
          public User select(int id) {
              return proxyTestUserDao.select(id);
          }
          @Override
          public List<User> selectAll() {
              return proxyTestUserDao.selectAll();
          }
          @Override
          public void insert(User user) {
              proxyTestUserDao.insert(user);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
    • 创建切面

      public class UserServiceInterceptor implements Interceptor {
          /**
           * 前置通知
          */
          public void before(JoinPoint joinPoint) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              //获取目标方法的实参信息
              String args = Arrays.toString(joinPoint.getArgs());
              System.out.println("前置通知,方法名:"+methodName+",参数:"+args);
          }
          /**
           * 后置通知
           */
          public void after(JoinPoint joinPoint) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              System.out.println("后置通知,方法名:"+methodName);
          }
      
          /**
           * 返回通知
           */
          public void afterReturning(JoinPoint joinPoint, Object result) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              System.out.println("返回通知,方法名:"+methodName);
          }
      
          /**
           * 异常通知
           */
          public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              System.out.println("异常通知,方法名:"+methodName+",异常:"+ex);
          }
      
          /**
           * 环绕通知
           */
          public Object around(ProceedingJoinPoint point){
              System.out.println("环绕通知开始");
              //获取连接点的名字
              String methodName = point.getSignature().getName();
              //获取连接点的参数
              String args = Arrays.toString(point.getArgs());
              System.out.println("环绕通知,方法名:"+methodName+",参数:"+args);
              Object o = null;
              try {
                  System.out.println("环绕通知:目标对象方法执行之前");
                  //目标对象(连接点)方法的执行
                  o = point.proceed();
                  System.out.println("环绕通知:目标对象方法返回值之后");
              } catch (Throwable throwable) {
                  throwable.printStackTrace();
                  System.out.println("环绕通知:目标对象方法出现异常时");
              } finally {
                  System.out.println("环绕通知:目标对象方法执行完毕");
              }
              return  o;
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
    • 在XML文件中配置

      
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             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/aop
                  https://www.springframework.org/schema/aop/spring-aop.xsd">
      	
          
          <bean id="interceptor" class="com.yu.spring.aspect.UserServiceInterceptor"/>
          <aop:config>
              
              <aop:aspect id="interceptor" ref="interceptor">
                  
                  <aop:pointcut id="service" expression="execution(* com.yu.spring.service.impl.UserServiceImpl.*(..))"/>
                  
                  <aop:before method="before" pointcut-ref="service"/>
                  
                  <aop:after-returning method="afterReturning" returning="result" pointcut-ref="service"/>
                  
                  <aop:after method="after" pointcut-ref="service"/>
                  
                  <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="service"/>
                  
                  <aop:around method="around" pointcut-ref="service"/>
              aop:aspect>
      
          aop:config>
      beans>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
    • 测试

      image-20230330113659918

    13.4.2、通过注解实现

    实现AOP的主流方式,它的常用注解:

    1@Aspect:用于表明一个类为切面
    2@Before("execution(* com.yu.service.impl.UserServiceImpl.*(..))"):将一个方法表示为前置通知,并设置切入点
    3@After("execution()"):后置通知
    4@AfterThrowing("execution()"):发生异常后执行
    5@AfterReturning("execution()"):无异常时执行
    6@Around:环绕通知
    	切点:execution()表达式
        execution:执行真实对象的方法时触发
        *:任意返回类型的方法
        com.yu.service.impl.UserServiceImpl:真实类的全限定名
        *:写真实对象中具体拦截的方法(*是所有)
        (..):任意参数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    Aspectj的指示器
    1、arg():限制连接点匹配参数为指定类型
    2、@args():限制连接点匹配参数为指定注解标注的执行方法
    3、execution:匹配连接点,最常用
    4、this():限制连接点匹配AOP代理的bean,引用为指定类型的类
    5、target:限制连接点匹配被代理对象为指定类型
    6、within():限制连接点匹配的包
    7、annotation:限制匹配带有指定注解的连接点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • Service、dao等不变

    • 创建切面,使用上面的几种注解

      @Aspect
      @Component
      public class UserServiceImplAspect {
          /**
           * 前置通知
           */
          @Before("execution(* com.yu.spring.service.impl.ProxyTestUserServiceImpl.*(..))")
          public void before(JoinPoint joinPoint) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              //参数
              String args = Arrays.toString(joinPoint.getArgs());
              System.out.println("前置通知(注解),方法名:"+methodName+",参数:"+args);
          }
      
          /**
           * 后置通知
           */
          @After("execution(* com.yu.spring.service.impl.ProxyTestUserServiceImpl.*(..))")
          public void after(JoinPoint joinPoint) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              System.out.println("后置通知(注解),方法名:"+methodName);
          }
      
      
          /**
           * 返回通知
           */
          @AfterReturning(value = "execution(* com.yu.spring.service.impl.ProxyTestUserServiceImpl.*(..))" , returning = "result")
          public void afterReturning(JoinPoint joinPoint, Object result) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              System.out.println("返回通知(注解),方法名:"+methodName);
          }
      
          /**
           * 异常通知
           */
          @AfterThrowing(value ="execution(* com.yu.spring.service.impl.ProxyTestUserServiceImpl.*(..))", throwing = "ex")
          public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
              //获取连接点的名字
              String methodName = joinPoint.getSignature().getName();
              System.out.println("异常通知(注解),方法名:"+methodName+",异常:"+ex);
          }
      
          /**
           * 环绕通知
           */
          @Around("execution(* com.yu.spring.service.impl.ProxyTestUserServiceImpl.*(..))")
          public Object around(ProceedingJoinPoint point){
              System.out.println("环绕通知开始(注解)");
              String methodName = point.getSignature().getName();
              String args = Arrays.toString(point.getArgs());
              System.out.println("环绕通知(注解),方法名:"+methodName+",参数:"+args);
              Object o = null;
              try {
                  System.out.println("环绕通知(注解):目标对象方法执行之前");
                  //目标对象(连接点)方法的执行
                  o = point.proceed();
                  System.out.println("环绕通知(注解):目标对象方法返回值之后");
              } catch (Throwable throwable) {
                  throwable.printStackTrace();
                  System.out.println("环绕通知(注解):目标对象方法出现异常时");
              } finally {
                  System.out.println("环绕通知(注解):目标对象方法执行完毕");
              }
              return  o;
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
    • 在主配置类使用注解开启aop

      @Configuration
      @ComponentScan("com.yu.spring")
      //支持处理标记有 AspectJ 的@Aspect注释的组
      @EnableAspectJAutoProxy
      public class AOPConfig {
          
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 测试,通过配置类

      image-20230330114336341

    13.4.3、api接口实现

    使用Spring的aip接口实现:

    1. 前置通知 org.springframework.aop.MethodBeforeAdvice
    2. 后置通知 org.springframework.aop.AfterReturningAdvice
    3. 环绕通知 org.aopalliance.intercept.MethodInterceptor
    4. 异常抛出通知 org.springframework.aop.ThrowsAdvice
    5. 引介通知 org.springframework.aop.IntroductionInterceptor

    测试一下:

    • service层

      public class StudentServiceImpl implements StudentService {
          @Override
          public void learning() {
              System.out.println("正在学习");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 前置通知和后置通知类

      public class BeforeAdvice implements MethodBeforeAdvice {
          /**
           * @param method:目标对象的方法
           * @param objects:参数
           * @param o:目标对象
           */
         @Override
          public void before(Method method, Object[] objects, Object o) throws Throwable {
              System.out.println("前置通知执行了"+o.getClass().getName()+"的"+method.getName()+"方法");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      public class MyAfterLog2 implements AfterReturningAdvice {
      
          @Override
          public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
              System.out.println("执行"+method.getName()+"方法后,返回结果为:"+returnValue);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • xml配置

      
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             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/aop
              https://www.springframework.org/schema/aop/spring-aop.xsd">
      
         <bean id="studentService" class="com.yu.service.impl.StudentServiceImpl"/>
         <bean id="afterAdvice" class="com.yu.aopaip.AfterAdvice"/>
         <bean id="beforeAdvice" class="com.yu.aopaip.BeforeAdvice"/>
          
         <aop:config>
              
            <aop:pointcut id="point" expression="execution(* com.yu.service.impl.StudentServiceImpl.*(..))"/>
            <aop:advisor advice-ref="afterAdvice" pointcut-ref="point"/>
            <aop:advisor advice-ref="beforeAdvice" pointcut-ref="point"/>
      
         aop:config>
      beans>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    • 测试

      public class AppTest {
          @Test
          public void test(){
              ApplicationContext context = new ClassPathXmlApplicationContext("spring2.xml");
              StudentService service = (StudentService)context.getBean("studentService");
              service.learning();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      image-20220912193933401
    13.4.4、ProxyFactoryBean

    ProxyFactoryBean是Spring环境中给指定的bean创建代理的一种方式,

    • service层

      public class StudentServiceImpl implements StudentService {
          @Override
          public void learning() {
              System.out.println("正在学习");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • XML方式

      • 前置通知和后置通知类

        public class AfterAdvice implements AfterReturningAdvice {
        
            @Override
            public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
                System.out.println("后置通知执行"+method.getName()+"方法后,返回结果为:"+o);
            }
        }
        public class BeforeAdvice implements MethodBeforeAdvice  {
            @Override
            public void before(Method method, Object[] objects, Object o) throws Throwable {
                System.out.println("前置通知执行了"+o.getClass().getName()+"的"+method.getName()+"方法");
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
      • xml配置

        <?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: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/aop
                    https://www.springframework.org/schema/aop/spring-aop.xsd">
        
           <bean id="studentService" class="com.yu.service.impl.StudentServiceImpl"/>
           <bean id="afterAdvice" class="com.yu.aopaip.AfterAdvice"/>
           <bean id="beforeAdvice" class="com.yu.aopaip.BeforeAdvice"/>
           <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
              <property name="target" ref="studentService"/>
              <property name="interceptorNames">
                 <list>
                    <value>afterAdvice</value>
                    <value>beforeAdvice</value>
                 </list>
              </property>
           </bean>
        </beans>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
      • 获取proxyFactoryBean

        public class AppTest {
            @Test
            public void test(){
                ApplicationContext context = new ClassPathXmlApplicationContext("spring2.xml");
                StudentService service = (StudentService)context.getBean("proxyFactoryBean");
                service.learning();
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        image-20220912200645554
    • 注解

      • 在@Configuration中注册前置、后置通知,和proxyFactoryBean

        @Configuration
        @ComponentScan
        @EnableAspectJAutoProxy
        public class ExplainConfiguration {
            @Bean
            public StudentService service(){
                return new StudentServiceImpl();
            }
            @Bean
            public MethodBeforeAdvice beforeAdvice() {
                return new MethodBeforeAdvice() {
                    @Override
                    public void before(Method method, Object[] objects, Object o) throws Throwable {
                        System.out.println("前置通知执行了"+o.getClass().getName()+"的"+method.getName()+"方法");
                    }
                };
            }
            //注册一个后置通知
            @Bean
            public AfterReturningAdvice afterAdvice() {
                return new AfterReturningAdvice(){
                    @Override
                    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
                        System.out.println("后置通知执行"+method.getName()+"方法后,返回结果为:"+o);
                    }
                };
            }
            //注册ProxyFactoryBean
            @Bean
            public ProxyFactoryBean service1Proxy() {
                //1.创建ProxyFactoryBean
                ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
                //2.设置目标对象的bean名称
                proxyFactoryBean.setTarget(service());
                //3.设置拦截器的bean名称列表,此处2个(advice1和advice2)
                proxyFactoryBean.setInterceptorNames("beforeAdvice", "afterAdvice");
                return proxyFactoryBean;
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
      • 获取ProxyFactoryBean测试

        public class AppTest {
            @Test
            public void test(){
                ApplicationContext context = new AnnotationConfigApplicationContext(ExplainConfiguration.class);
                StudentService service = (StudentService)context.getBean("service1Proxy");
                service.learning();
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        image-20220912201541663
  • 相关阅读:
    VB.Net 任务管理器相关操作
    【Python 千题 —— 基础篇】奇数列表
    redis2
    Linux下Jenkins服务器安装与使用
    【总结】助力2022年全国大学生数学建模 — 评价模型总结篇(万字总结)
    高并发内存池项目(C++实战项目)
    Java常用设计模式
    hive的安装配置及使用
    MATLAB设计ATF教程
    Dubbo简介
  • 原文地址:https://blog.csdn.net/yuandfeng/article/details/126822146