Spring AOP面向切面编程

Spring AOP面向切面编程

Spring AOP

  • Spring AOP - Aspect Oriented Programming 面向切面编程
  • AOP的做法是将通用、于业务无关的功能抽象封装为切面类
  • 切面可配置在目标方法的执行前、后运行,真正做到即插即用

Spring AOP

用到的依赖dependency:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.6</version>
    </dependency>
</dependencies>

applicationContext.xml中schema依赖

<?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 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 definitions here -->
</beans>

Spring AOP概念

Spring AOP与AspectJ的关系

  • Eclipse AspectJ,一种基于Java平台的面向切面编程的语言
  • Spring AOP使用AspectJWeaver实现类与方法的匹配
  • Spring AOP利用代理模式实现对象运行时功能扩展

几个关键概念

注解说明
Aspect切面,具体的可插拔组件功能类,通常一个切面只实现一个通用功能
Target Class/Method目标类、目标方法,指真正要执行与业务相关的方法
PointCut切入点,使用execution表达式说明切面要作用在系统的哪些类上
JoinPoint连接点,切面运行过程中时包括了目标类/方法元数据的对象
Advice通知,说明具体的切面的执行时机,Spring包含了五种不同类型通知

AOP配置过程

  • 依赖AspectJ
  • 实现切面类/方法
  • 配置Aspect Bean
  • 定义PointCut
  • 配置Advice

JoinPoint核心方法

注解说明
Object getTarget()获取IoC容器内目标对象
Signature getSignature()获取目标方法
Object[] getArgs()获取目标方法参数

PointCut切点表达式

在这里插入图片描述

通知

五种通知类型

注解说明
Before Advice前置通知,目标方法运行前执行
After Returning Advice返回后通知,目标方法返回数据后执行
After Throwing Advice异常通知,目标方法抛出异常后执行
After Advice后置通知,目标方法运行
Around Advice最强大通知,自定义通知执行时机,可决定目标方法是否运行

特殊的“通知”-引介增强

  • 引介增强(Introduction Interceptor)是对类的增强,而非方法
  • 引介增强允许在运行时为目标类增加新属性或方法
  • 引介增强允许在运行时改变类的行为,让类随运行环境动态变更

Spring AOP实现原理

  • Spring基于代理模式实现功能动态扩展,包含俩种形式
  • 目标类拥有接口,通过JDK动态代理实现功能扩展
  • 目标类没有接口,通过CGLib组件实现功能扩展

代理模式:

  • 代理模式通过代理对象对原对象的实现功能扩展

在这里插入图片描述

静态代理示例:

UserService.java

package com.imooc.spring.aop.service;

/**
 * @author Rex
 * @create 2021-01-05 11:47
 */
public interface UserService {
    public void createUser();
}

UserService.java

package com.imooc.spring.aop.service;

/**
 * @author Rex
 * @create 2021-01-05 11:47
 */
public class UserServiceImpl implements UserService{
    @Override
    public void createUser() {
        System.out.println("执行创建用户业务逻辑");
    }
}

UserServiceProxy.java

package com.imooc.spring.aop.service;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author Rex
 * @create 2021-01-05 11:49
 */
//静态代理是指必须手动创建代理类的代理模式使用方式
public class UserServiceProxy implements UserService{
    //持有委托类的对象
    private UserService userService;
    public UserServiceProxy(UserService userService){
        this.userService = userService;
    }

    @Override
    public void createUser() {
        System.out.println("======" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss").format(new Date()) +"==========");
        this.userService.createUser();
    }
}

UserServiceProxy1.java

package com.imooc.spring.aop.service;

/**
 * @author Rex
 * @create 2021-01-05 11:54
 */
public class UserServiceProxy1 implements UserService {
    private UserService userService;
    public UserServiceProxy1(UserService userService){
        this.userService = userService;
    }

    @Override
    public void createUser() {
        userService.createUser();
        System.out.println("=======后置扩展功能======");
    }
}

运行结果:

======2021-01-05 12:28:08 008==========
执行创建用户业务逻辑
=======后置扩展功能======

动态代理示例

UserSerivce.java

package com.imooc.spring.aop.service;

/**
 * @author Rex
 * @create 2021-01-05 11:47
 */
public interface UserService {
    public void createUser();
}

UserServiceImpl.java

package com.imooc.spring.aop.service;

/**
 * @author Rex
 * @create 2021-01-05 11:47
 */
public class UserServiceImpl implements UserService{
    @Override
    public void createUser() {
        System.out.println("执行创建用户业务逻辑");
    }
}

EmployeeService.java

package com.imooc.spring.aop.service;

/**
 * @author Rex
 * @create 2021-01-05 12:20
 */
public interface EmployeeService {
    public void createEmployee();
}

EmployeeServiceImpl.java

package com.imooc.spring.aop.service;

/**
 * @author Rex
 * @create 2021-01-05 12:20
 */
public class EmployeeServiceImpl implements EmployeeService{
    @Override
    public void createEmployee() {
        System.out.println("执行创建员工实现类");
    }
}

ProxyInvocationHandler.java

package com.imooc.spring.aop.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强
 * InvocationHandler实现类与切面类的的环绕通知类似
 * @author Rex
 * @create 2021-01-05 12:06
 */
public class ProxyInvocationHandler implements InvocationHandler {
    private Object target; //目标对象
    private ProxyInvocationHandler(Object target){
        this.target = target;
    }

    /**
     * 在invoke()方法对目标方法进行增强
     * @param proxy 代理类对象
     * @param method 目标方法对象
     * @param args 目标方法实参
     * @return 目标方法运行后返回值
     * @throws Throwable 目标方法抛出的异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("======" +new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss").format(new Date()) + "======");
        Object ret = method.invoke(target, args);//调用目标方法, ProceedingJoinPoint.proceed()
        return ret;
    }

    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(userService);
        //动态创建代理类
        UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                proxyInvocationHandler);
        userServiceProxy.createUser();

        //动态代理,必须实现接口才可以运行
        EmployeeService employeeService = new EmployeeServiceImpl();
        EmployeeService employeeServiceProxy = (EmployeeService) Proxy.newProxyInstance(employeeService.getClass().getClassLoader(),
                employeeService.getClass().getInterfaces(),
                new ProxyInvocationHandler(employeeService));
        employeeServiceProxy.createEmployee();
    }
}

运行结果:

======2021-01-05 12:23:20 020======
执行创建用户业务逻辑
======2021-01-05 12:23:20 020======
执行创建员工实现类

CGLib实现代理类

  • CGLib是运行时字节码增强技术
  • Spring AOP扩展无接口类使用CGLib
  • AOP会运行时生成目标继承类字节码的方式进行行为扩展

AOP代码实例:

XML形式

applicationContext.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 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="userDao" class="com.imooc.spring.aop.dao.UserDao"></bean>
 <bean id="employeeDao" class="com.imooc.spring.aop.dao.EmployeeDao"></bean>
 <bean id="userService" class="com.imooc.spring.aop.service.UserService">
     <property name="userDao" ref="userDao"></property>
 </bean>
 <bean id="employeeService" class="com.imooc.spring.aop.service.EmployeeService">
     <property name="employeeDao" ref="employeeDao"></property>
 </bean>

 <bean id="methodChecker" class="com.imooc.spring.aop.aspect.MethodChecker"></bean>
 <aop:config>
     <aop:pointcut id="pointcut" expression="execution(* com.imooc..*.*(..))"/>
     <aop:aspect ref="methodChecker">
         <aop:around method="check" pointcut-ref="pointcut"></aop:around>
     </aop:aspect>

 </aop:config>
</beans>

UserDao.java

package com.imooc.spring.aop.dao;

/**
 * 用户表Dao
 */
public class UserDao {
    public void insert(){
        System.out.println("新增用户数据");
    }
}

EmployeeDao.java

package com.imooc.spring.aop.dao;

/**
 * 员工表Dao
 * @author Rex
 * @create 2021-01-04 23:20
 */
public class EmployeeDao {
    public void insert(){
        System.out.println("新增员工数据");
    }
}

Userervice.java

package com.imooc.spring.aop.service;

import com.imooc.spring.aop.dao.UserDao;

/**
 * 用户服务
 * @author Rex
 * @create 2021-01-04 22:46
 */
public class UserService {
    private UserDao userDao;

    public void createUser(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行员工入职业务逻辑");
        userDao.insert();
    }
    public String generateRandomPassword(String type, Integer length){
        System.out.println("按" + type + "方式生成" + length + "位随机密码");
        return "Zxcquil";
    }
    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

EmployeeService.java

package com.imooc.spring.aop.service;

import com.imooc.spring.aop.dao.EmployeeDao;

/**
 * 员工服务
 * @author Rex
 * @create 2021-01-04 23:20
 */
public class EmployeeService {
    private EmployeeDao employeeDao;
    public void entry(){
        System.out.println("执行员工入职业务逻辑");
        employeeDao.insert();
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }
}

MethodCheck.java

package com.imooc.spring.aop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author Rex
 * @create 2021-01-04 23:07
 */
public class MethodChecker {
    //ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
    public Object check(ProceedingJoinPoint pjp) throws Throwable {

        try {
            long startTime = new Date().getTime();
            Object ret = pjp.proceed();//执行目标方法
            long endTime = new Date().getTime();
            long duration = endTime - startTime;
            if (duration >= 1000){
                String className = pjp.getTarget().getClass().getName();
                String methodName = pjp.getSignature().getName();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss");
                String now = sdf.format(new Date());
                System.out.println("==========="+now+":"+className+"."+methodName + "(" + duration+"ms)======");
            }
            return ret;
        } catch (Throwable throwable) {
            System.out.println("Exception message:"+throwable.getMessage());
            throw throwable;
        }
    }
}

SpringContext.java

package com.imooc.spring.aop;


import com.imooc.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author Rex
 * @create 2021-01-04 23:20
 */
public class SpringContext {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.createUser();
    }
}

执行结果:

执行员工入职业务逻辑
新增用户数据
===========2021-01-05 14:15:17 017:com.imooc.spring.aop.service.UserService.createUser(3046ms)======

注解形式

applicationContext.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"
       xmlns:context="http://www.springframework.org/schema/context"
       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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.imooc"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

BookShop.java

package com.imooc.spring.aop.entity;

import org.springframework.stereotype.Component;

/**
 * @author Rex
 * @create 2021-01-05 10:53
 */
@Component
public class BookShop {
    public void sellingBooks(){
        System.out.println("卖出一本java基础书籍");
    }
}

MethodPro.java

package com.imooc.spring.aop.aspect;

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;
import org.springframework.stereotype.Component;

/**
 * @author Rex
 * @create 2021-01-05 10:54
 */
@Component
@Aspect
public class MethodPro {
//    @Around("execution(public * com.imooc..*.*())")
    public void welcome(ProceedingJoinPoint pjd) throws Throwable {
        System.out.println("欢迎光临慕课小店");
        pjd.proceed();
        System.out.println("欢迎再次光临");
    }

    @Before("execution(public * com.imooc..*.*(..))")
    public void preSales(){
        System.out.println("=====售前服务=====");
    }

    @After("execution(public * com.imooc..*.*(..))")
    public void afterSale(){
        System.out.println("=====售后服务======");
    }
}

SpringApplication.java

package com.imooc.spring.aop;

import com.imooc.spring.aop.entity.BookShop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author Rex
 * @create 2021-01-05 11:03
 */
@ComponentScan
public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        BookShop bookShop = context.getBean("bookShop", BookShop.class);
        bookShop.sellingBooks();
    }
}

执行结果:

=====售前服务=====
卖出一本java基础书籍
=====售后服务======

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rex·Lin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值