AOP概念
定义:面向切面编程
-----------------------------------------------------------------------------------------
理解:将共同的业务从传统业务中抽离出来,单独封装,通过配置的形式进行关联
优点:可以在不修改原有逻辑代码的情况下给系统追加功能
-----------------------------------------------------------------------------------------
AOP典型应用
1.追加事务控制
2.异常日志记录
3.测试模块性能
-----------------------------------------------------------------------------------------
案例:要求在每个Controller方法执行之前输出打桩信息
封装一个组件,通过配置将封装的组件追加到每一个Controller方法中
AOP的相关概念:切面,切入点,通知,动态代理
切面(aspect):指的是封装的共同处理的组件,并且能够切入到其他组件的方法上,可以理解为封装的部分代码
切入点(pointcut):用于指定目标组件的方法
1.方法限定表达式
可以给某个组件中部分方法追加共同功能
execution(修饰符? 返回类型 方法名称(参数) 异常抛出?)
例如:execution(* add*(..))------匹配到add开头的所有方法(在部分方法上追加)
例如:execution(* cn.tedu.cloud_note.UserService.* (..))------匹配到UserService包下面的所有方法
例如:execution(* cn.tedu.cloud_note.service.*.* (..))------匹配到service包下所有类的所有方法
例如:execution(* cn.tedu.cloud_note.service..* (..))------匹配到service包及其子包下面的所有方法
---------------------------------------------------------------------------------------
2.类型限定表达式(重点且通用)
可以给某个组件所有方法追加共同功能
within(类型)
例如:within(cn.tedu.cloud_note.service.UserService)------匹配到UserService组件下面的所有方法
例如:within(cn.tedu.cloud_note.service.*)------匹配到service包下所有类的所有方法
例如:within(cn.tedu.cloud_note.service..*)------匹配到service包及其子包下的所有方法
---------------------------------------------------------------------------------------
3.bean名称限定表达式
可以给某个组件中所有方法追加共同功能
bean(id名字) 注意id的首字母
例如:bean(userService)------匹配id=userService组件的所有方法
例如:bean(*Service)------匹配以Service结尾的组件的所有方法
-----------------------------------------------------------------------------------------
通知:用于指定切入的时机,spring提供了5种通知类型
try{
前置通知<aop:before>
//执行的目标方法
后置通知<aop:after-returning>
}catch{
异常通知<aop:after-throwing>//例如:日志
}finally{
最终通知<aop:after>
}
@around=前置+后置(环绕通知)
-----------------------------------------------------------------------------------------
代码如下:
package cn.tedu.cloud_note.aspect;
public class LoggerBean {
public void logController(){
System.out.println("AOP功能注入!");
}
}
-----------------------------------------------------------------------------------------
使用配置文件配置AOP:
<!-- 配置一个bean,用于实例化切面部分的代码-->
<bean id="loggerBean" class="cn.tedu.cloud_note.aspect.LoggerBean"></bean>
<!-- -->
<aop:config>
<!-- 通过ref关联组件类 -->
<aop:aspect ref="loggerBean">
<!-- 通过method指定处理方法(bean中类里面的方法) pointcut切入点-->
<!-- 类型限定表达式(重点且通用)controller包及其子包下的所有方法 -->
<!--
<aop:before method="logController" pointcut="within(cn.tedu.cloud_note.controller..*)"/>
-->
<!-- 使用方法限定类型 service包下的所有类中的所有方法 -->
<!--
<aop:before method="logController" pointcut="execution(* cn.tedu.cloud_note.service.*.*(..))"/>
-->
<!-- bean(id)限定类型 因为使用的是bean的id,所以属于类级别 -->
<aop:before method="logController" pointcut="bean(*Controller)"/>
</aop:aspect>
</aop:config>
在id后缀为Controller的组件中切入方法logController,当调用Controller组件的时候会先调用该方法,使用了前置通知(before)
-----------------------------------------------------------------------------------------
切面:要给代码追加的功能(单独封装的代码)
切入点:给谁(所有的Controller)增加功能
通知:切入的时机(前置,后置,环绕......)
-----------------------------------------------------------------------------------------
动态代理
理解:spring中IOC注入的是一个bean的实现类,而AOP原理是使用动态代理技术,动态创建一个新的类型,重写目标接口或目标类的方法,在重写方法中追加了要切入的功能代码,然后实现注入
动态生成一个新的类型:(追加的处理和原有的代码功能)
注意:事务处理的时候需要配置
示意图:
-----------------------------------------------------------------------------------------
AOP事务的配置文件:
<!-- spring的事务处理,通过AOP实现的 -->
<!-- 定义事务管理Bean -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dbcp" />
</bean>
<!-- 用于支持 @Transactional 注解 必须配置事务管理器属性, 其值是一个Bean的ID-->
<tx:annotation-driven transaction-manager="txManager"/>
以UserServiceImpl为例:
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {
加上@Transactional注解
-----------------------------------------------------------------------------------------
动态代理测试:
public class TestUserService {
private UserService service;
@Before
public void init(){
String[] conf = {"conf/spring-mvc.xml",
"conf/spring-mybatis.xml",
"conf/spring-transaction.xml"
};
ApplicationContext ac = new ClassPathXmlApplicationContext(conf);
service = ac.getBean("userService",UserService.class);
}
@Test//测试:用户名不存在
public void test1(){
NoteResult<User> result = service.checkLogin("abc", "123456");
//输出service是什么类型的--UserServiceImpl的一个实例
System.out.println(service.getClass().getName());
}
}
若不使用动态代理,则输出的是一个实现类:cn,tedu.cloud_note.service.UserServiceImpl
由于UserServiceImpl 使用了动态代理,所以注入的该实例为动态生成的额类型:com.sun.proxy.$Proxy21
=====================================================================
以上结构主要是原理,理解,重点是以下代码:利用注解配置AOP
AOP注解配置
@Component 起到定义<bean>的作用
@Aspect 起到<aop:aspect ref="loggerBean">的作用
@Before 起到<aop:before method="logController" pointcut="within()"/>的作用
-----------------------------------------------------------------------------------------
使用注解配置AOP:
spring-aop.xml文件:
<!-- 配置AOP注解扫描 -->
<context:component-scan base-package="cn.tedu.cloud_note.aspect" />
<!-- 开启注解标记 -->
<aop:aspectj-autoproxy />
-----------------------------------------------------------------------------------------
package cn.tedu.cloud_note.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component //定义bean
@Aspect //表示切面代码
public class LoggerBean {
@Before("within(cn.tedu.cloud_note.controller..*)")//切入点
public void logController(){
System.out.println("AOP功能注入!");
}
}