spring -- aop

概述

AOP(Aspect Orient Programming)是一种设计思想 ,是软件设计领域中面向切面的编程,它是面向对象(OOP)编程的一种补充和完善 它可以通过通过预编译方式运行期动态代理的方式,实现在不修改源代码的情况下给程序动态添加额外功能的程序

什么是Aop

公式 :AOP = 通知方法 + 切入点表达式
说明 :当程序的执行满足aop表达式的时候,程序的代码会发生变化
场景: 日志记录 监控程序执行的时间 实现事物的控制 异常监控 缓存处理
特点 : 公共的业务

启动依赖

spring boot 添加依赖

	<dependency>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-aop</artifactId>

   </dependency>

原理分析

spring	Aop 底层给予代理机制实现功能扩展:
  • 假如目标对象(就是需要增强功能的对象 类)实现接口,则底层可以采用jdk动态代理机制为目标对象创建代理对象($Proxy33… )
  • 假如目标对象(被代理对象) 没有实现接口 则底层采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理对象会继承目标对象的类型)
    在这里插入图片描述
    spring中默认使用CGLIB代理 假如需要jdk动态代理 可以在配置文件中进行如下配置
spring.aop.proxy-target-class=false

AOP相关术语分析

切面(Aspect):该注解描述的类型为spring AOP中切面对象类型,用于定义一个切面对象,此对象类型可以封装:
a)切入点
b)通知

在这里插入图片描述

切入点(pointcut):注解用于定义切入点,具体方式可以基于特定表达式实现。例如:
  • bean 为一种切入点表达式
  • bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法。
    bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法。
  • sysUserServiceImple 为spring容器中的一个bean对象的名字(首字母小写) 当这个类中的任一方法执行时,都由本切面的通知方法进行功能增强
//注释方式的切入点表达式
@Pointcut("bean(sysUserServiceImpl)")
public void doLongPointCut(){};

@annotation 用于匹配具体方法的注释
com.cy.pj.common.annotation.RequiredCache 为注解的全路径

@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredCache)")
public void doLongPointCut(){};

execution(返回值类型 包名.类名.方法名(参数列表))

@Pointcut("execution(int com.cy.pj.add(int))")
public void doLongPointCut(){};
// 	..代表 任意参数类型   * 代表任意返回值类型
@Pointcut("execution(* com.cy.pj.add(..))")
public void doLongPointCut(){};
//返回值类型任意  并位于 com.cy.pj的所有子包中的所有类的所有方法
@Pointcut("execution(* com.cy.pj..*.*(..))") 
public void doLongPointCut(){};

在这里插入图片描述

扩展 自定义注解

修饰符 @Interface

/**
 * 自定义注解:(使用@interface定义注解,默认所有注解都是一个Annotation类型的对象)
 * @Target 注解用于告诉jdk我们自己写的注解可以描述的对象。
 * @Retention 注解用于告诉JDK我们自己写的注解何时有效。
 * 说明:所有的注解都是一种元素据(Meta Data)-一种描述数据的数据(例如使用注解描述类,
 * 描述方法,描述属性,描述方法参数等等)
 */
@Retention(RetentionPolicy.RUNTIME) //运行时有效
@Target(ElementType.METHOD) //可以修饰方法
public @interface RequiredCache {
	 String value() default "operation"; //定义注解中的属性  加上default就表明此值用户可以指定 也可以不用指定
}
连接点(joinpoint)

程序执行中某个特定的点,一般指被拦截到的方法,

  • ProceedingJoinPoint 一般用于@Around方法中的参数
  • JoinPoint 用于别的注解方法中
    可以通过 该对象调用proceed()方法 表示执行 目标方法
    Signature 封装了连接点信息的api 接口 MethodSignature 是该接口的一个实现类
@Around("doLongPointCut()")
public Object around(ProceedingJoinPoint jp){
	//获取目标方法的类名
    String methodName=jp.getSignature().getDeclaringTypeName();
   //获取目标方法参数  返回值为一个Object 数组
    Object[] objs=jp.getArgs();
	Objecet result =jp.proceed();//表示执行被增强的方法 返回值为Object类型
	//获取目标对象(要执行的那个目标业务对象)的类 ,返回值为Class<?> 基于此通过反射可以获取信息
	Class<?> targetClass=jp.getTarget().getClass();
	//获取方法签名,基于此签名 可以获取目标方法的方法名称 ,封装了要执行目标方法的信息
	MethodSignature ms=(MethodSignature)jp.getSignature();
	//获取方法的返回值类型
	Class returnClass = ms.getReturnType();
	//通过反射获取目标方法对象 基于此对象 获取方法上的注解,进而取到操作名
	//方法名 通过方法签名获取 方法类型也是
	//返回值为方法 Method
	Method tartgetMethod=targetClass.getMethod(ms.getName(),ms.getParameterTypes());
	//通过方法对象 获取该方法上的指定注解  假设注解为RequestMapping
	RequestMapping re=targetMethod.getAnnotation(RequestMapping.class);
	//通过注解对象 获取里面的值
	re.value();
	...
	}
@Before"doLongPointCut()"public void before(JoinPoint jp){
...
}
//通过连接点 获取目标方法上的信息  如果注解类名 为@RequiredCache 
//这样写的意思 是 拦截名字为aaa的类型的注解描述的方法  注意 名字自己起但是必须相同(aaa)
@Arount@annotation(aaa)public Object around(ProceedingJoinPoint jp , RequiredCache aaa){
		// 通过注解类型 aaa 获取 注解中的值
		aaa.value();
}

案例:redis +aop 实现缓存

//=====================aop切面
 @Autowired(required = false)  //告知spring容器 ,该注入不是必需的,如果该对象被程序调用时才会被注入只
    private Jedis jedis;
    @Around("@annotation(cacheTree)")
    public Object befor(ProceedingJoinPoint jp,CacheTree cacheTree) throws Throwable {
		Object result = null;
        ObjectMapper json = new ObjectMapper();
        //获取方法参数  返回值为一个Object 数组
        Object[] objs = jp.getArgs();
        String key = cacheTree.key() +"::"+objs[0];
        if (jedis.exists(key)) {
            String treecontext = jedis.get(key);
            System.out.println("缓存");
              MethodSignature ms = (MethodSignature) jp.getSignature();
            //获取方法的返回值类型
            Class returnType = ms.getReturnType();
            result = json.readValue(treecontext, returnType);
                return tree;
        }else {
            Object result = jp.proceed();
            if (cacheTree.seconds()>0){
                jedis.setex(key, cacheTree.seconds(), ObjectMapperUtil.toJson(result));
            }else{
                jedis.set(key, ObjectMapperUtil.toJson(result));
            }
            return result;
        }
    }
    //================================== 目标方法
 @Override
@CacheTree(key="ITEM_CAT_LIST")//使用注解自定义前缀
public List<EasyUITree> findItemCatByParentId(Long parentId) {

		System.out.println("查询");
		QueryWrapper<ItemCat> queryWrapper =new QueryWrapper<>();
		queryWrapper.eq("parent_Id", parentId);
		List<ItemCat> selectList = catMapper.selectList(queryWrapper);
		List<EasyUITree> tree =new ArrayList<>();
		for (ItemCat itemcat : selectList) {
			Long id = itemcat.getId();
			String text = itemcat.getName();
			String state = itemcat.getIsParent() ? "closed" : "open";
			tree.add(new EasyUITree(id, text, state));
		}
		return tree;
	}
//=========================================注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheTree {
    //设定key
    String key();
    //超时时间
    int seconds() default -1; //加上defailt  用户可写可不写
}

通知(Advice)

在切面的某个特定的连接点上 执行的动作
@Around 环绕通知 目标方法执行前后都要执行的方法(重点掌握) 可以影响程序的执行轨迹
  • 优先级最高, 方法返回值为Object
  • 方法参数 为ProcedingingJoinPoint类型
  • 方法抛出异常 Throwable
  • 可以在目标方法执行之前或者之后进行执行 如果是之前执行 优先级比@Before还要高,优先执行。
1.@Before 前置通知 在目标方法执行之前执行
2.@AfterReturning 后置通知 在目标方法执行之后执行
3.@AfterThrowing 异常通知 抛出异常时执行
4.@After 最终通知 无论用户发生了什么变化 都执行的操作 最后执行

在这里插入图片描述

Aop 缓存(自己定义的一个小Map)示例

@Aspect
@Component
public class SysCacheAspect {
	   @Autowired
	   private SimpleCache simpleCache;
	   /**
	    * @annotation(..)为注解方式的切入点表达式,这里表示由RequiredCache注解描述
	         * 的方法为切入点方法。
	    */
	   @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredCache)")
	   public void doCachePointCut() {}
	  
	   //@Pointcut("execution(* com.cy.pj.sys.service..*.updateObject(com.cy.pj.sys.pojo.SysDept))")
	   @Pointcut("@annotation(com.cy.pj.common.annotation.ClearCache)")
	   public void doClearCachePointCut() {}
	   
	   @AfterReturning("doClearCachePointCut()")
	   public void doAfterReturning() {//目标方法正常结束以后执行
		   System.out.println("==clear cache==");
		   simpleCache.clearObject();
	   }
//	   @AfterReturning("doClearCachePointCut()")
//	   public Object doAroundClearCache(ProceedingJoinPoint jp)throws Throwable{
//		   Object result=jp.proceed();
//		   simpleCache.clearObject();
//		   return result;
//	   }
	   
	   @Around("doCachePointCut()")
	   public Object around(ProceedingJoinPoint jp)throws Throwable{
		   System.out.println("Get data from cache");
		   Object obj=simpleCache.getObject("deptCache");//这个key的名字先自己写个固定值
		   if(obj!=null){
		   return obj;
		   }
		   Object result=jp.proceed();//最终要执行目标方法
		   System.out.println("put data to cache");
		   simpleCache.putObject("deptCache", result);
		   return result;
	   }
}

spring AOP中 Cache操作实现

启动缓存配置

这里的缓存是AOP自带的 需要在springboot 的启动类上 添加@EnableCaching注解,就可以启动缓存配置

/**
* 异步的自动配置生效).
 * @EnableCaching 注解表示启动缓存配置
 */
@EnableCaching
@SpringBootApplication
public class Application {
        public static void main(String[] args) {
                SpringApplication.run(Application.class, args);
        }
}

在具体方法上 应用缓存配置

在需要进行缓存的方法上通过@Cacheable注解进行描述,表示要将方法的返回值存储到Cache中

//缓存中的数据结构 是key  value 
//@Cacheable 表示要将该方法的返回值List<Map<String,Object>> 存储到缓存中的value
// 这里的deptCache 将作为缓存中的key
@Cacheable(value = "deptCache")
@Transactional(readOnly = true)
public List<Map<String,Object>> findObjects() {
....
}

在需要清除缓存的方法上 加注解@CacheEvict

	//@CacheEvict 注解描述的方法 表示要清除缓存
	//value 表示要清除的缓存的名字 key
	//allEntries 表示要清除缓存对象中的那些属性 true为所有
	// beforeInvocation 表示要在方法执行前清除 还是在执行后清除 true为在前
    @CacheEvict(value = "menuCache",allEntries = true,beforeInvocation = false)
    @Override
    public int saveObject(SysDept entity) {...}

Aop 异步操作实现

1. 启动异步配置

基于注解的方式进行配置,在springboot 的启动类上加上@EnableAsync注解  表示 启动线程池
@EnableAsync //spring容器启动时会创建线程池

   @SpringBootApplication

   public class Application {

        public static void main(String[] args) {

                SpringApplication.run(Application.class, args);

        }

}

2.在需要异步操作的业务方法上加@Async注解

为了提高程序的效率 ,可以将一些不需要同步更新的数据的写入数据库的操作来异步执行,从而提高项目的效率

	@Async //使用此注解描述的方法会运行在由spring框架提供的一个线程中。
	@Override
	public void saveObject(SysLog entity) {
		String tName=Thread.currentThread().getName();
		System.out.println("SysLogServiceImpl.saveObject.thread.name="+tName);
		try{Thread.sleep(5000);}catch(Exception e) {}
		sysLogDao.insertObject(entity);
	}

异步执行的方法 如果需要返回值 可以进行如下修改:
AsyncResult对象可以对异步方法的执行结果进行封装,假如外界需要异步方法结果时,可以通过Future对象的get方法获取结果。


	@Async //使用此注解描述的方法会运行在由spring框架提供的一个线程中。
	@Override
	public Futrue<Integer> saveObject(SysLog entity) {
		String tName=Thread.currentThread().getName();
		System.out.println("SysLogServiceImpl.saveObject.thread.name="+tName);
		try{Thread.sleep(5000);}catch(Exception e) {}
		int rows=sysLogDao.insertObject(entity);
		return new AsyncResult<Integer>(rows);
	}

Spring Aop事务处理

事物的定义
事务(Transaction)是一个业务,是一个不可分割的逻辑工作单元,基于事务可以更好的保证业务的正确性。
事物的特性:

  • 原子性(Atomicity):一个事务中的多个操作要么都成功要么都失败
  • 一致性(Consistency): (例如存钱操作,存之前和存之后的总钱数应该是一致的。
  • 隔离性(Isolation):事务与事务应该是相互隔离的
  • 持久性(Durability):事务一旦提交,数据要持久保存。

1. 启动事物

1.在启动类上 加上注解@EnableTransactionManagement,新版本中也可以不用添加。
2.将@Transactional注解 ,并设置合适的属性信息

 @Transactional(//事物的超时时间 默认值为-1 表示没有超时限制,如果设定了时间,超过具体时间还没有完成则自动回滚事物
 			   timeout = 30,
 			   //指定事物为只读事物,默认值为false:设置为true时表示为只读,只能读取数据,不能修改 删除等业务
               readOnly = false,
				//issolation 表示事物的隔离级别
               isolation = Isolation.READ_COMMITTED,
				// rollbackFor 指定事物能够触发回滚的异常类型
				//no-rollbackFor 抛出指定异常的类型 不用回滚事物
               rollbackFor = Throwable.class,
				
				//propagation=Propagation.REQUIRES_NEW 表示为新事物 挂起当前事物,开启新的事物
				//表示新的事物参与当前事物
               propagation = Propagation.REQUIRED
               )
               

  @Service

  public class SysUserServiceImpl implements SysUserService {

        @Transactional(readOnly = true)

    @Override

         public PageObject<SysUserDeptVo> findPageObjects(

                        String username, Integer pageCurrent) {}

}

缓存穿透:

特点:

  • 用户 在高并发环境下 访问数据库中根本不存在的数据 缓存中没有 会一直查询数据库 可能发生宕机的风险

解决方案:

  • 通过API网关解决
  • 限定ip的访问次数 限定ip每秒的次数

缓存击穿:

特点

  • 由于用户高并发访问 ,访问的数据 刚开始有缓存,但是由于特殊原因,导致(少量的)缓存失效 ,也会一直查询数据库,造成宕机风险。 只影响一段时间 例如:redis 的缓存设定时间正好清除 恰好又来了访问

解决方法:

  • 通过缓存(redis)串联的方式解决 造成时间差 不会让缓存 同时消失

缓存雪崩:

特点:

  • 高并发的环境下 ,大量用户访问服务器,redis中有大量的数据在同一时间超时(删除)**。导致用户直接访问数据库

解决方案:

  • 通过缓存(redis)串联的方式解决 造成时间差 不会让缓存 同时消失

Redis 持久化问题

问题说明

Redis中的数据都保存在 内存中,如果服务关闭或者道济,则内存资源直接丢失,导致缓存失效

RDB模式

特点:

  • 采用定期持久化的方式(例如:每隔10持久化一次) 可能会丢数据
  • 记录的是当前redis的内存记录快照 ,只记录当前状态 (快照只有一张)持久化效率最高
  • redis的默认持久化方式

持久化命令
1. save : 立即持久化 同步操作 可能对现有的操作 造成堵塞
2. bgsave : 异步操作 开启单独的线程 实现持久化任务 不能保证执行的时间

Aof 模式

特点

  • 默认关闭状态
  • 能够记录程序的执行过程,可以实现数据的实时持久化 AOF文件占用的空间较大,回复的速度较慢
  • AOF模式开启之后 RDB模式将不会再生效

如何选择 持久化方式

如果允许少量数据丢失  选择 RDB
如果不允许 数据丢失  选择 AOP

redis中如果 误操作了flushAll操作 如果持久化 命令是aof

解决方法 : 快速停止redis 修改aof文件 将flushAll 删除 重启redis

redis 内存的优化

修改redis的内存

在这里插入图片描述

场景说明
1.redis 运行的空间是内存,内存资源比较紧缺,所以,应该维护ridis的内存数据 ,让redis保存 热门数据
内存策略优化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值