• Spring框架——AOP面向切面编程



    简介

    AOP(面向切面编程):是一种新的方法论,是对传统OOP(面向对象编程)的补充。

    AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。

    AOP的好处:

    每个事物逻辑位于一个位置,代码不分散,便于维护和升级。

    业务模块更简洁,只包含核心业务的代码。

    横切关注点:
    这里写图片描述


    AOP练习

    Aop关于加减乘除的练习:

    定义一个算法接口,含有4个方法,加减乘除。

    public interface ArithmeticCalculator(){
        //加
        double add(double a,double b);
        //减
        double sub(double a,double b);
        //乘
        double mul(double a,double b);
        //除
        double div(double a,double b);
    }

    Aop思想:

    public void before(double a ,double b){
    System.out.println(a+"and"+b);
    }
    
    public void after(result){
    System.out.println("结果是"+result);
    }
    
    public double sub(double a, double b){
    before(a,b);
    double result=a+b;
    after(result);
    return result;
    }

    但是,这样做是有一些弊端存在的,比如:
    代码混乱:
    越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀,每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。

    代码分散:
    以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码,如果日志需求发生变化,必须修改所有模块。

    那么,如何解决这些问题呢?

    这就需要使用Spring的动态代理模式来完成了。


    使用动态代理解决问题

    代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理取代原始对象,任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。

    这里写图片描述


    Spring AOP

    用AspectJ注解声明切面

    切面:带有@Aspect注解的Java类

    通知:是标注有以下5种注解的简单的Java方法

    @Before:前置通知,在方法执行之前执行

    @After:后置通知,在方法执行之后执行

    @AfterReturning:返回通知,在方法返回结果之后执行

    @AfterThrowing:异常通知,在方法抛出异常之后 @Around:环绕通知,围绕着方法执行

    前置后置通知

    前置通知:在方法执行之前执行的通知
    前置通知使用: @Before , @After注解,并将切入点表达式的值作为注解值。

    利用方法签名编写AspectJ切入点表达式

    最典型的切入点表达式是根据方法的签名来匹配各种方法:
    这里写图片描述


    指定切面的优先级

    指定切面的优先级:
    在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。
    切面的优先级可以通过实现Ordered接口或利用@Order注解指定。

    实现Ordered接口,getOrder()方法的返回值越小,优先级越高。

    若使用@Order注解,序号出现在注解中。

    @Aspect
    @Order(0)
    public class CalculaotorValidationAspect
    
    @Aspect
    @Order(1)
    public class CalculatorLoggingAspect

    基于XML的配置声明切面

    基于XML声明切面:

    当使用XML声明切面时,需要在<beans>根元素中导入aop Schema

    在Bean配置文件中,所有的Spring AOP配置都必须定义在<aop:config>元素内部。对于每个切面而言,都要创建一个<aop:aspect>元素来为具体的切面实现引用后端Bean实例。

    使用<aop:pointcut>来配置切点,通知元素需要用<aop:before>等元素,method属性指定切面类中通知方法的名称。


    Spring实例练习

    了解了这些Spring的概念知识后,必须要有一个好的例子来理解,下面就以一个简单的小例子来举例子说明。

    定义一个实体类User,实现它属性的set,get方法:

    public class User {
        private int id;
        private String uname;
        private String pwd;
        private int score;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getUname() {
            return uname;
        }
        public void setUname(String uname) {
            this.uname = uname;
        }
        public String getPwd() {
            return pwd;
        }
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
        public int getScore() {
            return score;
        }
        public void setScore(int score) {
            this.score = score;
        }
    }

    定义一个接口ScoreService,接口中定义一个登陆方法:

    import com.jredu.aop.entity.User;
    
    public interface ScoreService {
    
        /**
         * 登录
         * @param user
         * @return
         */
        User login(User user);
    
    }

    定义一个类ScoreServiceImpl,实现ScoreService 接口中的方法:

    import org.springframework.stereotype.Component;
    
    import com.jredu.aop.entity.User;
    import com.jredu.aop.service.ScoreService;
    @Component("score")
    public class ScoreServiceImpl implements ScoreService{
    
        /**
         * 登录 
         */
        @Override
        public User login(User user) {
            // TODO Auto-generated method stub
            System.out.println("登录成功!");
            return user;
        }
    
    }

    在applicationContext-aop2.xml中依赖注入user,并配置扫描,配置切面等。

    <?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:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:util="http://www.springframework.org/schema/util"
        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/util
            http://www.springframework.org/schema/util/spring-util.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
              <!-- 自动扫描标签 -->
         <context:component-scan base-package="com.jredu.aop">
         </context:component-scan> 
         <bean
            id="user" class="com.jredu.aop.entity.User"
            p:id="1"
            p:uname="张三"
            p:pwd="123456"
            p:score="0"
         />
         <bean 
            id="springAspect" class="com.jredu.aop.aspect.SpringAspect"
         />
         <!-- 配置切面 -->
         <aop:config>
            <!-- 配置切点 -->
            <aop:pointcut expression="execution(* com.jredu.aop.service.ScoreService.login(..))" id="pointcut"/>
            <!-- 配置切面 -->
            <aop:aspect ref="springAspect">
                <aop:around method="around" pointcut-ref="pointcut"/>
                <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="obj"/>
            </aop:aspect>
         </aop:config>
    
    </beans>

    定义切面类SpringAspect,在切面中定义相关方法。

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    import com.jredu.aop.entity.User;
    
    public class SpringAspect {
    
        /**
         * 执行前
         * @param point
         *//*
        public void before(JoinPoint point){
            String method = point.getSignature().getName();
            System.out.println("before method:"+method);
        }*/
    
        /**
         * 执行后
         * @param point
         * @throws Throwable 
         */
        public User around(ProceedingJoinPoint point) throws Throwable{
            String method = point.getSignature().getName();
            System.out.println("before method:"+method);
            //调用目标方法前
            User user = (User) point.proceed();
            //调用目标方法后
            System.out.println("原来的用户积分:"+user.getScore());
            System.out.println("after method:"+method);
            user.setScore(user.getScore()+100);
            return user;
    
        }
    
        /**
         * 最终返回结果
         * @param point
         * @param obj
         */
        public void afterReturning(JoinPoint point,Object obj){
            String method = point.getSignature().getName();
            System.out.println("afterReturning method:"+method);
            User user = (User) obj;
            System.out.println("现在用户的积分:"+user.getScore());
        }
    }

    测试类:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.jredu.aop.entity.User;
    import com.jredu.aop.service.ScoreService;
    
    public class ScoreTest {
    
        public static void main(String[] args) {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext-aop2.xml");
            User user = (User) app.getBean("user");
            ScoreService service =(ScoreService) app.getBean("score");
            service.login(user);
        }
    }

    项目完成后的项目截图:
    这里写图片描述

  • 相关阅读:
    CF575A Fibonotci [线段树+矩阵快速幂]
    P3768 简单的数学题 [杜教筛,莫比乌斯反演]
    2-SAT 学习笔记
    CF776D The Door Problem [2sat]
    KD-Tree 学习笔记
    Mybatis入门笔记(2)——基于代理Dao实现CRUD
    Mybatis入门笔记(1)——基于原始dao实现CRUD
    mybatis入门看这一篇就够了
    使用JDBC程序的问题总结
    关于递归你知道多少?
  • 原文地址:https://www.cnblogs.com/aixing/p/13327666.html
Copyright © 2020-2023  润新知