Spring AOP面向切面编程(二)
一、切入点的execution表达式
execution的表达形式: execution(修饰符? 返回值类型 所在包类? 方法名(参数列表) 异常?)
- ?表示可有可无
execution(public * *(..))
工程中所有的public方法
execution(* set*(..))
工程中所有方法名以set开头的方法
execution(* com.xyz.service.AccountService.*(..))
com.xyz.service.AccountService类下面的所有方法
execution(* com.xyz.service..(..))
com.xyz.service包下所有类的所有方法
execution(* com.xyz.service...(..))
com.xyz.service包下及其子包下所有类的所有方法
execution(* com.xyz..service..(..))
报名以com.xyz开头的所有子包一直子到service下的所有类的所有方法
举例:com.xyz.a.b.service com.xyz.a.service com.xyz.a.b.c.service 会满足上面的条件- ?表示可有可无
二、五大通知的具体实现
- 环绕通知 around
- 5个中最强大的通知,唯一一个能在通知中控制目标方法是否执行的通知
- 5个中最强大的通知,唯一一个能在通知中控制目标方法是否执行的通知
- 前置通知
- 配置文件配置
- 后置通知
- 配置文件配置
- 异常通知
- 异常通知可以控制出现异常后事务回滚 , 和记录日志
- 配置文件配置
- 注意: JoinPoint参数必须放在第一位
- 最终通知
- 不管是么情况都会执行
三、AOP注解
- 在配置文件中把aop注解开关打开
- 通过注解配置切面类
- 通过注解配置通知方法
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
- 前置通知
四、通过注解生成切入点表达式的引用
- 创建一个空方法
- 使用@Pointcut注解生成一个切入点的引用
- 使用
- 使用@Pointcut注解生成一个切入点的引用
五、环绕嵌套问题
六、自定义注解
- 声明一个注解
- 使用
- 判断是否有注解
七、各种示例
异常
代码结构如图所示 PersonServlet PersonService PersonDao 用异常通知捕获servlet的所有的方法抛出的异常: 目标对象所在的类 cn.tedu.big1601.servlet.PersonServlet 抛出异常所在的方法 save() 抛出异常的名称 XxxException 异常信息 message 意义: 异常处理类和业务逻辑类完全松耦合。 时刻捕获生产生产环境中所有的错误,实时监控该系统,异常收集。 @Component @Aspect public class ExceptionAspect { @AfterThrowing(value = "execution(* com.tj..*(..))" ,throwing = "throwable" ) public void after(JoinPoint jp , Throwable throwable){ System.out.println("异常发生在:"+jp.getTarget().getClass()); System.out.println(jp.getSignature().getName()+"()发生了异常!"); System.out.println("发生异常类型:"+throwable.getClass()); System.out.println("异常信息:"+throwable.getMessage()); } }
统计方法执行时间
计算servlet的各个类的各个方法的执行时间 1.类的名称 2.方法的名称 3.执行的时间 控制台输出 意义:用来监控程序的性能问题 @Component @Aspect public class RuntimeAspect { @Around("execution(* com.tj..*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable{ Long begin = System.currentTimeMillis(); Object result = pjp.proceed(); Long end = System.currentTimeMillis(); System.out.println(pjp.getTarget().getClass()+"类"+pjp.getSignature().getName()+"方法执行了"+ (end - begin)+"毫秒!"); return result; } }
事务控制
当方法上有事务的注解,该方法就有事务。 写一个切面来完成 事务控制类 public class TxManage { /** * 开启事务 * */ public static void stattx(){ System.out.println("开启了事务"); } /** * 提交事务 * */ public static void commitTx(){ System.out.println("提交事务"); } /** * 回滚事务 * */ public static void rollback(){ System.out.println("事务回滚"); } } 自定义事务注解 @Target(value = { ElementType.METHOD }) @Retention(value = RetentionPolicy.RUNTIME) public @interface TxAnnotation { String value() default ""; } 给需要事务控制的地方添加注解 @Component public class PersonServiceImpl implements PersonService{ @Autowired private PersonDao dao; @TxAnnotation @Cacheable("add") @Override public void savePerson(Person person) { dao.savePerson(person); } @TxAnnotation @Cacheable("get") @Override public Person getPerson(int id) { Person person = dao.getPerson(id); return person; } @TxAnnotation @Cacheable("del") @Override public void delPerson(int id) { dao.delPerson(id); } } 事务控制切面 @Component @Aspect public class TxAspect { @Around(value = "execution(* com.tj..*(..)) && @annotation(ann)") public Object around(ProceedingJoinPoint pjp ,TxAnnotation ann ) throws Throwable{ Object result = null; try{ TxManage.stattx(); result = pjp.proceed(); TxManage.commitTx(); }catch(Exception e){ TxManage.rollback(); } return result; } }
权限控制
说明:每个方法上添加能够执行该方法的注解@PrivilegeInfo 并且要指明 PrivilegeInfo(name=”add”) 那么将来这个方法只能执行还有add权限的方法 自定义权限控制注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface PrivilegeInfo { String value() ; } 给需要控制权限的地方添加权限控制注解 @Component public class PersonServlet { @Autowired private PersonService ps; /** * 保存用户信息 * */ @PrivilegeInfo("add") public void savePerson(Person person){ // int i = 1/0; ps.savePerson(person); } /** * 获取用户信息 * */ @PrivilegeInfo("get") public Person getPerson(int id){ Person person = ps.getPerson(id); return person ; } /** * 删除用户信息 * */ @PrivilegeInfo("del") public void delPerson(int id ){ ps.delPerson(id); } } 权限控制切面类 @Component @Aspect public class PrivilegInfoAspect { //当前用户的权限 List<String> list =Arrays.asList("add" , "get"); @Around("execution(* com.tj..*(..)) && @annotation(ann)") public Object around(ProceedingJoinPoint pjp , PrivilegeInfo ann) throws Throwable{ String value = ann.value(); Object result = null; if(list.contains(value)){ System.out.println("尊敬的飞秋会员你好!"); result = pjp.proceed(); }else{ System.out.println("你没有这个权限 ,滚"); } return result; } }
数据缓存
缓存需求:1.savePerson的时候需要往数据库里保存一份然后再往内存(Map)中保存一份 2.getPerson的时候先从Map中获取 如果有则返回则不用执行目标方法,如果内存中没有则执行目标方法从数据库取 3.如果第一次调用getPerson内存中没有的话 执行目标方法从数据库取 取出来后同时把获取到的Person对象保存到内存中,以便后续获取时直接从内存中取 自定义 缓存控制注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Cacheable { String value(); } 给需要缓存的地方添加注解 @Component public class PersonServiceImpl implements PersonService{ @Autowired private PersonDao dao; @TxAnnotation @Cacheable("add") @Override public void savePerson(Person person) { dao.savePerson(person); } @TxAnnotation @Cacheable("get") @Override public Person getPerson(int id) { Person person = dao.getPerson(id); return person; } @TxAnnotation @Cacheable("del") @Override public void delPerson(int id) { dao.delPerson(id); } } 缓存控制切面类 @Component @Aspect public class CacheableAspect { //缓存 Map<Integer , Person> map = new HashMap<Integer,Person>(); @Around("execution(* com.tj..*(..))&& @annotation(ann)") public Object befer(ProceedingJoinPoint jp , Cacheable ann) throws Throwable{ Object result = null; String v = ann.value(); if(v.equals("add")){ Person person = (Person) jp.getArgs()[0]; int id = person.getId(); if(map.containsKey(id)){ System.out.println("该用户已存在"); }else{ System.out.println("存入 缓存"); map.put(id, person); result = jp.proceed(); } }else if(v.equals("get")){ int id = (Integer) jp.getArgs()[0]; if(map.containsKey(id)){ System.out.println("从缓存中获取"); result = map.get(id); }else{ result = jp.proceed(); Person person = (Person) result; map.put(person.getId(), person); } } return result; } }