zoukankan      html  css  js  c++  java
  • Spring学习之路3-AOP及实现AOP的三种方式

    AOP

    面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计、剖面导向程序设计)是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与装饰,如“对所有方法名以‘set*’开头的方法添加后台日志”。该思想使得开发人员能够将与代码核心业务逻辑关系不那么密切的功能(如日志功能)添加至程序中,同时又不降低业务代码的可读性。面向切面的程序设计思想也是面向切面软件开发的基础。

    Maven 依赖

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    

    实现方式一:原生 Spring API 接口

    public interface UserService {
        void add();
        int delete();
        void query();
        int update();
    }
    
    public class UserServiceImpl implements UserService {
        @Override
        public void add() {
            System.out.println("function:add()");
        }
    
        @Override
        public int delete() {
            System.out.println("function:delete()");
            return 0;
        }
    
        @Override
        public void query() {
            System.out.println("function:query()");
        }
    
        @Override
        public int update() {
            System.out.println("function:update()");
            return 0;
        }
    }
    
    //方法执行前的log
    public class BeforeLog implements MethodBeforeAdvice {
    
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
    
            System.err.println("method before log" + target.getClass().getName() + method.getName());
        }
    }
    
    //方法执行后的log
    public class AfterLog implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.err.println("method before log" + target.getClass().getName() + "-->" + method.getName() + "---return:" + returnValue);
        }
    }
    
    <?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="log1" class="com.youzi.log.BeforeLog"/>
        <bean id="log2" class="com.youzi.log.AfterLog"/>
        <bean id="userservice" class="com.youzi.service.UserServiceImpl"/>
    
        <!--配置AOP-->
        <aop:config>
            <aop:pointcut id="pointcut1" expression="execution(int com.youzi.service.UserServiceImpl.*(..))"/>
            <aop:pointcut id="pointcut2" expression="execution(void com.youzi.service.*.add(..))"/>
            <aop:advisor advice-ref="log1" pointcut-ref="pointcut1"/>
            <aop:advisor advice-ref="log2" pointcut-ref="pointcut2"/>
        </aop:config>
    </beans>
    

    execution() 切点函数语法:

    execution(返回类型 包名.类名.方法名(参数类型))
    
    • 也可以用*号表示所有的类型。

    • 类名也可以用*代替表示包下面的所有类

    • *(..):这个星号表示方法名,*号表示所有的方法,括号中是方法的参数,两个点表示任何参数。

    实现方式二:自定义类

    第一种实现方式虽然实现了在方法前或者方法后添加日志,但是每实现一个功能就需要实现一个接口,通过下面的方法可以把这些方法写在一个类中

    public class MethodLog {
        public void beforeMethod() {
            System.out.println("方法执行前");
        }
    
        public void afterMethod() {
            System.out.println("方法执行后");
        }
    }
    
    <?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="MethodLog" class="com.youzi.log.MethodLog"/>
        <bean id="userservice" class="com.youzi.service.UserServiceImpl"/>
    
        <aop:config>
            <aop:aspect ref="MethodLog">
                <aop:pointcut id="poindcut1" expression="execution(* com.youzi.service.*.*(..))"/>
                <aop:before method="beforeMethod" pointcut-ref="poindcut1"/>
                <aop:after method="afterMethod" pointcut-ref="poindcut1"/>
            </aop:aspect>
        </aop:config>
    </beans>
    

    上面的方法虽然简化了代码,但是目前看来不能像第一种方式一样通过反射取到执行方法的信息

    实现方式三:使用注解

    <?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="MethodLog" class="com.youzi.log.MethodLog"/>
        <bean id="userservice" class="com.youzi.service.UserServiceImpl"/>
        <!--在xml中声明使用注解的方式-->
        <aop:aspectj-autoproxy/>
    </beans>
    
    @Aspect	
    public class MethodLog {
        @Before("execution(* com.youzi.service.UserServiceImpl.*(..))")
        public void beforeMethod() {
            System.out.println("方法执行前1");
        }
    
        @After("execution(* com.youzi.service.UserServiceImpl.*(..))")
        public void afterMethod() {
            System.out.println("方法执行后1");
        }
    
        @Around("execution(* com.youzi.service.UserServiceImpl.update(..))")
        public int aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
            System.err.println("环绕前");
            System.err.println(joinPoint.getSignature());
            Object proceed = joinPoint.proceed();
            System.err.println("环绕后");
            return Integer.valueOf((Integer) proceed);
        }
    
    }
    

    测试代码统一为

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = context.getBean("userservice", UserService.class);
    userService.query();
    userService.add();
    userService.delete();
    userService.update();
    
  • 相关阅读:
    Java File类应用:递归遍历文件夹和递归删除文件
    综合应用题:多线程复制文件(知识点:多线程、随机读写流)
    Java IO流之随机读写流RandomAccessFile
    Java之枚举----小试牛刀练习
    Java之枚举
    PHP笔试题及答案
    静态方法何时使用
    你怎么理解并使用静态方法和实例化方法的?
    99%的人理解错 HTTP 中 GET 与 POST 的区别
    描述一下大流量高并发量网站的解决方案
  • 原文地址:https://www.cnblogs.com/wangjr1994/p/12528806.html
Copyright © 2011-2022 走看看