Spring AOP

Spring AOP 面向切面编程

  1. Spring AOP - Aspect Oriented Programming面向切面编程
  2. AOP的做法是将通用、与业务无关的功能抽象封装为切面类
  3. 切面可配置在目标方法的执行前、后运行,真正做到即插即用
    在不修改源码的情况下对程序进行扩展

举例
在原方法执行之前(获取其他时机)添加另外执行事件

public class EmployeeDao {
    public void insert(){
        System.out.println("新增员工");
    }
}
public class UserDao {
    public void insert(){
        System.out.println("新增用户");
    }
}
public class EmployeeService {
    private EmployeeDao employeeDao;
    public void entry(){
        System.out.println("执行员工入职业务逻辑");
        employeeDao.insert();
    }
    public EmployeeService() {
    }
    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }
    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }
}
public class UserService {
    private UserDao userDao;
    public UserService() {
    }
    public void createUser() {
        System.out.println("执行创建用户业务逻辑");
        userDao.insert();
    }
    public String generateRandomPassword(String type, Integer length) {
        System.out.println("按" + type + "方式生成" + length + "位随机密码");
        return "zdaa12";
    }
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

在POP.xml添加依赖

  <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!--aspectjweaver是Spring AOP的底层依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>

创建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:context="http://www.springframework.org/schema/context"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userDao" class="com.imooc.spring.aop.dao.UserDao"/>
    <bean id="employeeDao" class="com.imooc.spring.aop.dao.EmployeeDao"/>
    <bean id="userService" class="com.imooc.spring.aop.service.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="employeeService" class="com.imooc.spring.aop.service.EmployeeService">
        <property name="employeeDao" ref="employeeDao"/>
    </bean>

    <!--AOP配置-->
    <bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect"/>
    <aop:config>
    	<!-- expression表达式↓↓↓↓↓↓ -->
        <!-- PointCut切点,使用execution表达式描述切面的作用范围-->
        <!--execution(public * com.imooc..*.**(..))说明切面作用在com.imooc包下的所有类的所哟方法-->
        <!--<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"/>-->
        <!--只对所有Service类有效-->
        <!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"/>-->
        <!--只对返回值为String方法有效-->
        <!--<aop:pointcut id="pointcut" expression="execution(String com.imooc..*Service.*(..))"/>-->
        <!--对方法名进行约束-->
        <!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.create*(..))"/>-->
        <aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(*,*))"/>

        <!--定义切面类-->
        <aop:aspect ref="methodAspect">
            <!--before通知,代表在目标方法运行前先执行methodAspect.printExecutionTime()-->
            <!--brfore 通知 method 执行的方法  pointcut-ref 作用范围-定义的切点-->
            <aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
            <!--返回后通知-->
            <aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
            <!-- 执行后通知 -->
            <aop:after method="doAfter" pointcut-ref="pointcut"/>
            <!--异常通知-->
            <aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

public class MethodAspect {

    //切面方法,用于扩展额外的功能
    //JoinPoint 连接点 通过连接点可以获取目标类/方法的信息
    public void printExecutionTime(JoinPoint joinPoint) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        String now = sdf.format(new Date());
        String className = joinPoint.getTarget().getClass().getName();// 获取目标类的名称
        String methodName = joinPoint.getSignature().getName();// 获取目标方法名称
        System.out.println("------>" + now + ":" + className + "." + methodName);
        Object[] args = joinPoint.getArgs();
        System.out.println("------>参数个数:" + args.length);
        for (Object arg :
                args) {
            System.out.println("------>参数:" + arg);
        }
    }
    public void doAfterReturning(JoinPoint joinPoint, Object ret) {
        System.out.println("<------返回后通知" + ret);
    }
    public void doAfter(JoinPoint joinPoint) {
        System.out.println("<------触发后置通知");
    }
    public void doAfterThrowing(JoinPoint joinPoint, Throwable th) {
        System.out.println("<------异常通知:" + th.getMessage());
    }
}
public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "classpath:applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.createUser();
        userService.generateRandomPassword("MD5", 16);
    }
}

在这里插入图片描述
AOP 关键概念

Spring AOP与AspectJ的关系

  • Eclipse AspectJ,一种基于Java平台的面向切面编程的语言
  • Spring AOP使用AspectJWeaver实现类与方法匹配
  • Spring AOP利用代理模式实现对象运行时功能扩展
    在这里插入图片描述
    AOP配置过程
  • 依赖AspectJ
  • 实现切面类/方法
  • 配置Aspect Bean
  • 定义PointCut(切点)
  • 配置Advice

连接点参数
在这里插入图片描述
上边那个代码都有。

execution表达式
在这里插入图片描述
上边配置文件有案例。

五种通知类型
在这里插入图片描述
执行顺序与我们在配置文件中的顺序一致

特殊的“通知”-引介增强 (了解一下就行了,平常不用)

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

上边源码也有使用案例。

环绕通知
利用AOP进行方法性能筛查(将执行时间超过1s的方法列出来)

主要就是这个类

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;
        }
    }
}
<bean id="methodChecker" class="com.imooc.spring.aop.aspect.MethodChecker"/>
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.imooc..*.*(..))"/>
        <aop:aspect ref="methodChecker">
            <!--环绕通知-->
            <aop:around method="check" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "classpath:applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.createUser();
    }
}
public class UserService {
    private UserDao userDao;

    public UserService() {}
    public void createUser() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行创建用户业务逻辑");
        userDao.insert();
    }
    public UserDao getUserDao() {
        return userDao;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

在这里插入图片描述

利用注解配置Spring AOP

  • 引入依赖
  • 配置文件增加注解相关设置
 	<context:component-scan base-package="com.imooc"/>
    <!--启用AOP注解模式-->
    <aop:aspectj-autoproxy/>
@Repository
public class EmployeeDao {
    public void insert(){
        System.out.println("新增员工");
    }
}
// 用于持久化
@Repository
public class UserDao {
    public void insert(){
        System.out.println("新增用户");
    }
}
@Service
public class EmployeeService {
    @Resource
    private EmployeeDao employeeDao;
    public void entry(){
        System.out.println("执行员工入职业务逻辑");
        employeeDao.insert();
    }
    public EmployeeService() {
    }
    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

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

@Service
public class UserService implements IUserService{
    @Resource
    private UserDao userDao;

    public UserService() {
    }
    public void createUser() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行创建用户业务逻辑");
        userDao.insert();
    }
    public UserDao getUserDao() {
        return userDao;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
  • 增加切面类
@Component // 标记当前类为组件
@Aspect // 说明当前类是切面类
public class MethodChecker {

    // 环绕通知 参数为PointCut切点表达式
    @Around("execution(* com.imooc..*Service.*(..))")
    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;
        }
    }
}
public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "classpath:applicationContext.xml");
        IUserService userService = context.getBean("userService", IUserService.class);
        userService.createUser();
    }
}

在这里插入图片描述

Spring AOP 实现原理

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

在这里插入图片描述
静态代理案例:

// 用户服务
public interface UserService {
    public void createUser();
}
public class UserServiceImpl implements UserService {
    @Override
    public void createUser() {
        System.out.println("执行创建用户业务逻辑");
    }
}

代理类

//静态代理是指必须手动创建代理类的代理模式使用方式
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()) + "=======");
        userService.createUser();
    }
}

public class Application {
    public static void main(String[] args) {
        UserService userService = new UserServiceProxy(new UserServiceImpl());
        userService.createUser();
    }
}
  • 代理模式可以嵌套
public class UserServiceProxy1 implements UserService{
    private UserService userService;

    public UserServiceProxy1(UserService userService) {
        this.userService = userService;
    }
    public void createUser(){
        userService.createUser();
        System.out.println("======后置扩展功能======");
    }
}
public class Application {
    public static void main(String[] args) {      
 	UserService userService = new UserServiceProxy1(new UserServiceProxy(new UserServiceImpl()));
        userService.createUser();
    }
}

在这里插入图片描述
动态代理案例:

/**
 * InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强
 * InvocationHandler实现类与切面类的环绕通知类似
 */
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);//调用目标方法
        return ret;
    }

    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService);
        // 动态创建代理类
        // 第一个参数:类加载器
        // 第二个参数:当前类要实现的接口
        // 第三个参数:(如何对目标方法进行扩展)刚才实例化的invocationHandler 
        // 返回的类型就是,传入的对象
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                invocationHandler);
        userServiceProxy.createUser();

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

    }
}

public interface EmployeeService {
    public void createEmployee();
}
public class EmployServiceImpl implements EmployeeService{
    @Override
    public void createEmployee() {
        System.out.println("执行创建员工业务逻辑");
    }
}

public interface UserService {
    public void createUser();
}
public class UserServiceImpl implements UserService {
    @Override
    public void createUser() {
        System.out.println("执行创建用户业务逻辑");
    }
}

CGLib实现代理类

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

在这里插入图片描述
Spring AOP实现原理 两种情况

  • 目标类实现了接口,Spring优先底层使用JDK动态代理来生成目标类的代理,从而实现目标的扩展
  • 目标类没有实现接口,则自动使用CGLib来通过继承的方式对目标类进行扩展
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值