Spring基于XML文件配置AOP

AOP

AOP,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

AOP思想的实现方案:动态代理技术,在运行期间,对目标对象的方法进行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法

下面是AOP的实现思想

创建增强类,编写前方法与后方法

java 复制代码
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("执行增强前方法");
    }

    public void afterAdvice(){
        System.out.println("执行增前后方法");
    }
}

编写被增强类,并编写一个方法用于增强

java 复制代码
public class UserServiceImpl implements UserService {

    @Override
    public void show() {
        System.out.println("show...");
    }

}

Bean后处理器去实现将Bean对象替换成代理类

java 复制代码
public class MyBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //对指定方法进行增强
        if (bean instanceof UserService) {
            //获取增强方法
            MyAdvice myAdvice = context.getBean(MyAdvice.class);
            Object proxyInstance = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            myAdvice.beforeAdvice();
                            method.invoke(bean, args);
                            myAdvice.afterAdvice();
                            return proxy;
                        }
                    });
            return proxyInstance;
        }
        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}

XML文件配置如下

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"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

  <bean id="userService" class="com.zmt.service.impl.UserServiceImpl"></bean>
  <bean class="com.zmt.processor.MyBeanPostProcessor"/>
  <bean class="com.zmt.advice.MyAdvice"/>
</beans>

执行测试代码

java 复制代码
public class ApplicationTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = context.getBean(UserService.class);
        userService.show();
    }
}

执行结果如下

这种实现方式存在两个问题,一个是需要被增强的Bean对象需要硬编码指定,其次是增强方法也是被写死的,如果需要修改被增强的类或是修改增强方法都需要修改代码。

那么我们在了解Spring中AOP时,需要先了解如下几个概念

|--------|-----------|--------------------------|
| 目标对象 | Taeget | 被增强的方法所在的对象 |
| 代理对象 | Proxy | 对目标方法进行增强后的对象,客户端实际调用的对象 |
| 连接点 | Joinpoint | 目标对象中可以被增强的方法 |
| 切入点 | Pointcut | 目标对象中实际被增强的方法 |
| 通知\增强 | Advice | 增强部分的代码逻辑 |
| 切面 | Aspect | 增强和切入点的组合 |
| 织入 | Weaving | 将通知和切入点动态组合的过程 |

那么接下来给出基于XML的实现AOP的示例代码

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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="userService" class="com.zmt.service.impl.UserServiceImpl"/>
    <!--将增强类交给Spring管理-->
    <bean id="myAdvice" class="com.zmt.advice.MyAdvice"/>
    <aop:config>
        <!--配置切面表达式,指定需要被增强的方法-->
        <aop:pointcut id="myPointcut" expression="execution(void com.zmt.service.impl.UserServiceImpl.show())"/>
        <!--指定切点与哪些增强类结合-->
        <aop:aspect ref="myAdvice">
            <aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>
java 复制代码
public class UserServiceImpl implements UserService {

    @Override
    public void show() {
        System.out.println("show...");
    }

}
java 复制代码
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("执行增强前方法");
    }

    public void afterAdvice(){
        System.out.println("执行增前后方法");
    }
}

测试代码如下

java 复制代码
public class ApplicationTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = context.getBean(UserService.class);
        userService.show();
    }
}

运行结果如下

execution语法

execution([访问修饰符] 返回值类型 报名.类名.方法名(参数))

  • 访问修饰符可以省略不写
  • 返回值类型、某一级包名、类名、方法名可以使用 * 表示任意
  • 包名与类名之间使用单点 . 表示该包下的类,使用双点 .. 表示该包以及子包下的类
  • 参数列表可以使用双点 .. 表示任意参数

XML文件下通知的配置类型

|------|---------------------------|------------------------------|
| 通知名称 | 配置方式 | 执行时机 |
| 前置通知 | < aop:before > | 目标方法执行之前执行 |
| 后置通知 | < aop:after-returning > | 目标方法执行之后执行,目标方法异常时不再执行 |
| 环绕通知 | < aop:around > | 目标方法执行前后执行,目标方法异常时,环绕后方法不再执行 |
| 异常通知 | < aop:after-throwing > | 目标方法抛出异常时执行 |
| 最终通知 | < aop:after > | 不管目标方法是否有异常,最终都会执行 |

环绕通知需要将连接点传入,具体的增强类实现代码如下

java 复制代码
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("执行增强前方法");
    }

    public void afterAdvice(){
        System.out.println("执行增前后方法");
    }

    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知。。。");
        Object proceed = joinPoint.proceed();
        System.out.println("环绕后通知。。。");
        return proceed;
    }
}

增强方法在被调用时,Spring可以为其传递一些必要参数

|---------------------|---------------------------------------------|
| 参数类型 | 作用 |
| JoinPoint | 连接点对象,任何通知都可以使用,可以获取当前目标对象,目标方法参数等信息 |
| ProceedingJoinPoint | JoinPoint子类对象,主要是在环绕通知中执行proceed(),进而执行目标方法 |
| Throwable | 异常对象,使用在异常通知中,需要在配置文件中指出异常对象名称 |

除了上面实现AOP之外,基于XML文件配置的实现方式还存在另一种实现方式,通过实现接口来表示该增强类的具体操作。

java 复制代码
public class MyAdvice2 implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前通知。。。。");
        Object res = invocation.getMethod().invoke(invocation.getThis(), invocation.getArguments());
        System.out.println("环绕后通知。。。。");
        return res;
    }
}
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  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="userService" class="com.zmt.service.impl.UserServiceImpl"/>

  <bean id="myAdvice2" class="com.zmt.advice.MyAdvice2"/>
  <aop:config>
    <aop:pointcut id="myPointcut" expression="execution(void com.zmt.service.impl.UserServiceImpl.show())"/>
    <!-- 指定增强类与切点 -->
    <aop:advisor advice-ref="myAdvice2" pointcut-ref="myPointcut"/>
  </aop:config>
</beans>

注意:一个增强类可以实现多个Advice接口

AOP配置的两种语法形式不同点

语法形式不同:

  • advisor是通过实现接口来确认通知的类型
  • aspect是通过配置确认通知的类型,更加灵活

可配置的切面数量不同:

  • 一个advisor只能配置一个固定通知和一个切点表达式
  • 一个aspect可以配置多个通知和多个切点表达式任意组合

使用场景不同:

  • 允许随意搭配情况下可以使用aspect进行配置
  • 如果通知类型单一、切面单一的情况下可以使用advisor进行配置
  • 在通知类型已经固定,不用人为指定通知类型时,可以使用advisor进行配置
相关推荐
【D'accumulation】30 分钟前
典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
java·设计模式·mvc
试行1 小时前
Android实现自定义下拉列表绑定数据
android·java
茜茜西西CeCe1 小时前
移动技术开发:简单计算器界面
java·gitee·安卓·android-studio·移动技术开发·原生安卓开发
救救孩子把1 小时前
Java基础之IO流
java·开发语言
小菜yh1 小时前
关于Redis
java·数据库·spring boot·redis·spring·缓存
宇卿.1 小时前
Java键盘输入语句
java·开发语言
浅念同学1 小时前
算法.图论-并查集上
java·算法·图论
立志成为coding大牛的菜鸟.1 小时前
力扣1143-最长公共子序列(Java详细题解)
java·算法·leetcode
鱼跃鹰飞1 小时前
Leetcode面试经典150题-130.被围绕的区域
java·算法·leetcode·面试·职场和发展·深度优先
爱上语文3 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring