一、AOP简介
AOP:Aspect Oriented Programming,面向切面编程,通过预编译方式和动态代理实现程序功能的统一维护的一种技术。利用AOP对业务逻辑各个部分进行抽取隔离,从而使逻辑各部分间的耦合度降低,提高程序的可重用性,提高开发效率。
-
JDK动态代理和Cglib动态代理
- JDK动态代理(基于接口)
Target target = new Target(); //创建目标对象
//创建代理对象,Target implements TargetInterface TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置增强代码..."); Object invoke = method.invoke(target, args);//反射方法对象,即要执行的目标对象 System.out.println("后置增强代码..."); return invoke; } }); - Cglib动态代理(基于父类)
Target target = new Target(); //创建目标对象 Enhancer enhancer = new Enhancer(); //创建增强器 enhancer.setSuperclass(Target.class); //设置父类 enhancer.setCallback(new MethodInterceptor() { //设置回调 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("前置代码增强...."); Object invoke = method.invoke(target, objects); System.out.println("后置代码增强...."); return invoke; } }); Target proxy = (Target) enhancer.create(); //创建代理对象
- JDK动态代理(基于接口)
-
AOP相关术语
Target(目标对象) 代理的目标对象
Proxy (代理) 一个类被 AOP 织入增强后,就产生一个结果代理类 Joinpoint(连接点) 那些可以被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点 Pointcut(切入点) 对哪些 Joinpoint 进行拦截的定义 Advice(通知/ 增强) 拦截到 Joinpoint 之后所要做的事情就是通知 Aspect(切面)
是切入点和通知(引介)的结合 Weaving(织入) 把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
二、基于XML的AOP开发
-
步骤:
- 导包:aspectjweaver & spring-context
- 创建目标类和目标接口Target(创建Pointcut)
- 创建切面类Aspect(创建Advice)
- 将Target和Aspect注入Spring(配置Spring配置文件)
- 配置织入关系,首先需要进行AOP命名空间和约束路径的配置
命名空间: xmlns:aop="http://www.springframework.org/schema/aop" 约束路径: http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
配置织入关系
<aop:config> <!--引用myAspect的Bean为切面对象--> <aop:aspect ref="myAspect"> <aop:pointcut id="myPointcut" expression="execution(* GroupId.ArtifactId.aop.*.*(..))"/> <!--抽取表达式,此处主要配置 哪个位置的哪些方法-->
<!--通知的配置语法:<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式">,目标类在包下-->
<!--逻辑:切点被切面类的通知增强,主要明确:哪个位置的哪些方法被进行了哪些增强--> <aop:before method="before" pointcut-ref="myPointcut"></aop:before> <!--此处配置 进行了哪些增强--> </aop:aspect> </aop:config>
-
详解:
- 表达式语句:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号 * 代表任意
- 包名与类名之间一个点 . 代表当前包下的类, 两个点 .. 表示当前包及其子包下的类
- 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
- 通知类型
- 表达式语句:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
三、基于注解的AOP
-
@Aspect 标注切面
-
方法前声明通知类型,会抽取表达式语句
通知的配置语法:@通知注解(“切点表达式")
表达式语句的抽取:抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在在增强注解中进行引用。
@Component("myAspect") @Aspect public class MyAspect { @Before("MyAspect.myPoint()") public void before(){ System.out.println("前置代码增强....."); } @Pointcut("execution(* 全限定名aop.*.*(..))") public void myPoint(){} }
-
在配置文件中开启组件扫描和 AOP 的自动代理
<!--组件扫描--> <context:component-scan base-package="包aop全限定名"/> <!--aop的自动代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
四、AOP应用-->声明式事务控制
-
基于XML
需要引入tx命名空间
xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
-
常用对象
PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。
PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc 或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager
TransactionDefinition 是事务的定义信息对象
TransactionStatus 提供事务具体的运行状态 -
配置事务增强
<!--平台事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> <!--事务方法通过connection对象调用--> </bean> <!--事务增强配置--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice>
-
配置事务AOP织入
<!--事务的aop增强--> <aop:config> <aop:pointcut id="myPointcut" expression="execution(* 全限定名.service.impl.*.*(..))"/>
<!--事务比较特殊,因为内部事务增强已经定义好了(创建回滚提交),此处详细配置哪个位置的哪些方法需要被增强--> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"></aop:advisor> </aop:config>tx:method 代表切点方法的事务参数的配置,即 TransactionDefinition,其他属性如下:
- name:切点方法名称
- isolation:事务的务的传播行为
- timeout:超时时间
- read-only:是否只读
-
-
基于注解
- 编写 AccoutDao
//转账事务 @Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private JdbcTemplate jdbcTemplate; public void out(String outMan, double money) { jdbcTemplate.update("update account set money=money-? where name=?",money,outMan); } public void in(String inMan, double money) { jdbcTemplate.update("update account set money=money+? where name=?",money,inMan); } }
- 编写 AccoutService
@Service("accountService") @Transactional public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED) public void transfer(String outMan, String inMan, double money) { accountDao.out(outMan,money); // int i = 1/0; //发生异常 accountDao.in(inMan,money); } }
- 编写 applicationContext.xml 配置文件,开启事务的注解驱动
<!--组件扫描--> <context:component-scan base-package="com.itheima"/> <!--事务的注解驱动--> <tx:annotation-driven/>
- 编写 AccoutDao