• spring aop简单理解


    aop原理是spring帮我们封装了动态代理,然后我们只管写具体的业务,我们将公共业务也写到具体的一个类中并实现spring为我们提供的对应要连接切入哪个位置的接口,然后再xml中配置它们的关系即可。

    优点:和动态代理一样,具体实现只管具体实现使的代码更加纯粹,公共业务只需实现自己对应的接口,然后编码即可,有了动态代理的好处,又没有手写动态代理那么复杂,这就是aop(被分装后的动态代理)。

    一:实现spring接口的方式

    1.UserService接口:

    package mr.li.service;
    
    public interface UserService {
    
        void add();
        
        void remove();
        
    }

    2.UserServiceImpl实现类:

    package mr.li.service.impl;
    
    import mr.li.service.UserService;
    
    public class UserServiceImpl implements UserService{
    
        @Override
        public void add() {
            System.out.println("添加一条数据");
        }
    
        @Override
        public void remove() {
            System.out.println("删除一条数据");
        }
    
    }

    3.前置通知:Log日志打印

    package mr.li.log;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.MethodBeforeAdvice;
    /**
     * 前置切面:日志打印
     * @author yanLong.Li
     * @date 2019年3月16日 下午10:33:22
     */
    public class Log implements MethodBeforeAdvice{
    
        @Override
        public void before(Method method, Object[] arg1, Object target) throws Throwable {
            System.out.println("aop前置:"+target.getClass().getName()+"类的"+ method + "方法执行了~~");
        }
    
    }

    4后置通知:AfterLog 日志打印

    package mr.li.log;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.AfterReturningAdvice;
    /**
     * 后置切面:日志打印
     * @author yanLong.Li
     * @date 2019年3月16日 下午10:33:44
     */
    public class AfterLog implements AfterReturningAdvice{
    
        @Override
        public void afterReturning(Object result, Method method, Object[] arg2, Object target) throws Throwable {
            System.out.println("aop后置:"+target.getClass().getName()+"类的"+ method + "方法执行了,返回结果为"+result);
        }
    }

    5.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
        http://www.springframework.org/schema/aop/spring-aop.xsd">
           <bean id="userService" class="mr.li.service.impl.UserServiceImpl"/> 
           <bean id = "log" class="mr.li.log.Log"/>
           <bean id = "afterLog" class="mr.li.log.AfterLog"/>
           <aop:config>
                  <!-- 配置被切入的哪个类使用 execution表达式第一个:“*”表示返回值 第二个*表示所有的 ..标识所有的参数 -->
                  <aop:pointcut expression="execution(* mr.li.service.impl.UserServiceImpl.*(..))" id="aoop"/>
                  <!-- 设置要切入的类:-->
                  <aop:advisor advice-ref="log" pointcut-ref="aoop"/>
                  <aop:advisor advice-ref="afterLog" pointcut-ref="aoop"/>
           </aop:config> 
    </beans>

    6.测试类

    package mr.li.client;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import mr.li.service.UserService;
    
    public class Client {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) applicationContext.getBean("userService");
            userService.remove();
            userService.add();
        }
    }

    打印结果:

    aop前置:mr.li.service.impl.UserServiceImpl类的public abstract void mr.li.service.UserService.remove()方法执行了~~
    删除一条数据
    aop后置:mr.li.service.impl.UserServiceImpl类的public abstract void mr.li.service.UserService.remove()方法执行了,返回结果为null
    aop前置:mr.li.service.impl.UserServiceImpl类的public abstract void mr.li.service.UserService.add()方法执行了~~
    添加一条数据
    aop后置:mr.li.service.impl.UserServiceImpl类的public abstract void mr.li.service.UserService.add()方法执行了,返回结果为null

    二:自定义aop,切面不在需要实现接口,自己写即可,只是调整下配置

    1.UserService:和上面一样

    2.UserServiceImpl:和上面一样

    3.Log切面

    package mr.li.log;
    
    /**
     * 前置切面:日志打印
     * @author yanLong.Li
     * @date 2019年3月16日 下午10:33:22
     */
    public class Log {
    
        public void before() {
            System.out.println("前置通知");
        }
        
        public void after() {
            System.out.println("后置通知");
        }
    
    }

    4.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
        http://www.springframework.org/schema/aop/spring-aop.xsd">
           <bean id="userService" class="mr.li.service.impl.UserServiceImpl"/> 
           <bean id = "log" class="mr.li.log.Log"/>
           <aop:config>
                   <aop:aspect ref="log">
                       <!-- 配置被切入的哪个类使用 execution表达式 *表示所有的 ..标识所有的参数 -->
                          <aop:pointcut expression="execution(* mr.li.service.impl.UserServiceImpl.*(..))" id="pointcut"/>
                       <!-- 配置前置通知 -->
                       <aop:before method="before" pointcut-ref="pointcut"/>
                       <!-- 配置后置通知 -->
                       <aop:after method="after" pointcut-ref="pointcut"/>
                   </aop:aspect>
           </aop:config> 
    </beans>

    5.测试

    package mr.li.client;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import mr.li.service.UserService;
    
    public class Client {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) applicationContext.getBean("userService");
            userService.remove();
            userService.add();
        }
    }

    打印结果:

    前置通知
    删除一条数据
    后置通知
    前置通知
    添加一条数据
    后置通知

    三:注解的方式:注意下环绕通知,参数说明,以及使用(proceedingJoinpoint在下面有额外解释)

    1.UserService:和最上面一样

    2.UserServiceImpl:和最上面一样

    3.切面:Log

    package mr.li.log;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    /**
     * 切面:日志打印
     * @author yanLong.Li
     * @date 2019年3月16日 下午10:33:22
     */
    @Aspect
    public class Log {
    
        /**
         * 前置通知:execution表示切入点
         */
        @Before("execution(* mr.li.service.impl.UserServiceImpl.*(..))")
        public void before() {
            System.out.println("注解:前置通知");
        }
        
        /**
         * 后置通知
         */
        @After("execution(* mr.li.service.impl.UserServiceImpl.*(..))")
        public void after() {
            System.out.println("注解:后置通知");
        }
    
        /**
         * 环绕通知
         * @param jp 此参数是spring给我们的,它里面可以干很多事,包括继续执行被切面的方法,拿到方法签名,他会在被切面的方法之前运作
         * @throws Throwable 
         */
        @Around("execution(* mr.li.service.impl.UserServiceImpl.*(..))")
        public Object aroud(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("环绕前");
            System.out.println("方法签名:"+jp.getSignature());
            //执行此方法
            Object result = jp.proceed();
            System.out.println("环绕后");
            return result;
        }
    }

    4.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
        http://www.springframework.org/schema/aop/spring-aop.xsd">
           <bean id="userService" class="mr.li.service.impl.UserServiceImpl"/> 
           <bean id = "log" class="mr.li.log.Log"/>
           <!-- 此配置会自动去找aop配置 -->
           <aop:aspectj-autoproxy/> 
    </beans>

    5.测试:

    package mr.li.client;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import mr.li.service.UserService;
    
    public class Client {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
            UserService userService = (UserService) applicationContext.getBean("userService");
            userService.remove();
            userService.add();
        }
    }

    打印结果:

    注解:前置通知
    删除一条数据
    环绕后
    注解:后置通知
    环绕前
    方法签名:void mr.li.service.UserService.add()
    注解:前置通知
    添加一条数据
    环绕后
    注解:后置通知

     额外对ProceedingJoinPoint参数的描述

    package com.qty.arena.util;
    
    import java.util.Set;
    import java.util.concurrent.ConcurrentSkipListSet;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    import com.qty.arena.helper.MessagePushHelper;
    import com.qty.database.dto.arena.match.SyncAll;
    
    /**
     * 在被@NotDuplicate注解所标注的方法上,过滤请求者重复请求的消息
     * @author yanLong.Li
     * @date 2019年2月11日 上午11:11:01
     */
    @Aspect
    @Component
    public class NotDuplicateAop {
    
    //    private static final Set<String> KEY =  new ConcurrentSkipListSet<>();
    
        private static final Set<Long> KEY =  new ConcurrentSkipListSet<>();
        
        @Pointcut("@annotation(com.qty.arena.util.NotDuplicate)")
        public void duplicate() {}
    
        /**
         * 对方法拦截后进行参数验证
         * @param pjp
         * @return
         * @throws Throwable
         */
        @Around("duplicate()")
        public Object duplicate(ProceedingJoinPoint pjp) throws Throwable{
            Object[] objs = pjp.getArgs();
            Long dbId = null;
            if(objs[0] instanceof Long) {
                dbId = (Long)objs[0];                 
            }
            if(dbId == null) {
                throw new NumberFormatException("在解析请求的dbId时发生异常,数字不能解析,值:"+objs[0]);
            }
            boolean success = KEY.add(dbId);
            if(!success){
                MessagePushHelper.getInstance().pushSingleMessage(dbId, SyncAll.valueOf("频繁操作,请稍后再试"));
                return null;
            }
            try {
                //方法执行前
                return pjp.proceed();
            } finally {
                //方法执行后
                KEY.remove(dbId);
            }
        }
  • 相关阅读:
    Spring事务内方法调用自身事务 增强的三种方式
    SpringBoot优化内嵌的Tomcat
    Tomcat 8.0的并发优化
    Swift搭建本地http服务器,实现外部视频即时播放
    更新ruby:Error running 'requirements_osx_brew_update_system ruby-2.4.1报错解决
    iOS关于沙盒文件拷贝manager.copyItem的一个坑
    Swift udp实现根据端口号监听广播数据(利用GCDAsyncUdpSocket实现)
    iOS刻度尺换算之1mm等于多少像素理解
    Swift下的基于UIView的位置属性扩展
    iOS Main Thread Checker: UI API called on a background thread的解释
  • 原文地址:https://www.cnblogs.com/li-yan-long/p/10544772.html
Copyright © 2020-2023  润新知