理解Spring AOP并使用XML及注解两种方式实现Spring AOP

Spring AOP

概述

aop(面向切面编程) 是oop的扩展和延伸,是为了解决OOP中产生的开发问题。

应用场景

权限校验、日志记录、性能监控、事务控制
以权限校验举例AOP的优势
需求:在每个数据持久层dao层saveDB()方法前,统一验证,必须拥有管理员权限才可以save。此时就需要在每次saveDB前验证权限,假设为check()方法
解决方案:

  1. 在dao层的每一个方法都增加一个check()方法
    劣势:check方法中的代码重复,当要修改时,多个类需要修改
  2. 纵向继承: 让dao层的所有类都继承一个BaseDao,在BaseDao中实现check方法,这样dao层中的所有类都可以直接调用的方法。
    劣势: 当不需要check方法了,需要在basedao中做修改
  3. 横向抽取: 采用代理机制,在save方法前面增加一个check方法。这就是aop的实现原理,这样处理的好处就是当我需要增加这个check方法的时候我就使用代理类,不需要了我就不用。
    同理,日志记录,性能监控,事务控制都是需要在原有的处理方法的前面或者后面增加一段代码,我们都可以采用代理机制来实现。

spring aop底层实现使用的代理模式实现

代理模式

代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。

为什么AOP可以通过代理模式实现

从上文所述,实际上AOP的需求就是要一个已经实现好的类里面去增加一些操作。而代理模式中的代理类可以在被代理的类的基础上增加一些方法。
代理模式可以分为静态代理和动态代理,而AOP是通过动态代理来实现的。

Spring Aop采用的两种代理模式
  • JDK动态代理
  • CGLIB
JDK动态代理

JDK动态代理只能对实现了接口的类生成代理,而不能针对类
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

CGLIB

CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final

CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

Spring在选择用JDK还是CGLiB的依据:
  • 当Bean实现接口时,Spring就会用JDK的动态代理

  • 当Bean没有实现接口时,Spring使用CGlib是实现

  • 可以强制使用CGlib
    在spring配置中加入如下代码

<aop:aspectj-autoproxy proxy-target-class="true"/>
AOP的开发中的相关术语

图片

连接点: 可以被拦截的点
切入点: 真正被拦截的点
通知: 增强方法
引介: 类的增强
目标: 被增强的对象
织入: 将增强应用到目标的过程
代理: 织入增强后产生的对象
切面: 切入点和通知的组合

Spring中使用AOP

Spring 中使用AOP的方式有使用传统方式和AspectJ两种方式,本文只介绍使用AspectJ的方式,并分别通过xml和注解来实现

spring的aop的开发(AspectJ的XML的方式)

在spring的配置文件中配置目标类和切面类都交给spring管理,之后利用标签声明好pointcut、aspect

spring的配置文件------application2.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" xmlns:tx="http://www.springframework.org/schema/tx"
	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
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd">

	<!-- 在配置文件中开启注解的AOP的开发 -->
	<aop:aspectj-autoproxy />

	<!-- 配置目标类 -->
	<bean id="studentDao1" class="com.demo.spring_aop.aopXml.StudentDao">
	</bean>

	<!-- 配置切面类 -->
	<bean id="studentAspect1" class="com.demo.spring_aop.aopXml.StudentAspect">
	</bean>
	<!-- 配置 AOP -->
	<aop:config>
		<!-- 配置切点表达式 -->
		<!-- 
           整个表达式可以分为五个部分:
           1、execution()::表达式主体。
           2、第一个*号:表示返回类型, *号表示所有的类型。
           3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service包、子孙包下所有类的方法。
           4、第二个*号:表示类名,*号表示所有的类。
           5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
        -->
		<aop:pointcut
			expression="execution(* com.demo.spring_aop.aopXml.StudentDao.saveStudent(..))"
			id="pointcut1" />
		<aop:pointcut
            expression="execution(* com.demo.spring_aop.aopXml.StudentDao.updateStudent(..))"
            id="pointcut2" />
        <aop:pointcut
            expression="execution(* com.demo.spring_aop.aopXml.StudentDao.deleteStudent(..))"
            id="pointcut3" />
        <aop:pointcut
            expression="execution(* com.demo.spring_aop.aopXml.StudentDao.findStudent(..))"
            id="pointcut4" />
		<!-- 配置切面及通知 -->
		<aop:aspect ref="studentAspect1">
		    <!-- 前置增强 -->
			<aop:before method="before" pointcut-ref="pointcut1" />
			<!-- 异常抛出增强 -->
			<aop:after-throwing method="afterThrowing"
				pointcut-ref="pointcut3" throwing="e" />
			<!-- 后置增强 -->
			<aop:after-returning method="afterReturning"
				pointcut-ref="pointcut4" returning="result" />
			<!-- 环绕增强 -->
			<aop:around method="around" pointcut-ref="pointcut2" />
			<!-- 后置增强 -->
			<!-- <aop:after method="afterMethod" pointcut-ref="pointcut2" /> -->
		</aop:aspect>
	</aop:config>
</beans> 


需要被增强的类,该类里面所有的方法都是连接点,但是只有真正被增强的方法才是切入点,从我们上文的配置中我们把所有的方法都做了增加,即:该类中的所有方法都是切入点。
StudentDao.java

package com.demo.spring_aop.aopXml;

public class StudentDao {

	public void saveStudent(){
		System.out.println("==========增加学生========");
		System.out.println("save方法结束");
	}
	public void updateStudent(String name){
		System.out.println("=========更新学生" + name + "=========");
		System.out.println("update方法结束");
	}
	public void deleteStudent(){
		System.out.println("=======删除学生========");
		System.out.println("delete方法结束");
	}
	public String findStudent(){
		System.out.println("=======查找学生========");
		System.out.println("find方法结束");
		return "张小明";
	}
}

进行增强的切面类,可以设置切面中五种通知中需要做的不同的方法。
StudentAspect.java

package com.demo.spring_aop.aopXml;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 切面类:注解的切面类
 * 
 */
public class StudentAspect {

    // 前置通知
    public void before() {
        System.out.println("前置增强===========");
    }

    // 后置通知:
    public void afterReturning(Object result) {
        System.out.println("后置增强===========" + result);
    }

    // 环绕通知:
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前增强==========");
        Object obj = joinPoint.proceed();
        System.out.println("环绕后增强==========");
        return obj;
    }

    // 异常抛出通知:
    public void afterThrowing(Throwable e) {
        System.out.println("异常抛出增强=========" + e.getMessage());
    }

    // 最终通知
    public void after() {
        System.out.println("最终增强============");
    }
}

测试类
DemoTest.java

package com.demo.spring_aop.aopXml;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Spring的AOP的注解开发
 * @author jt
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class DemoTest {
	@SuppressWarnings("restriction")
    @Resource(name="studentDao1")
	private StudentDao studentDao;

	@Test
	public void demo1(){
//	    studentDao.saveStudent();
	    studentDao.updateStudent("小花");
//	    studentDao.deleteStudent();
//	    studentDao.findStudent();
	}
}

测试结果:
在这里插入图片描述

Spring AOP的注解方式

将切面类和需要被增强的类交给spring管理
spring的配置文件-----application.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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 在配置文件中开启注解的AOP的开发 -->
    <aop:aspectj-autoproxy/>

    <!-- 配置目标类 -->
    <bean id="studentDao" class="com.demo.spring_aop.aop.StudentDao">

    </bean>

    <!-- 配置切面类 -->
    <bean id="studentAspect" class="com.demo.spring_aop.aop.StudentAspect">
    
    </bean>
</beans> 

需要被增强的类
同xml方式的StudentDao.java

进行增强的切面类
@Aspect声明这是一个切面类
@Before:前置增强
@AfterReturning:在方法返回值之后增强
@Around:在方法前后都增强
@After:在方法结束时增强
@AfterThrowing: 抛出异常增强

package com.demo.spring_aop.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 切面类:注解的切面类
 * 
 */
@Aspect
public class StudentAspect {

    @Before(value = "StudentAspect.pointcut2()")
    public void before() {
        System.out.println("前置增强===========");
    }

    // 后置通知:
    @AfterReturning(value = "StudentAspect.pointcut1()", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("后置增强===========" + result);
    }

    // 环绕通知:
    @Around(value = "StudentAspect.pointcut3()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前增强==========");
        Object obj = joinPoint.proceed();
        System.out.println("环绕后增强==========");
        return obj;
    }

    // 异常抛出通知:
    @AfterThrowing(value = "StudentAspect.pointcut1()", throwing = "e")
    public void afterThrowing(Throwable e) {
        System.out.println("异常抛出增强=========" + e.getMessage());
    }

    // 最终通知
    @After(value = "StudentAspect.pointcut1()")
    public void after() {
        System.out.println("最终增强============");
    }

    // 切入点注解:
    @Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao.find*(..))")
    private void pointcut1() {
    }

    @Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao.save*(..))")
    private void pointcut2() {
    }

    @Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao.update*(..))")
    private void pointcut3() {
    }

    @Pointcut(value = "execution(* com.demo.spring_aop.aop.StudentDao..delete*(..))")
    private void pointcut4() {
    }
}

测试类

package com.demo.spring_aop.aop;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Spring的AOP的注解开发
 * @author jt
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:applicationContext.xml")
public class DemoTest {
	@SuppressWarnings("restriction")
    @Resource(name="studentDao")
	private StudentDao studentDao;

	@Test
	public void demo1(){
//	    studentDao.saveStudent();
	    studentDao.updateStudent("小红");
//	    studentDao.deleteStudent();
//	    studentDao.findStudent();
	}
}

测试结果:
在这里插入图片描述

github代码传送门

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值