spring AOP
AOP概念
aspect oriented programming
面向切面编程
是什么?
将共同业务处理从传统业务处理中抽离出来,单独封装,然后以配置的形式进行关联
为什么?
可以在不修改原有的逻辑代码的情况下,给系统追加功能
AOP
- 追加事务控制
- 异常日志记录
案例:
- 要求:在每个Controller方法执行前输出打桩信息
- 封装一个组件
- 通过配置将封装的组件追击到Controller方法里
AOP相关概念
- oop:类,对象,封装,继承,多态
- AOP:切面,切入点,通知,动态代理
切面(aspect)
指的是封装了共同处理的组件,并且能够切入到其他的组件的方法上
切入点(pointcut)
用于指定目标组件的方法
- 方法限定表达式
可以给某个组件中的部分方法追加共同功能
execution(修饰符?返回类型 方法名(参数) 异常抛出?)
//匹配到add开头的所有方法
execution( add(..))
//匹配UserService包下的所有方法
execution( io.codegitz.UserService.(…))
//匹配到service包下及其子包下的所有方法
execution( io.codegitz.service..(..))
//匹配到service包下所有类的方法
execution( io.codegitz.service..*(..)) 类型限定表达式
可以给某个组件的所有方法追加功能
within(类型)
//匹配UserService组件下的所有方法
within(io.codegitz.service.UserService)
//匹配service包下的所有类的所有方法
within(io.codegitz.service.)
//匹配service包及其子包下的所有方法
within(io.codegitz.service..)bean名称限定表达式
可以给某个组件中国的所有的方法追加功能
bean(id名)
//匹配id=userService的组件的所有方法
bean(userService)
//匹配以Service结尾的所有组件的所有方法
bean(*Service)
AOP配置文件
<?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:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.2.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<bean id="loggerBean" class="io.codegitz.cloud_note.aspect.LoggerBean">
</bean>
<aop:config>
<!-- 通过REF关联组件类 -->
<aop:aspect ref="loggerBean">
<aop:before method="logController" pointcut="within(io.codegitz.cloud_note.controller..*)" />
</aop:aspect>
</aop:config>
</beans>
遇到错误:
cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'aop:config'.
有点狗的是引用和xsd文件都有了,后来把后面的版本号去掉了,然后就暂时不报错了,过了一会再次报错,然后把版本号又加了回去,然后就行了….
通知
用于指定切入的时机
spring提供了五种通知类型
try{
使用前置通知
//执行的目标方法
后置通知
}catch{
异常通知
}finally{
最终通知:
}
@around=前置+后置通知
切面:追加啥功能?单独封装的代码
切入点:切谁?所有Controller
通知:啥时候切?前置,后置,环绕
动态代理
AOP原理:使用动态代理技术
可以创建一个新的类型,重写目标接口或目标类的方法
在重写方法中,追加了要切入的功能代码和方法代码
AOP的配置使用形式
<?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:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.2.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<!-- Spring事务管理 -->
<!-- 定义事务管理bean -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dbcp"></property>
</bean>
<!-- 开启transaction注解扫描,将标记作用在具体的方法上 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
然后在实现类前面加一个@Transactional注解
执行此测试方法:
@Test
public void testUserService1(){
System.out.println(service.getClass().getName());
}
未使用动态代理则会输出原来的类名:io.codegitz.cloud_note.service.UserServiceImpl
使用动态代理输出动态代理名:com.sun.proxy.$Proxy22
spring有两种动态技术
- 基于目标接口的
- 基于目标类的
public class $Proxy22 implements UserService{
public void checkLogin(){
//追加事务处理
//重写了UserServiceImpl.checkLogin
}
}
public class $Proxy22 extends 目标类{
}
AOP注解配置
注解标记
@Component 起到应以
@Aspect
@Before
案例:
- 要求:当系统发生service异常,将异常信息写入到日志文件
- 切面:将异常写入文件
- 切入点:after-throwing(“within(service..* )”)
记录异常类:
package io.codegitz.cloud_note.aspect;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component //扫描spring容器
@Aspect //将该类作为切面组件
public class ExceptionBean {
//e是目标组件抛出的异常对象
@AfterThrowing(throwing="e",pointcut="within(io.codegitz.cloud_note.service..*)")
public void execute(Exception e){
//将异常信息输入文件
try {
FileWriter fw=new FileWriter("F:\\note_error\\record.log",true);
PrintWriter pw=new PrintWriter(fw);
//利用pw对象写入异常信息
Date time=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strTime=sdf.format(time);
pw.println("************************");
pw.println("异常类型:"+e);
pw.println("异常事件:"+strTime);
pw.println("*****异常的详细信息******");
e.printStackTrace(pw);
pw.close();
fw.close();
} catch (Exception ex) {
System.out.println("记录异常失败");
}
}
}
模拟异常:
String str=null;
str.length();
后台报错:
日志文件记录:
记录异常成功。