Q为什么需要切面
A:事务模块不仅仅是为特定业务提供服务,还要处理系统辅组功能(日志记录,安全管理)。这时候我们就需要aop,将核心代码以及次要代码分隔开。这样做有两个好处:1.关注点代码都集中于一处,而不是分布在多代码,方便管理2.其次,服务模块更加简洁,因为他们只用关注功能逻辑的代码。以后需要添加辅助功能时候,也不会干涉到原本的类。
然后就会在运行期间,根据切入点动态织入目标对象中,生成代理对象。
AOP几个专属名词解释
通知:就是切面需要额外执行的逻辑
- 前置通知:在目标方法调用前
- 后置通知:在目标方法调用完成之后,但是没有执行渲染
- 异常通知
- 环绕通知
连接点:可以插入切点的地方
切点:插入切面的地方
切面:what(通知内容) when(通知时间) where(切点)
织入:切面在指定的连接点中,织入到目标对象中,目标对象的生命周期中有多个点可以进行织入:
编译期间:切面在目标类编译时被织入,这种方式需要特殊的编译器。AspectJ的织入编译就是这样的
类加载期:切面在目标类加载到JVM时候被织入。这种方式需要特殊的类加载(class loader),它可以在目标类被引用之前强加入目标类的字节码。
运行期:切面在应用运行时的某一个时刻织入,一般情况下,在织入切面时候,AOP容器会为目标对象生成一个代理对象。springaop就是采用运行期。
定义切面的类 也是需要注册到IOC容器中,运行期间调用目标类时候,首先判断是否需要将切面织入到目标类中,如果不需要直接返回目标类,否则返回代理类。
切面织入到目标类中,通过动态代理生成代理类,放置在springaop容器中。
以下转自:https://www.cnblogs.com/cndota/p/6129244.html
简单的记录一下spring aop的一个示例
基于两种配置方式:
基于xml配置
基于注解配置
这个例子是模拟对数据库的更改操作添加事物
其实并没有添加,只是简单的输出了一下记录
首先看下整个例子的目录图
全部代码就不贴了,数目有点多,不过很简单,看一部分就能够明白
第一种配置方式
基于xml方式配置
首先将service,dao注册到spring容器
配置一下扫描包还是很方便的
接下来看下service
1 package com.yangxin.core.service.impl;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.stereotype.Service;
5
6 import com.yangxin.core.dao.UserDao;
7 import com.yangxin.core.pojo.User;
8 import com.yangxin.core.service.UserService;
9
10 @Service
11 public class UserServiceImpl implements UserService {
12
13 @Autowired
14 private UserDao userDao;
15
16 @Override
17 public void addUser(User user) {
18 userDao.insertUser(user);
19 System.out.println("添加成功");
20 }
21
22 @Override
23 public void deleteUser(String name) {
24 userDao.deteleUser(name);
25 System.out.println("删除成功");
26 }
27
28 }
要做的事情很简单,插入一条数据,删除一条数据
接下来看下切面代码
1 package com.yangxin.core.transaction;
2
3 import org.aspectj.lang.ProceedingJoinPoint;
4
5 import com.yangxin.core.pojo.User;
6
7 public class TransactionDemo {
8
9 //前置通知
10 public void startTransaction(){
11 System.out.println("begin transaction ");
12 }
13
14 //后置通知
15 public void commitTransaction(){
16 System.out.println("commit transaction ");
17 }
18
19 //环绕通知
20 public void around(ProceedingJoinPoint joinPoint) throws Throwable{
21 System.out.println("begin transaction");
22
23 joinPoint.proceed();
24
25 System.out.println("commit transaction");
26 }
27
28 }
然后看下这个切面在applicationContext.xml中是如何配置的
<aop:config>
<aop:pointcut expression="execution(* com.yangxin.core.service.*.*.*(..))" id="p1" />
<aop:aspect ref = "transactionDemo">
<aop:before method="startTransaction" pointcut-ref="p1" />
<aop:after-returning method="commitTransaction" pointcut-ref="p1"/>
</aop:aspect>
</aop:config>
这里没有演示环绕通知
好了,运行测试代码
控制台输出如下
begin transaction
添加成功
commit transaction
begin transaction
删除成功
commit transaction
现在来测试一下环绕通知
修改一下applicationContext.xml中的配置切面那一部分
修改后的代码
<aop:config>
<aop:pointcut expression="execution(* com.yangxin.core.service.*.*.*(..))" id="p1" />
<aop:aspect ref = "transactionDemo">
<aop:around method="around" pointcut-ref="p1"/>
</aop:aspect>
</aop:config>
运行测试代码
输出如下
begin transaction
添加成功
commit transaction
begin transaction
删除成功
commit transaction
好了,现在贴下如何用注解的方法
贴下基于注解的切面的代码
package com.yangxin.core.transaction;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class TransactionDemo2 {
@Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))")
public void point(){
}
@Before(value="point()")
public void before(){
System.out.println("transaction begin");
}
@AfterReturning(value = "point()")
public void after(){
System.out.println("transaction commit");
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("transaction begin");
joinPoint.proceed();
System.out.println("transaction commit");
}
}
在applicationContext.xml中配置
1 <bean id = "transactionDemo2" class = "com.yangxin.core.transaction.TransactionDemo2" />
1 <aop:aspectj-autoproxy />
测试步骤和以上一致,这里就不贴了
完毕
记一下使用javaConfig配置方式的一些坑
以上 切面 不管是基于注解的还是基于xml配置的 这里把切面加载到容器中都是用xml配置bean的方式
如果用javaConfig方式的话
需要在配置类上加上 @EnableAspectJAutoProxy 注解
然后如果使用扫包的方式配置切面bean的话
切面上除了要加上@Aspect注解标识为这是一个切面bean之外
还需要在上面加上@component这个注解
很急很关键
排错的时候只关注切点表达式的问题了
当时一直在找切点表达式的问题
其实这个表达式写的是没有问题的
是配置切面bean的时候出的问题
深坑
切面也是属于ioc容器的,@EnableAspectJAutoProxy true默认是cglib动态代理