SpringAOP
1 Spring AOP相关概念
2.AOP的相关术语
Target(目标对象)
要被增强的对象,一般是业务逻辑类的对象。
Proxy(代理)
一个类被 AOP 织入增强后,就产生一个结果代理类。
Aspect(切面)
表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。
Joinpoint(连接点)
Pointcut(切入点)
Advice(通知/增强)
Weaving(织入).
具体作用
Spring Aop:Spring中Aop底层基于动态代理实现的,目标对象如果有接口,自动使用jdk动态代理,如果目标对象没有接口,自动使用cglib
问题
1.什么是目标对象?
2.怎么找到目标对象需要增强(修改)的方法?
3.如何对要增强(修改)的方法进行处理?
aop几个概念?(重点)
1.什么是目标对象?
1)Target(目标对象):要被增强(修改)的方法的对象。企业中,一般以业务类的对象作为目标对象。
2.怎么找到目标对象需要增强(修改)的方法?
Joinpoint(连接点) 和Pointcut(切入点)
Joinpoint(连接点):指代需要增强(修改)的目标对象中的特定方法。
Pointcut(切入点):切入点就是表达式:用来找到对应的要增强(修改)的目标对象中的特定方法。
* :0或者多个字符
.. :在方法的形参列表中,表示参数任意,在包名后面,表示当前包及其子包
Pointcut表达式格式:
execution(访问权限 返回值类型 包名.类名.方法名(参数列表)异常类型)
eg:
execution(* com.lr.spring.lr.*.*(..));
表示aop下面所有的类,所有的方法。
(第一个*表示返回任意类型第二个*表示任意类,第二个*表示任意方法)
execution(* com.lr.spring..*.*(..));
表示spring包以及子包中的任意的类中的任意方法
3.如何对要增强(修改)的方法进行处理?
Advice(通知/增强)和Aspect(切面类)
Advice(通知/增强):指定处理方法的位置(Pointcut),具体处理方法
Aspect(切面类):Advice所在的类就是切面类.
代码加入注解
1.准备工作
1.在pom.xml中引入外部文件
<dependency>
//引入的文件
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.15.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
//原本的文件
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
2.配置文件spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描文件 -->
<context:component-scan base-package="com.lr.spring.service com.lr.spring.aop"></context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
2.service包
1.UserService接口
package com.lr.spring.service;
public interface UserService {
void insertUser(String name);
void updateUser(String name);
void findUserById(int id);
void deleteUserBuId(int id);
}
2.UserServiceImpl实现类(目标对象)
package com.lr.spring.service;
//目标对象
@Controller
public class UserServiceImpl implements UserService {
@Override
public void insertUser(String name) {
System.out.println("插入操作");
}
@Override
public void updateUser(String name) {
System.out.println("修改操作");
}
@Override
public void findUserById(int id) {
System.out.println("查询操作");
}
@Override
public void deleteUserBuId(int id) {
System.out.println("删除操作");
}
}
3.Aop包
1.前置通知:Aspect代码
package com.lr.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//切面类(advice,pointcut)
@Component
@Aspect
public class MyAspect {
@Before("execution(* com.lr.spring.service.*.*(..))")
public void before(JoinPoint jp){//jp指代要处理的目标对象的方法
System.out.println("前置通知:在目标对象方法执行之前执行:");
System.out.println("拦截到要处理的方法的方法名字:");
String name = jp.getSignature().getName();//要处理的方法名字
System.out.println("方法名:"+name);
System.out.println("拦截要处理的方法的参数:");
Object[] args=jp.getArgs();
for (Object arg : args) {
System.out.println("参数:"+arg);
}
}
}
2.测试程序
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserServiceImpl) context.getBean("userServiceImpl");
userService.insertUser("龙仁");
System.out.println("...................");
userService.findUserById(100);
System.out.println("...................");
userService.updateUser("龙傲天");
System.out.println("...................");
userService.deleteUserBuId(100);
}
3.后置通知:修改原码
修改源代码,给查询方法一个返回值
package com.lr.spring.service;
public interface UserService {
void insertUser(String name);
void updateUser(String name);
String findUserById(int id);
void deleteUserBuId(int id);
}
package com.lr.spring.service;
import org.springframework.stereotype.Controller;
//目标对象
@Controller
public class UserServiceImpl implements UserService {
@Override
public void insertUser(String name) {
System.out.println("插入操作");
}
@Override
public void updateUser(String name) {
System.out.println("修改操作");
}
@Override
public String findUserById(int id) {
System.out.println("查询操作");
return "张三";
}
@Override
public void deleteUserBuId(int id) {
System.out.println("删除操作");
}
}
4.后置通知:Aspect代码
//后置通知:目标对象方法执行介绍之后,执行后置通知(可以获得当前方法的返回值)
@AfterReturning(value = "execution(* com.lr.spring.service.*.*(..))",returning = "result")
public void AfterReturning(Object result){
System.out.println("后置通知:获得目标对象方法的返回值");
System.out.println(result);
}
5.测试程序
@Test
public void test02() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserServiceImpl) context.getBean("userServiceImpl");
userService.findUserById(100);
}
6. 异常通知:Aspect代码
//异常通知:只有当前对象方法出现异常,反之没有异常,不执行该通知
@AfterThrowing(value = "execution(* com.lr.spring.service.*.*(..))",throwing ="ex")
public void exception(JoinPoint joinPoint,Throwable ex){
System.out.println(joinPoint.getSignature()+"方法抛出异常");
System.out.println(ex.getMessage());
}
7:修改实现类代码:加入空指针异常
@Override
public String findUserById(int id) {
System.out.println("查询操作");
String str =null;
str.indexOf(101);
return "张三";
}
8. 测试程序
@Test
public void test02() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserServiceImpl) context.getBean("userServiceImpl");
userService.findUserById(101);
}
9.环绕通知 Aspect代码
//环绕通知:在目标对象的方法执行前后执行(调用目标对象的方法执行)
@Around("execution(* com.lr.spring.service.*.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕通知---------目标对象方法执行之前");
//执行目标对象的方法
Object result = proceedingJoinPoint.proceed();
System.out.println("环绕通知---------目标对象方法执行指挥");
return result;
}
10.最终通知
//最终通知
@After(value = "execution(* com.lr.spring.service.*.*(..))")
public void myAfter(){
System.out.println("最终被执行");
}
执行顺序环绕——》前置——》后置——》最终——》环绕
注解方式实现AOP
1.注解方法
package com.lr.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.w3c.dom.ls.LSOutput;
//切面类(advice,pointcut)
@Component
@Aspect
public class MyAspect {
// 标记某一个注解
@Pointcut("execution(* com.lr.spring.service.*.*(..))")
private void pointcut(){ }
//前置通知:在目标对象方法执行之前执行,拦截到要处理的方法的方法名字
@Before("pointcut()")
public void before(JoinPoint jp){//jp指代要处理的目标对象的方法
System.out.println("前置通知:在目标对象方法执行之前执行:");
System.out.println("拦截到要处理的方法的方法名字:");
String name = jp.getSignature().getName();//要处理的方法名字
System.out.println("方法名:"+name);
System.out.println("拦截要处理的方法的参数:");
Object[] args=jp.getArgs();
for (Object arg : args) {
System.out.println("参数:"+arg);
}
}
//后置通知:目标对象方法执行介绍之后,执行后置通知(可以获得当前方法的返回值)
@AfterReturning(value = "pointcut()",returning = "result")
public void AfterReturning(Object result){
System.out.println("后置通知:获得目标对象方法的返回值");
System.out.println(result);
}
//异常通知:只有当前对象方法出现异常,反之没有异常,不执行该通知
@AfterThrowing(value = "pointcut()",throwing ="ex")
public void exception(JoinPoint joinPoint,Throwable ex){
System.out.println(joinPoint.getSignature()+"方法抛出异常");
System.out.println(ex.getMessage());
}
//环绕通知:在目标对象的方法执行前后执行(调用目标对象的方法执行)
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕通知---------目标对象方法执行之前");
//执行目标对象的方法
Object result = proceedingJoinPoint.proceed();
System.out.println("环绕通知---------目标对象方法执行指挥");
return result;
}
//最终通知
@After(value = "execution(* com.lr.spring.service.*.*(..))")
public void myAfter(){
System.out.println("最终被执行");
}
}
2.XML方式实现AOP
package com.lr.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//切面类(advice,pointcut)
@Component
@Aspect
public class MyAspect2 {//只要通知了
public void before(JoinPoint jp){//jp指代要处理的目标对象的方法
System.out.println("前置通知:在目标对象方法执行之前执行:");
}
public void AfterReturning(Object result){
System.out.println("后置通知:获得目标对象方法的返回值");
System.out.println(result);
}
public void exception(JoinPoint joinPoint,Throwable ex){
System.out.println(joinPoint.getSignature()+"方法抛出异常");
System.out.println(ex.getMessage());
}
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕通知---------目标对象方法执行之前");
//执行目标对象的方法
Object result = proceedingJoinPoint.proceed();
System.out.println("环绕通知---------目标对象方法执行指挥");
return result;
}
public void myAfter(){
System.out.println("最终被执行");
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描文件 -->
<context:component-scan base-package="com.lr.spring.service com.lr.spring.aop"></context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<aop:config>
<!-- 切入点 -->
<aop:pointcut id="t1" expression="execution(* com.lr.spring.service.*.insert*(..))"/>
<aop:pointcut id="t2" expression="execution(* com.lr.spring.service.*.update*(..))"/>
<aop:pointcut id="t3" expression="execution(* com.lr.spring.service.*.delete*(..))"/>
<aop:pointcut id="t4" expression="execution(* com.lr.spring.service.*.find*(..))"/>
<!-- 切入点跟通知结合 -->
<aop:aspect ref="myAspect2">
<aop:before method="before" pointcut-ref="t1"></aop:before>
<aop:before method="before" pointcut-ref="t4"></aop:before>
<aop:after-returning method="AfterReturning" returning="result" pointcut-ref="t4"></aop:after-returning>
</aop:aspect>
</aop:config>
</beans>