Spring之AOP

Spring整合JUnit单元测试

为了简化Junit的测试,使用Spring框架也可以整合测试

引入Sprin测试整合单元测试的环境

 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
  </dependency>
   <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

在具体的测试类上添加注解

@RunWith(SpringJUnit4ClassRunner.class)
//如果我们使用的配置文件的方式,则加载类路径的配置文件
//@ContextConfiguration("classpath:applicationContext.xml")
//如果我们使用的是配置类的方式则加载配置类的字节码类型
@ContextConfiguration(classes = SpringConfig.class)
@RunWith(value = SpringJUnit4ClassRunner.class)
	@ContextConfiguration(classes = SpringConfig.class)
	public class TestAccountDao2 {
	    @Autowired  //记得注入dao
	    private IAccountDao dao;
	    @Test
	    public void testFindAll() {
	        //执行查询所有的操作
	        List<Account> list = dao.findAll();
	        for (Account account : list) {
	            System.out.println(account);
	        }
	    }
	}

使用AOP技术对DAO层进行增强功能

Spring框架的核心功能之AOP技术

spring框架的aop技术实现其实就是基于动态代理

动态代理:

程序在执行过程中自动生成代理对象,通过代理对象的执行方法,使得被代理对象源代码不更改的情况下实现其功能扩展。

jdk动态代理:

jdk内置的动态代理要求代理对象要实现接口,jdk提供了三个类,Proxy,Method,InvocationHandler用于创建代理对象,实现功能扩展。

cglib 动态代理:

cglib是第三方类库。要求被代理的类必须可以被继承,其子类就是功能增强了的代理对象,要求被代理类及其方法不能被final修饰

AOP是OOP(面向对象)的延续,是函数式编程的一种衍生范畴。

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高=程序的可重用性,同时提高了开发效率。

AOP

使用AOP主要是可以在不修改源代码的情况下对程序进行增强。

面向切面编程,切面就是我们需要增强的功能,也是一种思想,是基于jdk,cglib动态代理的,是动态代理的规范化。

  1. 使用的时候先找到被代理对象的切面,也就是除了业务之外的功能。
  2. 确定切面执行的时间
  3. 确定切面的执行位置,给谁增强功能。
AOP的相关术语:
  1. Jointpoint(连接点):指那些被拦截到的点,在spring中这些点指的是方法,因为Spring只支持方法类型的连接点。
  2. Pointcut(切入点):指要对哪些Jointpoint进行拦截的定义,也就是打算做增强的那个方法。
  3. Adivice(通知/增强):指拦截到Jointpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
  4. Introduction(引介):引介是一种特殊的通知在不修改代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
  5. Target(目标对象):代理的目标对象
  6. Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。
  7. Proxy(代理):一个类被AOP织入增强后,就会产生一个结果代理类。
  8. Aspect(切面):是切入点和通知的结合,切面是以后自己来编写和配置的。
AOP技术实现框架

使用aspectJ框架,在maven项目pom.xml中添加依赖包

  1. 切面执行时间常用以下注解。

    1. @Before:前置通知,在目标类的方法执行之前执行。配置文件信息:<aop:before method=“before” pointcut-ref=“myPointcut3”/>

      可以对方法的参数做校验。

    2. @AfterReturning:后置通知,方法正常执行后的通知,在配置文件中编写具体的配置:

      <aop:after-returning method=“afterReturning” pointcut-ref=“myPointcut2”/>

      应用:可以修改方法的返回值

    3. @Around:环绕通知,方法的执行前后执行,

      在配置文件中编写具体的配置:

      <aop:around method=“around” pointcut-ref=“myPointcut2”/>

      要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。 环绕通知:方法执行之前和方法执行之后进行通知,默认的情况下,目标对象的方法不能执行的。需要手动让目标对象的方法执行

      public void around(ProceedingJoinPoint joinPoint){  //ProceedingJoinPoint 固定的一个类 使用他然后目标对象的方法执行.
      	System.out.println("环绕通知1...");
      	try {
      		// 手动让目标对象的方法去执行
      		joinPoint.proceed();
      	} catch (Throwable e) {
      		e.printStackTrace();
      	}
      	System.out.println("环绕通知2...");
      }
      
    4. @AfterThrowing:异常抛出通知

      • 在抛出异常后通知

      • 在配置文件中编写具体的配置:
        throwing=“e” 这个e的意思就是你切面类里面配置的afterThrowingLog(Exception e)这个增强的方法的形参的名称

        //当你目标对象中delCustomer()方法中出现了异常,那么切面类里面配置的afterThrowingLog()这个增强的方法就会执行
        <aop:after-throwing method=“afterThrowingLog”
        pointcut=“execution(public * .Customer*.delCustomer(…))” throwing=“e”/>
        //切面类中 抛出异常后通知 需要在增强的方法形参里面提供一个异常对象,当你切入点里面抛出你配置的这个异常类型

    5. @After:最终通知,在目标类的方法执行之后执行,如果程序出现了异常,最终方法也会执行,在配置文件中编写具体的配置:

      <aop:after method=“after” pointcut-ref=“myPointcut3”/>

      应用:例如像释放资源

  2. 切面的执行位置采用切入点表达式

    ​ 以上几个注解均有属性value,属性值为切入点表示式的字符串,表示切面执行的位置
    语法格式:
    execution(访问权限 方法返回值 方法声明(参数) 异常类型)

    3.符号

*---- 0至多个任意字符串

… -----用在方法参数中,表示任意多个参数用在包名后,表示当前包及其子包

±-----用在类名后,表示当前类及其子类,用在接口后, 表示当前接口及其实现类。

例如:
execution(public * *(…)) //指定切入点为:任意公共方法。

代码演示:

//接口
public interface Target {
    void doSome(String name,Integer age);
}
/**
 - 被代理对象
 */
public class TargetImpl implements Target {
    @Override
    public void doSome(String name,Integer age) {
        //在执行doSome方法前增加一个功能
        System.out.println("targetImpl的doSome方法执行了");
    }
}
//@Aspect注解声明这个类是切面类
@Aspect
public class ProxyByAspectJ {
    /**
     * 声明需要增加的功能
     * 注意:
     * 1.这个功能需是public修饰
     * 2.这个功能没有返回值
     */
    //Before:前置通知注解,在被代理对象方法执行之前执行
    //execution(public * *.doSome(..)):任意包中的doSome方法,可以写具体切入点表示式
    @Before(value="execution(public * *.doSome(..))")
    public void doBefore(){
        System.out.println("在doSome方法之前执行这个功能");
    }
}

在xml配置中声明aspectj框架中的自动代理生成器标签引入具体的AOP的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:context="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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--创建被代理对象-->
    <bean id="target" class="com.itheima.TargetImpl"/>

    <!--创建切面类对象-->
    <bean id="proxy" class="com.itheima.ProxyByAspectJ"/>

    <!--声明自动生成代理生成器,在内存中改变被代理对象的结构为代理对象-->
    <context:aspectj-autoproxy/>
</beans>

通用化切入点的表达式:

<!--定义切面 指定拦截方法时 做什么-->
<bean id="xmlAopDemoUserLog" class="com.ganji.demo.service.aspect.XmlAopDemoUserLog"></bean>
<aop:config>
    <aop:aspect ref="xmlAopDemoUserLog"> <!--指定切面-->
        <!--定义切点-->
        <aop:pointcut id="logpoint" expression="execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..))"></aop:pointcut>
        <!--定义连接点-->
        <aop:before pointcut-ref="logpoint" method="beforeLog"></aop:before>
        <aop:after pointcut-ref="logpoint" method="afterLog"></aop:after>
        <aop:after-returning pointcut-ref="logpoint" method="afterReturningLog"></aop:after-returning>
        <aop:after-throwing pointcut-ref="logpoint" method="afterThrowingLog"></aop:after-throwing>
    </aop:aspect>
</aop:config>

Spring框架的AOP技术

步骤一:

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

步骤二:创建Spring的配置文件,引入具体的schema约束;

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

步骤三:创建包结构,编写具体的接口和实现类

org.itheima.demo1
		 CustomerDao			-- 接口
		 CustomerDaoImpl		-- 实现类

步骤四:将目标类配置到Spring中

<bean id="customerDao" class="org.westos.demo1.CustomerDaoImpl"/>

步骤五:定义切面类

添加切面和通知的注解

@Aspect – 定义切面类的注解

@Aspect
		public class MyAspectAnno {
			@Before(value="execution(public void org.westos.demo1.CustomerDaoImpl.save())")
			public void log(){
				System.out.println("记录日志...");
			}
		}

步骤六:在配置文件中定义切面类

<bean id="myAspectAnno" class="com.itheima.demo1.MyAspectAnno"/>

步骤七:在配置文件中开启自动代理

<aop:aspectj-autoproxy/> 放在最前面

步骤八:完成测试

@RunWith(SpringJUnit4ClassRunner.class)
	@ContextConfiguration("classpath:applicationContext.xml")
	public class Demo1 {
		
		@Resource(name="customerDao")
		private CustomerDao customerDao;
		
		@Test
		public void run1(){
			customerDao.save();
			customerDao.update();
		}
	}
通知类型采用注解的方式:

配置通用的切入点

* 使用@Pointcut定义通用的切入点
@Aspect
	public class MyAspectAnno {
		//随便定义一个方法,上面用注解定义一个切入点
		@Pointcut(value="execution(public void com.itheima.demo1.CustomerDaoImpl.save())")
		public void fn(){}
		
		//引用这个定义的切入点 格式:切面类名.方法名
		@Before(value="MyAspectAnno.fn()")
		public void log(){
			System.out.println("记录日志...");
		}
		/**
		 * 环绕通知
		*/
		@Around(value="MyAspectAnno.fn()")
		public void around(ProceedingJoinPoint joinPoint){
			System.out.println("环绕通知1...");
		try {
			// 让目标对象的方法执行
			joinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		  }
			System.out.println("环绕通知2...");
		}
		
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值