实习笔记 —— Spring基础
实习笔记 —— IOC反转控制(xml配置文件 + 注解)
实习笔记 —— AOP开发I
文章目录
- 系列文章目录
- 一、AOP中Advice的类型的XML配置文件形式
- 二、AOP中Advice的类型的基本注解形式
- 三、AOP中Advice的类型的切入点注解形式
- 四、总结
一、AOP中Advice的类型的XML配置文件形式
先回顾昨天学的AOP中的相关术语,体会Advice的含义:
实习时介绍了AOP中Advice的五种类型,我们将这五种类型综合起来学习。
1.before advice前置通知: 在执行切点前的通知,可以判断是否可以执行该切点。
2.after-returning advice后置通知: 在运行完切点完未返回之前,进行通知。
3.around advice环绕通知: 包裹一个方法的执行,这是一个功能强大的通知方式。可以在方法执行前或者执行后around advice,也可以用来判断是否往下执行当前的切点,或者返回切点的值。或者抛出一个异常。
4.after throwing advice异常通知: 在运行完切点时抛出异常进行的通知。
5.After advice最终通知:执行完该切点后,进行的通知。
以具体项目为例:(创建项目第一步先导包)
①声明接口:
②声明接口的实现类:
③创建切面类,将所有新添加的需求对应的方法写在这里:
package com.high.demo1;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//最开始导错包了import org.aopalliance.intercept.Joinpoint;
// 切面类,存放用户要增加的方法
public class MyAspectXML {
// Advice:通知,增强;甲方要求新增加的方法或需求(如显示用户身份需求)
// public void checkPri() {
// System.out.println("进行身份验证........");
// }
// ctrl + alt + down
// 1.前置通知
public void checkPri(JoinPoint jointJoinpoint) {
System.out.println("save方法的前置通知:进行身份验证........" + jointJoinpoint);
}
// 2.后置通知
public void writeLog(Object result) {
System.out.println("delete方法的后置通知:Be recorded....." + result);
}
// 3.环绕通知:性能监控
// ProceedingJoinPoint是指被增加的接入点find()
public Object aroundOne(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前.....");
// 调用了此方法对应的切入点 find()方法,基本都要写,用于体现“环绕”
Object proceed = joinPoint.proceed();
System.out.println("环绕通知后前.....");
return proceed;
}
// 4.异常抛出通知
// 参数: Throwable
public void afterThrow(Throwable ex) {
System.out.println("Error appeared." + ex.getMessage());
}
// 5.最终通知 —— 相当于异常中的 finally代码块
public void afterLast() {
System.out.println("This is the final Advice.");
}
}
④编写XML配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean name="productDao" class="com.high.demo1.ProductDaoImpl">bean>
<bean name="myAspect" class="com.high.demo1.MyAspectXML">bean>
<aop:config>
<aop:pointcut id="pointcut1" expression="execution(* com.high.demo1.ProductDaoImpl.save(..))"/>
<aop:pointcut id="pointcut2" expression="execution(* com.high.demo1.ProductDaoImpl.delete(..))"/>
<aop:pointcut id="pointcut3" expression="execution(* com.high.demo1.ProductDaoImpl.find(..))"/>
<aop:pointcut id="pointcut4" expression="execution(* com.high.demo1.ProductDaoImpl.update(..))"/>
<aop:pointcut id="pointcut5" expression="execution(* com.high.demo1.ProductDaoImpl.find(..))"/>
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
<aop:around method="aroundOne" pointcut-ref="pointcut3"/>
<aop:after-throwing method="afterThrow" pointcut-ref="pointcut4" throwing="ex"/>
<aop:after method="afterLast" pointcut-ref="pointcut5"/>
aop:aspect>
aop:config>
beans>
⑤编写测试类:
package com.high.demo1;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
// AOP入门
// 书写类注解,类层级,所以写在类外面
// 整合JUnit4
@RunWith(SpringJUnit4ClassRunner.class)
// 加载XML配置文件(严格按照格式书写,不要添加任何空格等字符)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo {
// 1.未使用注解整合JUnit
// @Test
// public void demo() {
// ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// ProductDaoImpl product = (ProductDaoImpl)applicationContext.getBean("productDao");
//
// product.save();
// product.update();
// product.delete();
// product.find();
// }
// 2.整合后
// 想用哪个类就注入哪个类,交给spring管理
// 对私有属性的引入
@Resource(name = "productDao")
private ProductDao productDao;
@Test
public void demo2() {
productDao.save();
productDao.update();
productDao.delete();
productDao.find();
}
}
在正常无Advice的情况下,测试类的输出结果为:
在为各个方法添加新需求后:体会五种类型的Advice的作用
核心:
在配置文件中启用注解功能:
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
①创建类(应该是接口及其实现类,偷懒了):
②编写配置文件:
由于使用注解形式弱化了配置文件的作用,所以配置文件精简了许多
③最核心的部分:编写切面类
package com.high.demo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
// 切面类
// AOP注解
@Aspect
public class MyAspectDemo {
// 1.前置增强
@Before(value = "execution(* com.high.demo.OrderDao.save(..))")
public void checkPri(JoinPoint joinPoint) {
System.out.println("save方法的前值增强:权限检测..." + joinPoint);
}
// 2.后置增强
@AfterReturning(value = "execution(* com.high.demo.OrderDao.find(..))", returning = "result")
public void afterReturn(String result) {
System.out.println("find方法的后置增强..." + result);
}
// 3.环绕增强
@Around(value = "execution(* com.high.demo.OrderDao.delete(..))")
public Object aroundOne(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("delete方法的环绕前通知");
// 获取接入点
Object target = joinPoint.proceed();
System.out.println("delete方法的环绕后通知");
return target;
}
// 4.异常通知
@AfterThrowing(value = "execution(* com.high.demo.OrderDao.update(..))", throwing="ex")
public void afterThrowing(Throwable ex) {
System.out.println("update方法的异常通知:Error appeared: " + ex.getMessage());
}
// 5.最终通知
@After(value = "execution(* com.high.demo.OrderDao.update(..))")
public void afterAdvice() {
System.out.println("This is the After Advice of method update.");
}
}
④测试类:
测试类完全使用注解形式了,比之前的先获取配置文件再使用对象及其方法的形式简便了一些
测试结果:
当解除 update 方法的注释 i = 1 / 0 后,代码中出现 0 作分母的错误,此时异常通知就开始起作用了(update 方法未能成功执行,而针对update 方法的最终通知无论如何都会执行)
这是最简便的书写方式了,相对于标题二中的基本注解形式,进一步简化代码,声明切入点的注解,使得不必再对方法的每个新增需求创建切入点,而是对每个原有方法声明切入点,大大减少了需要声明的切入点的个数。
在切面类中写@Aspect注解,并在类中用注解进行切入点的声明(这是注解方式相较于XML配置文件方式简练的点,不再需要在XML配置文件中单独声明切入点并编写织入过程了)
其实实体类、测试类、配置文件相对于标题二下的内容都没有修改,只是在切面类中增加了切入点的注解:
①加入切入点注解的切面类写法:
package com.high.demo2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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 MyAspect {
// AOP切入点的注解,提供更大的灵活性,方便解耦
@Pointcut(value = "execution(* com.high.demo2.UserDao.save(..))")
private void pointcut1() {}; //此处是一个方法,不是变量
@Pointcut(value = "execution(* com.high.demo2.UserDao.find(..))")
private void pointcut2() {};
@Pointcut(value = "execution(* com.high.demo2.UserDao.delete(..))")
private void pointcut3() {};
@Pointcut(value = "execution(* com.high.demo2.UserDao.update(..))")
private void pointcut4() {};
// 1.前置通知
@Before(value = "MyAspect.pointcut1()")
public void checkPri(JoinPoint joinPoint) {
System.out.println("切入点注解测试:save方法的前置通知; " + joinPoint);
}
// 2.后置通知
@AfterReturning(value = "MyAspect.pointcut2()", returning = "result")
public void afterOne(String result) {
System.out.println("切入点注解测试:find方法的后置通知; " + result);
}
// 3.环绕通知
@Around(value = "MyAspect.pointcut3()")
public Object aroundOne(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("切入点注解测试:delete方法的环绕通知(前);");
// 获取接入点
Object target = joinPoint.proceed();
System.out.println("切入点注解测试:delete方法的环绕通知(后);");
return target;
}
// 4.异常通知
@AfterThrowing(value = "MyAspect.pointcut4()", throwing = "ex")
public void afterThrowing(Throwable ex) {
System.out.println("切入点注解测试:update方法的异常通知; " + ex.getMessage());
}
// 5.最终通知
@After(value = "MyAspect.pointcut4()")
public void afterAdvice() {
System.out.println("切入点注解测试:update方法的最终通知;");
}
}
②测试结果:
(尽管实体类等没有变化,但在切面类书写 Advice 时在熟练之后对各输出语句内容进行了自由发挥,整体代码自认为更容易理解了。)
1.后置通知在return语句前输出。
2.异常通知是报错时才会出现的通知,after-throwing 中的after 是指在执行后才能发现异常。
(同时发现:在项目spring03中测试时,按照测试类调用方法的顺序,update执行错误后剩余的delete和find不执行了。)
3.关于最终通知,若异常测试对应的方法(切入点)出错,最终通知会显示在异常测试报错之前,再报错。
4.若所有的新增需求甲方都不要了,我们要改哪些代码?
——在配置文件中将切面类相关的代码删除,保留
<bean name="productDao" class="com.high.demo1.ProductDaoImpl">bean>
仍可在测试类中以XML配置文件形式使用spring框架对对象进行 *** 作。
5.声明切入点时的expression属性写法(spring的切入点表达式写法):
6.注解有两种:类层面的注解 + 方法/属性层面(类内部)的注解。
7.spring注重团队开发与规则,便于大规模使用。类比自家饭菜与连锁店,自家饭菜一家一个味,但不利于形成规模。
8.代码能力相对容易掌握,但排错能力需要经验与锻炼。日常的节奏是写一段代码,报错,排错,完善,再往下写。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)