传统创建对象:
基于xml配置IOC
开发者需要在xml中 把对象进行配置 Spring框架会读取这个配置文件,根据配置文件的内容来创建对象。
根据xml解析再进行反射来创建对象。
1.配置xml 在reasources文件下创建xml文件
2.创建一个应用上下文(ApplicationContext) 也可以理解为整个上下文容器
基于注解方式配置IOC
1.创建配置类
用一个java来代替xml文件,把xml中配置的内容放在java的配置类里
3.扫包+注解
使用compontent注解,告诉spring这个类要注入到ioc
还可以使用value这个注解添加值
AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
Aop名词解释
切面(Aspect):切面编程的完整定义模块,比如日志切面,包含了何时、对谁、如何等等所有的内容。
连接点(Join point):能够植入切面的部分,比如方法前后,抛出异常时都可以是连接点,spring只支持方法连接点
通知(Advice):要对切面添加的功能代码,比如 安全,事物,日志等功能代码。类型:before/after/around/after-returning/after-throwing
切入点(Pointcut):针对哪些连接点植入通知,也就是指定具体的拦截地点。可以通过spEL指定连接点
引入(Introduction):对目标类添加新方法及属性
目标对象(Target object):被切面切的对象。真正的业务逻辑,完全不知道存在切面
代理(proxy):实现AOP的一种原理
织入(Weaving):把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。
代码实现
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.标注切面类
@Aspect 标识为切面类,被容器识别。
@Aspect
@Component
@Slf4j
public class VipCheckAspect {
........
}
3.编写切入点(Pointcut)和通知
针对哪些连接点植入通知,也就是指定具体的拦截地点。可以通过spEL指定连接点
@Pointcut("execution(public * qnm.lr.modules.classifyquestion.controller.ClassifyQuestionController.queryQuestion(..))")
public void controllerRequestLog() {
}
@Before("controllerRequestLog()") // 对应上面的controllerRequestLog()方法
public void doQuestionBefore(JoinPoint joinPoint) {
.........
}
通知类型
@Before 前置通知 (方法前) : 在目标方法运行前增强, 前置通知。应用场景: 日志记录、权限控制
@AfterReturning 后置通知 (方法返回后) : 在目标方法返回值获取后进行增强,后置通知。 例如: 网上营业厅查询余额后,自动下发短信。
@Around 环绕通知 (方法前后) : 在目标方法运行前后进行增强,环绕通知。应用场景:日志、缓存、权限、性能监控、 事务管理。
@AfterThrowing 抛出通知 (异常发生后) : 在目标方法抛出异常后进行增强,抛出通知。应用场景: 处理异常 ,记录日志
@After 最终通知 (一定在方法后执行): 不管是否发生异常,该通知都执行,类似 finally。应用场景 : 关闭和释放一些资源
@AfterReturning(value = "execution(* com.wuwl.service.impl.StudentServiceImpl.(..) )" , returning = "returning")
public void doReturn(JoinPoint joinPoint,Object returning){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"方法返回,返回值为:"+returning);
}
@AfterThrowing(value = "execution( com.wuwl.service.impl.StudentServiceImpl.*(..) )",throwing = "ex")
public void doThrow(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"方法异常,异常信息为:"+ex.getMessage());
}
@PointCut注解
在Spring 2.0中,Pointcut的定义包括两个部分:Pointcut表示式(expression)和Pointcut签名(signature)
//Pointcut表示式
@Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
//Point签名
private void log(){}
然后要使用所定义的Pointcut时,可以指定Pointcut签名如下:
@Before("og()")
使用方式等同于以下方式,直接定义execution表达式使用
@Before("execution(* com.savage.aop.MessageSender.*(..))")
Pointcut表示式(expression)用法
格式: execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
括号中各个pattern分别表示:
修饰符匹配(modifier-pattern?)
返回值匹配(ret-type-pattern)可以为表示任何返回值,全路径的类名等
类路径匹配(declaring-type-pattern?)
方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set开头的所有方法
参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“ , ”隔开,各个参数也可以用 “ * ”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(…)表示零个或多个任意参数
异常类型匹配(throws-pattern?)
其中后面跟着“?”的是可选项
例如:
1)execution(* *(..)) //表示匹配所有方法
2)execution(public * com. savage.service.UserService.*(..)) // 表示匹配com.savage.server.UserService中所有的公有方法
3)execution(public * qnm.lr.modules.*.controller..*.*(..))") // 表示匹配 qnm.lr.modules下所有的controller中的方法
4)execution(* com.savage.server..*.*(..)) // 表示匹配com.savage.server包及其子包下的所有方法
5)execution(public * qnm.lr.modules.classifyquestion.controller.ClassifyQuestionController.queryQuestion(..)) //表示匹配 qnm.lr.modules.classifyquestion.controller.ClassifyQuestionController中的queryQuestion()方法
6)Pointcut定义时,还可以使用&&、||、! 这三个运算
@Pointcut("execution(public * qnm.lr.modules.classifyquestion.controller.ClassifyQuestionController.queryQuestion(..)) " +
"|| execution(public * qnm.lr.modules.video.controller.VideoShortController.queryShortVideoList(..))" +
" || execution(public * qnm.lr.modules.customquestion.controller.CustomQuestionController.queryCategory(..))")
在切面类中获取切入点相关方法的参数、方法名、返回值、异常等信息
joinPoint.getArgs()方法: 返回切入方法的参数信息,返回值为一个数组,遍历即可获取;
joinPoint.getSignature()方法: 返回值为方法签名,签名接口中就包括方法名之类的信息。
@Aspect
@Component
@Slf4j
public class StudentServiceLogger {
@Before("execution(* com.wuwl.service.impl.StudentServiceImpl.*(..) )")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); // 接收到请求,记录请求内容
if (attributes == null) {
log.info("ServletRequestAttributes 为空, 跳过请求参数打印");
return;
}
HttpServletRequest request = attributes.getRequest();
String method = request.getMethod(); // 方法名
String uri = request.getRequestURI(); // 请求的URL
String methodName = joinPoint.getSignature().getName(); // 方法名
System.out.println(methodName+"方法执行前...");
Object[] args = joinPoint.getArgs(); // 参数
System.out.println("参数为:"+ Arrays.asList(args));
// ClassifyDTO arg=(ClassifyDTO)args[0]; // 根据类型进行转换
}
}