传统Spring AOP编程案例

传统SpringAOP 是指 1.2版本之后开始支持AOP编程 。

老的AOP的编程配置过于复杂,这里采用AspectJ的切入点语法来讲解。

面向切面编程开发步骤(动态织入)

  • 确定目标对象(target—>bean)
  • 编写Advice通知方法 (增强代码)
  • 配置切入点和切面

直接使用 CustomerService(需要接口)和ProductService(不需要接口)作为 target目标对象

提示:spring的所有功能,都是基于bean的,下面所说的“目标”,都是bean。

传统SpringAOP的Advice编写(了解)

传统Spring AOP的通知(增强)种类:

AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice

Spring按照通知Advice在目标类方法的连接点位置,可以分为5类

(1)前置通知 org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强

(2)后置通知 org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强

(3)环绕通知 org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强

(4)异常抛出通知 org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强

(5)引介通知 org.springframework.aop.IntroductionInterceptor
在目标类中添加一些新的方法和属性

简单的说通知就是增强的方式方法

遵循aop联盟规范,传统Spring AOP编程的Advice有五种(前置通知、后置通知、环绕通知、异常通知、引介通知), 传统SpringAOP的Advice 必须实现对应的接口!

【需求】:开发一个记录方法运行时间的例子。将目标方法的运行时间,写入到log4j的日志中。

开发步骤:

第一步:引入所需依赖:

pom.xml

<!-- spring核心依赖 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
		</dependency>
		
		<!—- springaspect集成 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
		</dependency>

		<!-- 单元测试 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>

		<!-- 日志 -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
		</dependency>
		
		<!-- spring集成测试 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>4.3.13.RELEASE</version>
		</dependency>

使用动态代理的代码:
在这里插入图片描述

第二步:编写传统aop的Advice通知类。

创建包:cn.itcast.spring.b_oldaop,创建类TimeLogInterceptor 。
传统aop的通知,必须实现MethodInterceptor接口

package cn.itcast.spring.AOP;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;

public class TimeLogInterceptor implements MethodInterceptor {
    //日志记录器
    private static Logger LOGGER = Logger.getLogger(TimeLogInterceptor.class);

    @Override
    //invocation:代理对象的包装类,获取 代理对象,目标对象,目标方法等信息
    public Object invoke(MethodInvocation invocation) throws Throwable {

        //方法开始前
        long beforeTime = System.currentTimeMillis();


        //调用目标对象原来的方法,并返回结果
        Object proceed = invocation.proceed();//相当于 method.invoke();


        //方法结束后
        long afterTime = System.currentTimeMillis();


        //计算运行时间
        long runTime = afterTime - beforeTime;

        System.out.println(invocation.getThis().getClass().getName() + "类的" + invocation.getMethod().getName() + "方法运行了:" + runTime);

        LOGGER.info("- 方法名为 : " + invocation.getMethod().getName() + "()的运行时间为: " + runTime + "毫秒");

        return proceed;
    }
}


配置log4j.properties:

log4j.rootLogger=INFO,stdout,file
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n


log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout

#两种写法都可以
#log4j.appender.file.File=C:\\Users\\U100926\\Desktop\\my.log
log4j.appender.file.File=C:/Users/U100926/Desktop/my1.log

log4j.appender.file.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n




第三步:核心配置文件中,创建applicationContext.xml文件:

(确定目标和配置通知),仍然使用CustomerServiceImpl和ProductService进行测试。

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
       					   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">


    <!-- HelloService的bean -->
    <bean id="helloService" class="cn.itcast.spring.service.HelloService"/>

    <!-- 1.确定了要增强的target对象 -->
    <!-- 对于spring来说,目标:就是bean对象 -->
    <!-- 基于接口类 -->
    <bean id="customerService" class="cn.itcast.spring.AOP.CustomerServiceImpl"/>
    <!-- 基于一般类(没有实现接口的类) -->
    <bean id="productService" class="cn.itcast.spring.AOP.ProductService"/>

    <!-- 2.配置增强:原则bean能增强bean
        Advice:通知,增强
    -->
    <bean id="timeLogAdvice" class="cn.itcast.spring.AOP.TimeLogInterceptor"/>
  </beans>


第四步:使用SpringTest进行测试

package cn.itcast.spring.AOP;

import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

//springjunit集成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringTest3 {
    //注入要测试bean
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ProductService productService;

    //测试
    @Test
    public void test() {
        //基于接口
        customerService.save();
        customerService.find();
        //基于类的
        productService.save();
        productService.find();
    }
    private static Logger LOGGER = Logger.getLogger(SpringTest3.class);
    @Test
    public void printLog(){
        LOGGER.info("this is info log");
        LOGGER.debug("this is debug log");
        LOGGER.warn("this is warn log");
        LOGGER.error("this is error log");
        LOGGER.fatal("this is fatal log");
    }
}

第五步:查看执行结果:

在这里插入图片描述

但是发现,此时并没有执行TimeLogInterceptor 类的invoke()方法,也就是说,并没有计算执行Service类的时间,那怎么办呢?我们往下看,需要在spring容器中配置spring的aop。

配置切入点和切面

配置切面,让通知关联切入点

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
       					   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">


    <!-- HelloService的bean -->
    <bean id="helloService" class="cn.itcast.spring.service.HelloService"/>

    <!-- 1.确定了要增强的target对象 -->
    <!-- 对于spring来说,目标:就是bean对象 -->
    <!-- 基于接口类 -->
    <bean id="customerService" class="cn.itcast.spring.AOP.CustomerServiceImpl"/>
    <!-- 基于一般类(没有实现接口的类) -->
    <bean id="productService" class="cn.itcast.spring.AOP.ProductService"/>

    <!-- 2.配置增强:原则bean能增强bean
        Advice:通知,增强
    -->
    <bean id="timeLogAdvice" class="cn.itcast.spring.AOP.TimeLogInterceptor"/>

    <!-- 3.配置切入点和切面 :aop:config-->
    <aop:config>
        <!--
            配置切入点:即你要拦截的哪些 连接点(方法)
                * expression:表达式:匹配方法的,语法:使用aspectj的语法,相对简单
                * 表达式:bean(bean的名字),你要对哪些bean中的所有方法增强
                * expression=bean(*Service):在spring容器中,所有id/name以Service单词结尾的bean的都能被拦截
                * id="myPointcut":为切入点定义唯一标识
        -->
        <aop:pointcut expression="bean(*Service)" id="myPointcut"/>

        <!--
                advice-ref="timeLogAdvice"
                * 配置切面:通知(增强的方法)关联切入点(目标对象调用的方法)
                pointcut-ref="myPointcut"
                * 告诉:你要对哪些方法(pointcut),进行怎强的增强 (advice)
         -->
        <aop:advisor advice-ref="timeLogAdvice" pointcut-ref="myPointcut"/>
    </aop:config>
    
</beans>


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值