转载

Spring之AOP

  • IOC :控制反转

    • 将创建和管理对象全部交给框架完成
  • DI : 依赖注入

    set
    

AOP

  • 面向切面编程,是面向对象编程的重要组成部分,在不改变业务逻辑功能的基础上,对 横切逻辑 进行扩展
  • aspectj 框架是 aop 编程思想的体现, spring-aopaspectj 又进一步的封装
  • Aop的实现原理是 jdk的动态代理Cglib代理
  • 如果 IOC容器组件 实现接口使用 JDK动态代理 ,如果没有实现接口使用 Cglib代理

实现步骤

依赖jar包

  • aspectjweaver
  • aspectjrt
  • spring-aop
    • 这个是spring对 aspectj 的封装,因此我们使用起来更加简单

添加依赖

  • pom.xml 中添加如下依赖
<!-- 导入aspectj依赖 -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjrt</artifactId>
	<version>1.8.13</version>
</dependency>

<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.8.13</version>
</dependency>

<!-- 导入spring的aop -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aop</artifactId>
	<version>3.2.8.RELEASE</version>
</dependency>

创建一个aop的实现类

  • 创建类
@Component  //spring自动创建对象
@Aspect     //表示当前类是一个切面类
public class DemoAop{
	
	/**
	 * @Before("bean(bean的名称)")  表示该方法在这个bean中的所有方法执行之前执行
	 * 	其中可以使用通配符,比如bean("*ServiceImpl")  表示全部的service类,比如userServiceImpl
	 */
	@Before("bean(userServiceImpl)")//方法执行之前
	public void before(){
		System.out.println("方法执行之前...............");
	}
	
	
	/**
	 * 在方法之后执行
	 */
	@After("bean(*ServiceImpl)")
	public void after(){
		System.out.println("在方法执行之后执行");
	}
	
	/**
	 * 在业务方法没有异常的时候才会执行,并且实在@After之后执行
	 * 如果业务方法有异常,那么不会执行
	 */
	@AfterReturning("bean(*ServiceImpl)")
	public void afterReturning(){
		System.out.println("方法在after之后执行,并且这个业务方法没有出现异常");
	}
	
	/**
	 * 在业务方法发生异常才会执行,并且在@After之后执行
	 * 如果没有发生异常,不会执行
	 */
	@AfterThrowing("bean(*ServiceImpl)")
	public void afterThrowing(){
		System.out.println("方法发生异常执行....");
	}
	
	/**
	 * 环绕通知
	 * @param jp
	 * @throws Throwable 
	 */
	@Around("bean(*ServiceImpl)")
	public Object test(ProceedingJoinPoint jp)throws Throwable{
		System.out.println("环绕通知 .....之前");
		
		//调用业务层的方法,其中的Object是接收业务层方法的返回值
		Object object =jp.proceed();  
		
		System.out.println("环绕通知..........之后");
		return object;   //这里的返回值必须返回,否则在业务层将不会获取到
	}
		
}

定义配置文件

  • 配置注解扫描( spring-aop.xm l)
<!-- 定义spring组件扫描componet -->
<context:component-scanbase-package="cn.tedu.store.aop"/>

<!-- 解析切面注解 -->
<aop:aspectj-autoproxy/>

通知(5种)

  • @Before :在业务方法执行之前调用
  • @After :在方法之后执行
  • @AfterReturning :在方法之后执行,只有在业务方法没有出现异常的时候才会执行
  • @AfterThrowing : 在方法之后执行,只有在业务方法出现异常的时候才会执行
  • @Around : 环绕通知,在业务方法执行之前和之后执行,即使在 @Before 之前执行,在 @After之后 执行,必须又返回值,这里的返回值就是业务层方法的返回值,如果不返回,那么业务层方法就获取不到返回值

连接点

  • 业务层的所有方法,叫做连接点
  • 业务类中可以被增强的方法都叫做连接点

切点

  • 能切入切面逻辑的方法,叫做切点
  • 实际被增强的方法叫做切入点 ,其他的那些没有被增强的方法(连接点)不是切点

切面

  • 定义了增强方法的类就叫做切面

定义切点

第一种方式(基于spring创建的bean)

  • bena 的切点定义 : (bean("userServiceImpl")) ,这个是作用到该业务类中的所有方法上,并不能定义到某一个方法上
  • bean("*ServiceImpl") :作用到多个业务层,比如: userServiceImpl , addressServiceImpl
  • bean("userServiceImpl")||bean("addressServiceImpl") : 只作用到当前的两个业务层

第二种方式(基于类的)

  • (within("全类名")) : 其中写的是全类名
  • (within("cn.tedu.store.service.UserServiceImpl")) :作用于 UserServiceImpl 这个业务层中的所有方法
  • (within("cn.tedu.store.service.*ServiceImpl")) : 使用 * 通配符,作用到全部的业务层

第三种方式(基于方法的)

  • ("execution(* cn.tedu.store.service.UserServiceImpl.login(..))") :第一个 * 表示方法的返回类型,一般使用 * 表示,其中的形式是 全类名.方法名(..)
  • ("execution(* cn.tedu.store.service.UserServiceImpl.get*(..))") :这个将作用于 UserServiceImpl 这个业务类中的所有以 get 开头的方法
  • ("execution(* cn.tedu.store.service.*ServiceImpl.login(..))") : 这个将作用于所有的业务类中的所有以 get 开头的方法
  • ("execution(* cn.tedu.store..get*(..))") :这个将作用于 cn.tedu.store 这个包和其子包下的所有类中的所有以 get 开头的方法
@Component
@Aspect
public class TestAop{
    /**
    	在调用UserServiceImpl中的login()方法之前执行这个方法
    */
	@Before("execution(* cn.tedu.store.service.UserServiceImpl.login(..))")
	public void test(){
		System.out.println("TestAop.text");
	}
    
    	
	/**
	 * 测试登录的业务方法的性能
	 */
	@Around("execution(* cn.tedu.store.service.UserServiceImpl.login(..))")
	public Object test1(ProceedingJoinPoint jp)throws Throwable{
		Long before=System.currentTimeMillis();  //获取执行之前的系统时间
		Object object=jp.proceed();  //调用业务层的方法
		Long after=System.currentTimeMillis();  //获取执行之后的系统时间
		System.out.println(after-before);
		return object;
	}
}

使用场景

  1. 测试系统性能
  2. 打印日志
  3. 事务处理
  4. ……………………………………

实现原理

  • 基于动态代理完成
  • Aop的实现原理是 jdk的动态代理Cglib代理
  • cglib代理 使用的是 继承动态代理 使用的是 接口 ,如果需要添加横切逻辑的类没有接口,那么使用的是cglib代理,如果有接口,使用的是jdk的动态代理
  • spring-aop 是对 aspectj 的进一步封装
  • Spring之AOP

Spring-aop 处理事务

处理的前提

  • 默认发生 RuntimeException 或者其子类类型异常时, spring-aop 会捕获异常,并且处理事务

配置文件

  1. 创建事务管理器对象
  2. 开启基于注解的事务管理
<!-- 配置事务管理器 -->
<beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!-- 注入数据源,这里使用的是上面配置好的DataSource -->
	<propertyname="dataSource"ref="dataSource"></property>
</bean>

<!-- 开启事务注解 ,transaction-manager指定的是上面配置的事务管理器的id-->
<tx:annotation-driventransaction-manager="transactionManager"/>

注解配置事务 @Transactional

  • 可以在service的实现类上添加,那么所有的实现方法都会被事务管理器管理
  • 可以在某一个方法上添加,那么只有配置了注解的方法才会被事务管理器管理
  • 可以在 Service 的接口上添加注解,那么所有的接口方法都会被事务管理器管理
  • 我们推荐在 Service 的接口类上添加注解,并且在只涉及到 查询 语句的方法中设置传播行为为只读 @Transactional(readOnly=true)

Spring事务的传播属性

名称 解释
PROPAGATION_REQUIRED 0 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 Spring默认的事务的传播。
PROPAGATION_SUPPORTS 1 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 2 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 3 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 4 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 5 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 6 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

Spring事务的隔离级别

名称 解释
ISOLATION_DEFAULT -1 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应
ISOLATION_READ_UNCOMMITTED 1 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。
ISOLATION_READ_COMMITTED 2 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
ISOLATION_REPEATABLE_READ 4 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。
ISOLATION_SERIALIZABLE 8 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。

配置须知

  • 我们知道其实只有在涉及到数据库的修改才应该被事务管理,查询不需要被事务管理,但是一旦我们在一个 Service 接口上添加了 @Transactional 这个注解,那么默认这个接口中所有的方法都会被事务管理,因为这些方法都使用了默认的传播属性 PROPAGATION_REQUIRED ,我们可以在只涉及到 查询 语句的方法上添加 @Transactional(readyOnly=true) ,这样可以优化事务管理

实例

  • 接口上一旦添加了事务的注解,那么所有的方法都会被管理,但是我们可以设置只涉及到查询语句的方法传播属性为只读
/**
 * 博客的业务层接口
 * @author chenjiabing
 */
@Transactional  //在接口中添加事务管理,那么其中的所有方法都被事务管理了
public interface IBlogService{
	/**
	 * 获取当前用户的所有博客分类
	 * @param bloggerId  博主id
	 * @return
	 */
	@Transactional(readOnly=true)  //设置传播属性为只读,因为其中只涉及了查询语句
	List<BlogType>getBlogTypeList(Integer bloggerId);
	
	/**
	 * 添加博客
	 * @param blog  Blog对象,其中封装了需要添加的内容
	 */
// @Transactional(propagation=Propagation.REQUIRED) //这个是默认的,可以不用定义,因为在接口上已经定义了
	void addBlog(Blog blog);
	
	/**
	 * 分页获取博客总数
	 * @param bloggerId
	 * @param offest
	 * @param count
	 * @return
	 */
	@Transactional(readOnly=true)
	List<Blog>getBlogList(Integer bloggerId,Integer offest,Integer count);
	
	/**
	 * 获取博客总数
	 * @param bloggerId
	 * @return
	 */
	@Transactional(readOnly=true)
	IntegergetBlogCount(Integer bloggerId,String title,Integer typeId);
	
	/**
	 * 批量删除博客
	 * @param ids
	 */
	void moveBlogByIdsBatch(Integer[] ids);
	
	/**
	 * 根据id查询博客信息
	 * @param id  主键id
	 * @return  返回Blog对象,其中封装了需要的信息
	 */
	@Transactional(readOnly=true)
	BloggetBlogById(Integer id);
	
	/**
	 * 根据日期分类
	 * @param bloggerId
	 * @return
	 */
	@Transactional(readOnly=true)
	List<Blog_Count_ReleaseDateStr_Vo>getBlogGroupByReleaseDateStr(Integer bloggerId);
	
	/**
	 * 按照博客分类来获取博客信息
	 * @param typeId  分类id
	 * @return
	 */
	@Transactional(readOnly=true)
	List<Blog>getBlogByTypeId(Integer typeId,Integer offest,Integer count);
	
	/**
	 * 按照日期分类获取博客信息
	 * @param bloggerId  博主id
	 * @param releaseDateStr  日期
	 * @param offest  偏移量
	 * @param count  数量
	 * @return
	 */
	@Transactional(readOnly=true)
	List<Blog>getBlogByreleaseDateStr(@Param("bloggerId")Integer bloggerId,@Param("releaseDateStr")String releaseDateStr,@Param("offest")Integer offest,@Param("count")Integer count);
	
	/**
	 * 按照日期查询博客数量
	 * @param bloggerId  博主id
	 * @param releaseDateStr  日期
	 * @return
	 */
	@Transactional(readOnly=true)
	IntegergetBlogCount(Integer bloggerId,String releaseDateStr);
	
	/**
	 * 修改博客的点击次数
	 * @param id
	 * @param clickHit
	 */
	void modifyclickHit(Integer id,Integer clickHit);
}
原文  https://chenjiabing666.github.io/2018/05/21/Spring之AOP/
正文到此结束
Loading...