传统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>